[Syntax] Refactor the expression parser to use ParseResult

This commit is contained in:
Araozu 2023-11-04 06:46:24 -05:00
parent e02a95f065
commit 799cda1bf0
6 changed files with 67 additions and 37 deletions

View File

@ -17,6 +17,7 @@ impl Transpilable for Expression {
}
Expression::Boolean(value) => String::from(if *value { "true" } else { "false" }),
Expression::Identifier(value) => format!("{}", *value),
Expression::FunctionCall(_) => todo!("FunctionCall codegen is not implemented yet"),
}
}
}

View File

@ -1,3 +1,5 @@
use self::functions::FunctionCall;
pub mod functions;
pub mod statement;
pub mod var_binding;
@ -32,4 +34,5 @@ pub enum Expression {
String(Box<String>),
Boolean(bool),
Identifier(Box<String>),
FunctionCall(FunctionCall),
}

View File

@ -1,5 +1,5 @@
use super::ast::var_binding::{Binding, ValBinding, VarBinding};
use super::utils::{try_operator, parse_token_type};
use super::utils::{parse_token_type, try_operator};
use super::{expression, ParseResult};
use crate::error_handling::SyntaxError;
use crate::lexic::token::{Token, TokenType};
@ -30,7 +30,8 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
/*
* identifier
*/
let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier) {
let (identifier, next_pos) = match parse_token_type(tokens, current_pos, TokenType::Identifier)
{
ParseResult::Ok(t, n) => (t, n),
ParseResult::Mismatch(token) => {
// The parser found a token, but it's not an identifier
@ -38,7 +39,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
error_start: token.position,
error_end: token.get_end_position(),
reason: "??".into(),
})
});
}
ParseResult::Err(error) => {
return ParseResult::Err(error);
@ -60,7 +61,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
/*
* Equal (=) operator
*/
let equal_operator: &Token = match try_operator(tokens, current_pos, String::from("=")) {
let equal_operator = match try_operator(tokens, current_pos, String::from("=")) {
Result3::Ok(t) => t,
Result3::Err(t) => {
// The parser found a token, but it's not the `=` operator
@ -80,15 +81,16 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding,
}
};
let expression = expression::try_parse(tokens, current_pos + 1);
if expression.is_none() {
return ParseResult::Err(SyntaxError {
reason: String::from("Expected an expression after the equal `=` operator"),
error_start: equal_operator.position,
error_end: equal_operator.get_end_position(),
});
}
let expression = expression.unwrap();
let (expression, _next) = match expression::try_parse(tokens, current_pos + 1) {
ParseResult::Ok(exp, next) => (exp, next),
_ => {
return ParseResult::Err(SyntaxError {
reason: String::from("Expected an expression after the equal `=` operator"),
error_start: equal_operator.position,
error_end: equal_operator.get_end_position(),
});
}
};
let binding = if is_val {
Binding::Val(ValBinding {

View File

@ -1,4 +1,4 @@
use super::ast::Expression;
use super::{ast::Expression, ParseResult};
use crate::lexic::token::{Token, TokenType};
/// An expression can be:
@ -7,16 +7,36 @@ use crate::lexic::token::{Token, TokenType};
/// - A string
/// - A boolean
/// - An identifier
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> Option<Expression> {
tokens.get(pos).and_then(|token| match token.token_type {
TokenType::Number => Some(Expression::Number(Box::new(token.value.clone()))),
TokenType::String => Some(Expression::String(Box::new(token.value.clone()))),
TokenType::Identifier if token.value == "true" || token.value == "false" => {
Some(Expression::Boolean(token.value == "true"))
}
TokenType::Identifier => Some(Expression::Identifier(Box::new(token.value.clone()))),
_ => None,
})
/// - A function call
pub fn try_parse(tokens: &Vec<Token>, pos: usize) -> ParseResult<Expression, ()> {
/*
match function_call::try_parse(tokens, pos) {
super::ParseResult::Ok(_, _) => todo!(),
super::ParseResult::Err(_) => todo!(),
super::ParseResult::Mismatch(_) => todo!(),
super::ParseResult::Unmatched => todo!(),
}
*/
match tokens.get(pos) {
Some(token) => match token.token_type {
TokenType::Number => {
ParseResult::Ok(Expression::Number(Box::new(token.value.clone())), pos + 1)
}
TokenType::String => {
ParseResult::Ok(Expression::String(Box::new(token.value.clone())), pos + 1)
}
TokenType::Identifier if token.value == "true" || token.value == "false" => {
ParseResult::Ok(Expression::Boolean(token.value == "true"), pos + 1)
}
TokenType::Identifier => ParseResult::Ok(
Expression::Identifier(Box::new(token.value.clone())),
pos + 1,
),
_ => ParseResult::Unmatched,
},
None => ParseResult::Unmatched,
}
}
#[cfg(test)]
@ -27,10 +47,10 @@ mod tests {
#[test]
fn should_parse_a_number() {
let tokens = get_tokens(&String::from("40")).unwrap();
let expression = try_parse(&tokens, 0).unwrap();
let expression = try_parse(&tokens, 0);
match expression {
Expression::Number(value) => assert_eq!("40", format!("{}", value)),
ParseResult::Ok(Expression::Number(value), _) => assert_eq!("40", format!("{}", value)),
_ => panic!(),
}
}
@ -38,10 +58,12 @@ mod tests {
#[test]
fn should_parse_a_string() {
let tokens = get_tokens(&String::from("\"Hello\"")).unwrap();
let expression = try_parse(&tokens, 0).unwrap();
let expression = try_parse(&tokens, 0);
match expression {
Expression::String(value) => assert_eq!("\"Hello\"", format!("{}", value)),
ParseResult::Ok(Expression::String(value), _) => {
assert_eq!("\"Hello\"", format!("{}", value))
}
_ => panic!(),
}
}
@ -49,10 +71,10 @@ mod tests {
#[test]
fn should_parse_a_boolean() {
let tokens = get_tokens(&String::from("true")).unwrap();
let expression = try_parse(&tokens, 0).unwrap();
let expression = try_parse(&tokens, 0);
match expression {
Expression::Boolean(value) => assert!(value),
ParseResult::Ok(Expression::Boolean(value), _) => assert!(value),
_ => panic!(),
}
}
@ -60,10 +82,12 @@ mod tests {
#[test]
fn should_parse_an_identifier() {
let tokens = get_tokens(&String::from("someIdentifier")).unwrap();
let expression = try_parse(&tokens, 0).unwrap();
let expression = try_parse(&tokens, 0);
match expression {
Expression::Identifier(value) => assert_eq!("someIdentifier", format!("{}", value)),
ParseResult::Ok(Expression::Identifier(value), _) => {
assert_eq!("someIdentifier", format!("{}", value))
}
_ => panic!(),
}
}

View File

@ -17,16 +17,18 @@ use self::ast::TopLevelDeclaration;
#[derive(Debug)]
pub enum ParseResult<A, B> {
/// The parsing was a success
/// The parsing was a success. The first element is the parsed construct,
/// the second element is the position of the next token to parse
Ok(A, usize),
/// The parsing failed past a point of no return.
///
/// For example, when parsing a function declaration
/// the `fun` token is found, but then no identifier
Err(SyntaxError),
/// A construct different from the one expected was found
/// Some special value was expected, but something else was found.
/// The inside element is the something else found.
Mismatch(B),
/// No construct was found
/// This parsing didn't succeed, but it's not a fatal error.
Unmatched,
}

View File

@ -28,7 +28,6 @@ pub fn try_operator(tokens: &Vec<Token>, pos: usize, operator: String) -> Result
}
}
/// Expects the token at `pos` to be of type `token_type`
pub fn parse_token_type(
tokens: &Vec<Token>,
@ -58,4 +57,3 @@ pub fn parse_token_type(
None => ParseResult::Unmatched,
}
}