[syntax] refactor bindings

master
Araozu 2023-09-24 18:51:08 -05:00
parent 1fbc353ebf
commit 2e9776de01
2 changed files with 40 additions and 52 deletions

View File

@ -1,6 +1,6 @@
use super::ast::{Binding, ValBinding, VarBinding}; use super::ast::{Binding, ValBinding, VarBinding};
use super::utils::{try_operator, try_token_type}; use super::utils::{try_operator, try_token_type};
use super::{expression, SyntaxResult}; use super::{expression, ParseResult};
use crate::error_handling::SyntaxError; use crate::error_handling::SyntaxError;
use crate::lexic::token::{Token, TokenType}; use crate::lexic::token::{Token, TokenType};
use crate::utils::Result3; use crate::utils::Result3;
@ -9,7 +9,7 @@ use crate::utils::Result3;
// - Success: binding parsed successfully // - Success: binding parsed successfully
// - NotFound: the first token (var | val) was not found, so the parser should try other options // - NotFound: the first token (var | val) was not found, so the parser should try other options
// - Error: token (var | val) was found, but then other expected tokens were not found // - Error: token (var | val) was found, but then other expected tokens were not found
pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult> { pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Binding, ()> {
let mut current_pos = pos; let mut current_pos = pos;
// Optional datatype annotation // Optional datatype annotation
let datatype_annotation = { let datatype_annotation = {
@ -38,7 +38,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult>
Result3::Ok(var_token) => (false, var_token), Result3::Ok(var_token) => (false, var_token),
// Neither VAL nor VAR were matched, the parser should try // Neither VAL nor VAR were matched, the parser should try
// other constructs // other constructs
_ => return None, _ => return ParseResult::Unmatched,
} }
} }
} }
@ -51,25 +51,25 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult>
Result3::Ok(t) => t, Result3::Ok(t) => t,
Result3::Err(t) => { Result3::Err(t) => {
// The parser found a token, but it's not an identifier // The parser found a token, but it's not an identifier
return Some(SyntaxResult::Err(SyntaxError { return ParseResult::Err(SyntaxError {
reason: format!( reason: format!(
"There should be an identifier after a `{}` token", "There should be an identifier after a `{}` token",
if is_val { "val" } else { "var" } if is_val { "val" } else { "var" }
), ),
error_start: t.position, error_start: t.position,
error_end: t.get_end_position(), error_end: t.get_end_position(),
})); });
} }
Result3::None => { Result3::None => {
// The parser didn't find an Identifier after VAL/VAR // The parser didn't find an Identifier after VAL/VAR
return Some(SyntaxResult::Err(SyntaxError { return ParseResult::Err(SyntaxError {
reason: format!( reason: format!(
"There should be an identifier after a `{}` token", "There should be an identifier after a `{}` token",
if is_val { "val" } else { "var" } if is_val { "val" } else { "var" }
), ),
error_start: binding_token.position, error_start: binding_token.position,
error_end: binding_token.get_end_position(), error_end: binding_token.get_end_position(),
})); });
} }
}; };
@ -80,29 +80,29 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult>
Result3::Ok(t) => t, Result3::Ok(t) => t,
Result3::Err(t) => { Result3::Err(t) => {
// The parser found a token, but it's not the `=` operator // The parser found a token, but it's not the `=` operator
return Some(SyntaxResult::Err(SyntaxError { return ParseResult::Err(SyntaxError {
reason: format!("There should be an equal sign `=` after the identifier"), reason: format!("There should be an equal sign `=` after the identifier"),
error_start: t.position, error_start: t.position,
error_end: t.get_end_position(), error_end: t.get_end_position(),
})); });
} }
Result3::None => { Result3::None => {
// The parser didn't find the `=` operator after the identifier // The parser didn't find the `=` operator after the identifier
return Some(SyntaxResult::Err(SyntaxError { return ParseResult::Err(SyntaxError {
reason: format!("There should be an equal sign `=` after the identifier",), reason: format!("There should be an equal sign `=` after the identifier",),
error_start: identifier.position, error_start: identifier.position,
error_end: identifier.get_end_position(), error_end: identifier.get_end_position(),
})); });
} }
}; };
let expression = expression::try_parse(tokens, current_pos + 3); let expression = expression::try_parse(tokens, current_pos + 3);
if expression.is_none() { if expression.is_none() {
return Some(SyntaxResult::Err(SyntaxError { return ParseResult::Err(SyntaxError {
reason: String::from("Expected an expression after the equal `=` operator"), reason: String::from("Expected an expression after the equal `=` operator"),
error_start: equal_operator.position, error_start: equal_operator.position,
error_end: equal_operator.get_end_position(), error_end: equal_operator.get_end_position(),
})); });
} }
let expression = expression.unwrap(); let expression = expression.unwrap();
@ -120,10 +120,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> Option<SyntaxResult>
}) })
}; };
Some(SyntaxResult::Ok( ParseResult::Ok(binding, current_pos + 4)
super::ast::TopLevelDeclaration::Binding(binding),
current_pos + 4,
))
} }
#[cfg(test)] #[cfg(test)]
@ -134,15 +131,12 @@ mod tests {
#[test] #[test]
fn should_parse_val_binding() { fn should_parse_val_binding() {
let tokens = get_tokens(&String::from("val identifier = 20")).unwrap(); let tokens = get_tokens(&String::from("val identifier = 20")).unwrap();
let binding = try_parse(&tokens, 0).unwrap(); let ParseResult::Ok(Binding::Val(binding), _) = try_parse(&tokens, 0) else {
panic!()
};
match binding {
SyntaxResult::Ok(TopLevelDeclaration::Binding(Binding::Val(binding)), _) => {
assert_eq!("identifier", format!("{}", binding.identifier)); assert_eq!("identifier", format!("{}", binding.identifier));
} }
_ => panic!(),
}
}
#[test] #[test]
fn should_parse_val() { fn should_parse_val() {
@ -172,37 +166,31 @@ mod tests {
#[test] #[test]
fn should_parse_binding_with_datatype() { fn should_parse_binding_with_datatype() {
let tokens = get_tokens(&String::from("Num val identifier = 20")).unwrap(); let tokens = get_tokens(&String::from("Num val identifier = 20")).unwrap();
let binding = try_parse(&tokens, 0).unwrap(); let ParseResult::Ok(Binding::Val(binding), _) = try_parse(&tokens, 0) else {
panic!()
};
match binding {
SyntaxResult::Ok(TopLevelDeclaration::Binding(Binding::Val(binding)), _) => {
assert_eq!(Some(String::from("Num")), binding.datatype); assert_eq!(Some(String::from("Num")), binding.datatype);
assert_eq!("identifier", format!("{}", binding.identifier)); assert_eq!("identifier", format!("{}", binding.identifier));
}
_ => panic!(),
}
let tokens = get_tokens(&String::from("Bool var identifier = true")).unwrap(); let tokens = get_tokens(&String::from("Bool var identifier = 20")).unwrap();
let binding = try_parse(&tokens, 0).unwrap(); let ParseResult::Ok(Binding::Var(binding), _) = try_parse(&tokens, 0) else {
panic!()
};
match binding {
SyntaxResult::Ok(TopLevelDeclaration::Binding(Binding::Var(binding)), _) => {
assert_eq!(Some(String::from("Bool")), binding.datatype); assert_eq!(Some(String::from("Bool")), binding.datatype);
assert_eq!("identifier", format!("{}", binding.identifier)); assert_eq!("identifier", format!("{}", binding.identifier));
} }
_ => panic!("D: {:?}", binding),
}
}
#[test] #[test]
fn should_return_correct_error() { fn should_return_correct_error() {
let tokens = get_tokens(&String::from("val")).unwrap(); let tokens = get_tokens(&String::from("val")).unwrap();
assert_eq!(TokenType::VAL, tokens[0].token_type); assert_eq!(TokenType::VAL, tokens[0].token_type);
assert_eq!(0, tokens[0].position); assert_eq!(0, tokens[0].position);
let binding = try_parse(&tokens, 0).unwrap(); let binding = try_parse(&tokens, 0);
match binding { match binding {
SyntaxResult::Err(error) => { ParseResult::Err(error) => {
assert_eq!(0, error.error_start); assert_eq!(0, error.error_start);
assert_eq!(3, error.error_end); assert_eq!(3, error.error_end);
} }
@ -215,10 +203,10 @@ mod tests {
let tokens = get_tokens(&String::from("val 322")).unwrap(); let tokens = get_tokens(&String::from("val 322")).unwrap();
assert_eq!(TokenType::VAL, tokens[0].token_type); assert_eq!(TokenType::VAL, tokens[0].token_type);
assert_eq!(0, tokens[0].position); assert_eq!(0, tokens[0].position);
let binding = try_parse(&tokens, 0).unwrap(); let binding = try_parse(&tokens, 0);
match binding { match binding {
SyntaxResult::Err(error) => { ParseResult::Err(error) => {
assert_eq!(4, error.error_start); assert_eq!(4, error.error_start);
assert_eq!(7, error.error_end); assert_eq!(7, error.error_end);
} }
@ -226,10 +214,10 @@ mod tests {
} }
let tokens = get_tokens(&String::from("val \"hello\"")).unwrap(); let tokens = get_tokens(&String::from("val \"hello\"")).unwrap();
let binding = try_parse(&tokens, 0).unwrap(); let binding = try_parse(&tokens, 0);
match binding { match binding {
SyntaxResult::Err(error) => { ParseResult::Err(error) => {
assert_eq!(4, error.error_start); assert_eq!(4, error.error_start);
assert_eq!(11, error.error_end); assert_eq!(11, error.error_end);
} }
@ -240,10 +228,10 @@ mod tests {
#[test] #[test]
fn should_return_error_when_equal_op_is_wrong() { fn should_return_error_when_equal_op_is_wrong() {
let tokens = get_tokens(&String::from("val id \"error\"")).unwrap(); let tokens = get_tokens(&String::from("val id \"error\"")).unwrap();
let binding = try_parse(&tokens, 0).unwrap(); let binding = try_parse(&tokens, 0);
match binding { match binding {
SyntaxResult::Err(error) => { ParseResult::Err(error) => {
assert_eq!(7, error.error_start); assert_eq!(7, error.error_start);
assert_eq!(14, error.error_end); assert_eq!(14, error.error_end);
} }

View File

@ -5,7 +5,7 @@ use crate::{
}; };
use super::{ use super::{
ast::{FunctionDeclaration, ParamsList}, ast::FunctionDeclaration,
block::parse_block, block::parse_block,
params_list::parse_params_list, params_list::parse_params_list,
utils::{parse_token_type, try_token_type}, utils::{parse_token_type, try_token_type},
@ -98,7 +98,7 @@ pub fn try_parse<'a>(tokens: &'a Vec<Token>, pos: usize) -> ParseResult<Function
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{lexic::get_tokens, syntax::ast::TopLevelDeclaration}; use crate::lexic::get_tokens;
use super::*; use super::*;
@ -317,7 +317,7 @@ mod tests {
#[cfg(test)] #[cfg(test)]
mod whitespace_test { mod whitespace_test {
use crate::{lexic::get_tokens, syntax::ast::TopLevelDeclaration}; use crate::lexic::get_tokens;
use super::*; use super::*;