fix: add tests to the function declaration parser

This commit is contained in:
Fernando Araoz 2024-10-01 14:57:52 -05:00
parent 2a52615153
commit b7d7244cfa
4 changed files with 138 additions and 87 deletions

View File

@ -89,7 +89,7 @@ pub struct ParamsList<'a> {
impl Positionable for ParamsList<'_> { impl Positionable for ParamsList<'_> {
fn get_position(&self) -> (usize, usize) { fn get_position(&self) -> (usize, usize) {
todo!() (self.start, self.end)
} }
} }

View File

@ -4,7 +4,7 @@ use crate::{
syntax::{ syntax::{
ast::{Block, BlockMember, Expression, Statement}, ast::{Block, BlockMember, Expression, Statement},
parseable::{Parseable, ParsingError, ParsingResult}, parseable::{Parseable, ParsingError, ParsingResult},
utils::parse_token_type, utils::{parse_token_type, Tokenizer},
}, },
}; };
@ -80,15 +80,21 @@ impl<'a> Parseable<'a> for Block<'a> {
return Err(ParsingError::Err(econtainer)); return Err(ParsingError::Err(econtainer));
} }
Err(ParsingError::Unmatched) => { Err(ParsingError::Unmatched) => {
let label = ErrorLabel { let label_1 = ErrorLabel {
message: String::from("Expected a closing brace `}` here"), message: String::from("The block starts here"),
start: current_pos, start: opening_brace.position,
end: current_pos + 1, end: opening_brace.get_end_position(),
};
let label_2_pos = tokens.code_position_from_idx(current_pos);
let label_2 = ErrorLabel {
message: String::from("The code ends here without closing the block"),
start: label_2_pos,
end: label_2_pos + 1,
}; };
let econtainer = ErrorContainer { let econtainer = ErrorContainer {
error_code: SYNTAX_INCOMPLETE_BLOCK, error_code: SYNTAX_INCOMPLETE_BLOCK,
error_offset: current_pos, error_offset: tokens.code_position_from_idx(current_pos),
labels: vec![label], labels: vec![label_1, label_2],
note: None, note: None,
help: None, help: None,
}; };

View File

@ -7,7 +7,7 @@ use crate::{
ast::{Block, FunctionDeclaration, Positionable}, ast::{Block, FunctionDeclaration, Positionable},
functions::params_list::parse_params_list, functions::params_list::parse_params_list,
parseable::{Parseable, ParsingError, ParsingResult}, parseable::{Parseable, ParsingError, ParsingResult},
utils::{parse_token_type, try_operator}, utils::{parse_token_type, try_operator, Tokenizer},
}, },
}; };
@ -184,13 +184,13 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
} }
}; };
let label = ErrorLabel { let label = ErrorLabel {
message: String::from("Expected a block here, after the function declaration"), message: String::from("Expected a block after this parameter list"),
start: error_start, start: error_start,
end: error_end, end: error_end,
}; };
let econtainer = ErrorContainer { let econtainer = ErrorContainer {
error_code: SYNTAX_INVALID_FUNCTION_DECLARATION, error_code: SYNTAX_INVALID_FUNCTION_DECLARATION,
error_offset: error_start, error_offset: tokens.code_position_from_idx(current_pos),
labels: vec![label], labels: vec![label],
note: None, note: None,
help: None, help: None,
@ -215,7 +215,12 @@ impl<'a> Parseable<'a> for FunctionDeclaration<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{error_handling::error_messages::SYNTAX_INCOMPLETE_BLOCK, lexic::get_tokens}; use crate::{
error_handling::error_messages::{
SYNTAX_INCOMPLETE_BLOCK, SYNTAX_INCOMPLETE_PARAMETER_LIST,
},
lexic::get_tokens,
};
use super::*; use super::*;
@ -230,98 +235,125 @@ mod tests {
} }
#[test] #[test]
fn should_not_parse_fun_without_identifier() { fn should_fail_on_incomplete_1() {
let tokens = get_tokens(&String::from("fun = 20")).unwrap(); let tokens = get_tokens(&String::from("fun ")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl { match fun_decl {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 4);
}
_ => panic!("Expected an error: {:?}", fun_decl),
}
let tokens = get_tokens(&String::from("fun")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl {
Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 0); assert_eq!(err.error_offset, 0);
assert_eq!(SYNTAX_INVALID_FUNCTION_DECLARATION, err.error_code);
let first_label = &err.labels[0];
assert_eq!(first_label.start, 0);
assert_eq!(first_label.end, 3);
assert_eq!(
first_label.message,
"Expected an identifier after this `fun` keyword",
);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected a ParsingErr"),
} }
} }
#[test] #[test]
fn should_not_parse_fun_without_parens() { fn should_fail_on_invalid_identifier() {
let tokens = get_tokens(&String::from("fun 322")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl {
Err(ParsingError::Err(err)) => {
assert_eq!(err.error_offset, 4);
assert_eq!(SYNTAX_INVALID_FUNCTION_DECLARATION, err.error_code);
let first_label = &err.labels[0];
assert_eq!(first_label.start, 4);
assert_eq!(first_label.end, 7);
assert_eq!(first_label.message, "Expected an identifier here",);
}
_ => panic!("Expected a ParsingErr"),
}
}
#[test]
fn should_fail_on_missing_params_list() {
let tokens = get_tokens(&String::from("fun id")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl {
Err(ParsingError::Err(err)) => {
assert_eq!(err.error_offset, 6);
assert_eq!(SYNTAX_INVALID_FUNCTION_DECLARATION, err.error_code);
let first_label = &err.labels[0];
assert_eq!(first_label.start, 4);
assert_eq!(first_label.end, 6);
assert_eq!(
first_label.message,
"Expected a parameter list after this identifier",
);
}
_ => panic!("Expected a ParsingErr"),
}
}
#[test]
fn should_fail_on_invalid_params_list() {
let tokens = get_tokens(&String::from("fun id =")).unwrap(); let tokens = get_tokens(&String::from("fun id =")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl { match fun_decl {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 7); assert_eq!(err.error_offset, 8);
}
_ => panic!("Expected an error: {:?}", fun_decl),
}
let tokens = get_tokens(&String::from("fun id")).unwrap(); let label = &err.labels[0];
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); assert_eq!(label.start, 7);
match fun_decl { assert_eq!(label.end, 8);
Err(ParsingError::Err(err)) => { assert_eq!(label.message, "Expected a parameter list here");
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 4);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected an error, got {:?}", fun_decl),
} }
} }
#[test] #[test]
fn should_not_parse_fun_without_closing_paren() { fn should_fail_on_incomplete_params_list() {
let tokens = get_tokens(&String::from("fun id(=")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl {
Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 7);
}
_ => panic!("Expected an error: {:?}", fun_decl),
}
let tokens = get_tokens(&String::from("fun id(")).unwrap(); let tokens = get_tokens(&String::from("fun id(")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl { match fun_decl {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); assert_eq!(err.error_code, SYNTAX_INCOMPLETE_PARAMETER_LIST);
assert_eq!(err.error_offset, 6); assert_eq!(err.error_offset, 7);
let label = &err.labels[0];
assert_eq!(label.message, "The parameter list starts here");
assert_eq!(label.start, 6);
assert_eq!(label.end, 7);
let label = &err.labels[1];
assert_eq!(
label.message,
"The code ends here without closing the parameter list"
);
assert_eq!(label.start, 7);
assert_eq!(label.end, 8);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected an error: {:?}", fun_decl),
} }
} }
#[test] #[test]
fn should_not_parse_fun_when_missing_id() { fn should_fail_on_missing_body() {
let tokens = get_tokens(&String::from("fun")).unwrap(); let tokens = get_tokens(&String::from("fun id() ")).unwrap();
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); let fun_decl = FunctionDeclaration::try_parse(&tokens, 0);
match fun_decl { match fun_decl {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 0); assert_eq!(err.error_offset, 8);
}
_ => panic!("Expected an error: {:?}", fun_decl),
}
let tokens = get_tokens(&String::from("fun\n")).unwrap(); let label = &err.labels[0];
println!("{:?}", tokens); assert_eq!(label.message, "Expected a block after this parameter list");
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); assert_eq!(label.start, 6);
assert_eq!(label.end, 8);
match fun_decl {
Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 0);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected an error: {:?}", fun_decl),
} }
@ -336,16 +368,14 @@ mod tests {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 9); assert_eq!(err.error_offset, 9);
}
_ => panic!("Expected an error: {:?}", fun_decl),
}
let tokens = get_tokens(&String::from("fun id()")).unwrap(); let label = &err.labels[0];
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); assert_eq!(
match fun_decl { label.message,
Err(ParsingError::Err(err)) => { "Expected a block here, after the function declaration"
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); );
assert_eq!(err.error_offset, 4); assert_eq!(label.start, 9);
assert_eq!(label.end, 10);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected an error: {:?}", fun_decl),
} }
@ -359,18 +389,20 @@ mod tests {
match fun_decl { match fun_decl {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INCOMPLETE_BLOCK); assert_eq!(err.error_code, SYNTAX_INCOMPLETE_BLOCK);
assert_eq!(err.error_offset, 9); assert_eq!(err.error_offset, 10);
}
_ => panic!("Expected an error: {:?}", fun_decl),
}
let tokens = get_tokens(&String::from("fun id() {")).unwrap(); let label = &err.labels[0];
let fun_decl = FunctionDeclaration::try_parse(&tokens, 0); assert_eq!(label.message, "The block starts here");
assert_eq!(label.start, 9);
assert_eq!(label.end, 10);
match fun_decl { let label = &err.labels[1];
Err(ParsingError::Err(err)) => { assert_eq!(
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); label.message,
assert_eq!(err.error_offset, 9); "The code ends here without closing the block"
);
assert_eq!(label.start, 10);
assert_eq!(label.end, 11);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected an error: {:?}", fun_decl),
} }
@ -405,7 +437,12 @@ mod tests {
match fun_decl { match fun_decl {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 9); assert_eq!(err.error_offset, 12);
let label = &err.labels[0];
assert_eq!(label.message, "Expected a Datatype here");
assert_eq!(label.start, 12);
assert_eq!(label.end, 13);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected an error: {:?}", fun_decl),
} }
@ -420,6 +457,14 @@ mod tests {
Err(ParsingError::Err(err)) => { Err(ParsingError::Err(err)) => {
assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION); assert_eq!(err.error_code, SYNTAX_INVALID_FUNCTION_DECLARATION);
assert_eq!(err.error_offset, 9); assert_eq!(err.error_offset, 9);
let label = &err.labels[0];
assert_eq!(
label.message,
"Expected a Datatype after this arrow `->` operator"
);
assert_eq!(label.start, 9);
assert_eq!(label.end, 11);
} }
_ => panic!("Expected an error: {:?}", fun_decl), _ => panic!("Expected an error: {:?}", fun_decl),
} }

View File

@ -19,7 +19,7 @@ impl<'a> Parseable<'a> for ModuleAST<'a> {
/// As this function parses the whole file, it ignores `current_pos` and /// As this function parses the whole file, it ignores `current_pos` and
/// always starts from token 0. /// always starts from token 0.
/// ///
/// Its grammar is defined it the spec, at the webpage /// Its grammar is defined at the spec, at the webpage
fn try_parse(tokens: &'a Vec<Token>, _current_pos: usize) -> ParsingResult<'a, Self::Item> { fn try_parse(tokens: &'a Vec<Token>, _current_pos: usize) -> ParsingResult<'a, Self::Item> {
let mut productions = Vec::<ModuleMembers>::new(); let mut productions = Vec::<ModuleMembers>::new();
let tokens_len = tokens.len(); let tokens_len = tokens.len();