From b48135159744044f56228c2255294977604b081a Mon Sep 17 00:00:00 2001 From: Araozu Date: Tue, 27 Aug 2024 09:43:08 -0500 Subject: [PATCH] feat: enforce new line after a statement --- CHANGELOG.md | 1 + src/semantic/types/expression.rs | 4 +++- src/syntax/parsers/binding.rs | 38 +++++++++++++++++++++++++++++ src/syntax/parsers/module.rs | 41 ++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3874c0..0fd6b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ - [x] Check for conflicting identifiers at the current scope - [x] Semantic check for unary operator - [x] Semantic check for binary operator +- [x] Enforce new lines after every statement ## v0.1.0 diff --git a/src/semantic/types/expression.rs b/src/semantic/types/expression.rs index 68cc797..c36b8e5 100644 --- a/src/semantic/types/expression.rs +++ b/src/semantic/types/expression.rs @@ -58,7 +58,9 @@ impl Typed for Expression<'_> { })), } } - _ => unimplemented!("Get datatype of an expression that resolves into a function call"), + _ => unimplemented!( + "Get datatype of an expression that resolves into a function call" + ), } } Expression::UnaryOperator(op, exp) => { diff --git a/src/syntax/parsers/binding.rs b/src/syntax/parsers/binding.rs index 44d1d5c..83a4cd4 100644 --- a/src/syntax/parsers/binding.rs +++ b/src/syntax/parsers/binding.rs @@ -125,6 +125,26 @@ impl<'a> Parseable<'a> for VariableBinding<'a> { } }; + // After the expression there should be a new line + // to terminate the statement + match tokens.get(next_pos) { + Some(t) if t.token_type == TokenType::NewLine || t.token_type == TokenType::EOF => { + // continue + } + Some(t) => { + return Err(ParsingError::Err(SyntaxError { + error_start: t.position, + error_end: t.get_end_position(), + reason: format!("Unexpected token `{}`, expected a new line", t.value), + })) + } + _ => { + // this should never happen, the lexer always appends + // an EOF + unreachable!("got to the final of a token stream without finding EOF") + } + } + let binding = VariableBinding { datatype, identifier: &identifier, @@ -354,4 +374,22 @@ mod tests { _ => panic!("Error expected"), } } + + #[test] + fn should_error_when_there_is_no_delimiter() { + let tokens = get_tokens(&String::from("val identifier = 322 print(identifier)")).unwrap(); + let binding = VariableBinding::try_parse(&tokens, 0); + + match binding { + Err(ParsingError::Err(error)) => { + assert_eq!(21, error.error_start); + assert_eq!(26, error.error_end); + assert_eq!( + "Unexpected token `print`, expected a new line", + error.reason + ); + } + _ => panic!("Error expected"), + } + } } diff --git a/src/syntax/parsers/module.rs b/src/syntax/parsers/module.rs index 8e9e5b1..1c190b1 100644 --- a/src/syntax/parsers/module.rs +++ b/src/syntax/parsers/module.rs @@ -40,6 +40,32 @@ impl<'a> Parseable<'a> for ModuleAST<'a> { // Attempt to parse an expression match Expression::try_parse(tokens, current_pos) { Ok((prod, next_pos)) => { + // After a expression is parsed as an statement + // there should be a delimiter (new line or EOF) + match tokens.get(next_pos) { + Some(t) + if t.token_type == TokenType::NewLine + || t.token_type == TokenType::EOF => + { + // continue + } + Some(t) => { + return Err(ParsingError::Err(SyntaxError { + error_start: t.position, + error_end: t.get_end_position(), + reason: format!( + "Unexpected token `{}`, expected a new line", + t.value + ), + })) + } + _ => { + // this should never happen, the lexer always appends + // an EOF + unreachable!("got to the final of a token stream without finding EOF") + } + } + productions.push(ModuleMembers::Expr(prod)); current_pos = next_pos; continue; @@ -122,4 +148,19 @@ mod test { Err(_) => {} } } + + #[test] + fn should_fail_on_two_expressions_together() { + // TODO: enforce newlines after every statement + let tokens = get_tokens(&String::from("print print")).unwrap(); + let result = ModuleAST::try_parse(&tokens, 0); + + match result { + Ok(_) => panic!("Expected an error"), + Err(ParsingError::Err(err)) => { + assert_eq!("Unexpected token `print`, expected a new line", err.reason); + } + _ => panic!("Expected a parsing error"), + } + } }