From aede32a06893c63aedd2bfa6f14d5da0649f1fca Mon Sep 17 00:00:00 2001 From: Araozu Date: Sat, 19 Oct 2024 20:57:09 -0500 Subject: [PATCH] feat: parse assignments --- CHANGELOG.md | 7 ++ src/error_handling/error_messages.rs | 1 + src/semantic/checks/top_level_declaration.rs | 3 + src/syntax/ast/mod.rs | 9 ++ src/syntax/parsers/assignment.rs | 91 ++++++++++++++++++++ src/syntax/parsers/mod.rs | 1 + src/syntax/parsers/statement.rs | 9 +- 7 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 src/syntax/parsers/assignment.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 37fbcbe..a10f437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Remove all panic! and todo! - Change REPL to execute code only after `;;` is found - Forward the code generated by the REPL to the PHP repl +- Test assignment parsing ## v0.1.3 @@ -23,6 +24,12 @@ - [ ] Test semantic analysis - [ ] Generate php code from current AST - [x] Test correct operator precedence +- [x] Parse assignments +- [ ] Parse dot `.` operator +- [ ] Parse logic operators `&& ||` +- [ ] Parse Array access `arr[pos]` +- [ ] Parse namespace operator `::` +- [ ] Implement subtyping for numbers ## v0.1.2 diff --git a/src/error_handling/error_messages.rs b/src/error_handling/error_messages.rs index ea01446..5440793 100644 --- a/src/error_handling/error_messages.rs +++ b/src/error_handling/error_messages.rs @@ -24,6 +24,7 @@ pub const COMPILER_TODO: u32 = 20; pub const SEMANTIC_MISMATCHED_TYPES: u32 = 21; pub const SEMANTIC_DUPLICATED_REFERENCE: u32 = 22; pub const SEMANTIC_MISMATCHED_ARGUMENT_COUNT: u32 = 23; +pub const SYNTAX_INVALID_ASSIGNMENT: u32 = 24; /// Reads the error codes from the error code list pub fn error_code_to_string() -> String { diff --git a/src/semantic/checks/top_level_declaration.rs b/src/semantic/checks/top_level_declaration.rs index 353b54d..14fd2c4 100644 --- a/src/semantic/checks/top_level_declaration.rs +++ b/src/semantic/checks/top_level_declaration.rs @@ -25,6 +25,9 @@ impl SemanticCheck for Statement<'_> { Statement::Conditional(c) => c.check_semantics(scope), Statement::ForLoop(f) => f.check_semantics(scope), Statement::WhileLoop(w) => w.check_semantics(scope), + Statement::Assignment(_assignment) => { + unimplemented!("Semantic check for an assignment") + } } } } diff --git a/src/syntax/ast/mod.rs b/src/syntax/ast/mod.rs index c18ead1..cd67556 100644 --- a/src/syntax/ast/mod.rs +++ b/src/syntax/ast/mod.rs @@ -37,6 +37,15 @@ pub enum Statement<'a> { Conditional(Conditional<'a>), ForLoop(ForLoop<'a>), WhileLoop(WhileLoop<'a>), + Assignment(Assignment<'a>), +} + +#[derive(Debug)] +pub struct Assignment<'a> { + /// The left side of the assignment + pub identifier: &'a Token, + /// The right side of the assignment + pub expression: Box>, } #[derive(Debug)] diff --git a/src/syntax/parsers/assignment.rs b/src/syntax/parsers/assignment.rs new file mode 100644 index 0000000..2e76f93 --- /dev/null +++ b/src/syntax/parsers/assignment.rs @@ -0,0 +1,91 @@ +use crate::{ + error_handling::{error_messages::SYNTAX_INVALID_ASSIGNMENT, ErrorContainer, ErrorLabel}, + lexic::token::{self, TokenType}, + syntax::{ + ast::{Assignment, Expression}, + parseable::{self, Parseable, ParsingError}, + utils::{parse_token_type, try_operator}, + }, +}; + +/// https://thp-lang.org/spec/ast/ast/#assignment +impl<'a> Parseable<'a> for Assignment<'a> { + type Item = Assignment<'a>; + + fn try_parse( + tokens: &'a Vec, + current_pos: usize, + ) -> parseable::ParsingResult<'a, Self::Item> { + // parse the target identifier + let (identifier, next) = match parse_token_type(tokens, current_pos, TokenType::Identifier) + { + Ok(tuple) => tuple, + _ => return Err(ParsingError::Unmatched), + }; + + // parse the equal sign + let (equal_operator, next) = match try_operator(tokens, next, String::from("=")) { + Ok((t, next)) => (t, next), + Err(ParsingError::Mismatch(t)) => { + // The parser found a token, but it's not the `=` operator + let label = ErrorLabel { + message: String::from("Expected an equal sign `=` here, the identifier"), + start: t.position, + end: t.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_ASSIGNMENT, + error_offset: t.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); + } + _ => { + // The parser didn't find the `=` operator after the identifier + let label = ErrorLabel { + message: String::from("Expected an equal sign `=` after this identifier"), + start: identifier.position, + end: identifier.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_ASSIGNMENT, + error_offset: identifier.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); + } + }; + + // parse the expression + let (expression, next) = match Expression::try_parse(tokens, next) { + Ok((exp, next)) => (exp, next), + _ => { + let label = ErrorLabel { + message: String::from("Expected an expression after this equal `=` operator"), + start: equal_operator.position, + end: equal_operator.get_end_position(), + }; + let econtainer = ErrorContainer { + error_code: SYNTAX_INVALID_ASSIGNMENT, + error_offset: equal_operator.position, + labels: vec![label], + note: None, + help: None, + }; + return Err(ParsingError::Err(econtainer)); + } + }; + + // Build and return the assignment object + let assignment = Assignment { + identifier, + expression: Box::new(expression), + }; + + Ok((assignment, next)) + } +} diff --git a/src/syntax/parsers/mod.rs b/src/syntax/parsers/mod.rs index 5c519c1..8cdd575 100644 --- a/src/syntax/parsers/mod.rs +++ b/src/syntax/parsers/mod.rs @@ -1,3 +1,4 @@ +pub mod assignment; pub mod binding; pub mod block; pub mod conditional; diff --git a/src/syntax/parsers/statement.rs b/src/syntax/parsers/statement.rs index e20f317..f9f0ac7 100644 --- a/src/syntax/parsers/statement.rs +++ b/src/syntax/parsers/statement.rs @@ -4,7 +4,7 @@ use crate::{ ast::{ loops::{ForLoop, WhileLoop}, var_binding::VariableBinding, - Conditional, FunctionDeclaration, Statement, + Assignment, Conditional, FunctionDeclaration, Statement, }, parseable::{Parseable, ParsingError, ParsingResult}, }, @@ -59,6 +59,13 @@ impl<'a> Parseable<'a> for Statement<'a> { _ => {} } + // Try to parse an assignment + match Assignment::try_parse(tokens, current_pos) { + Ok((prod, next)) => return Ok((Statement::Assignment(prod), next)), + Err(ParsingError::Err(e)) => return Err(ParsingError::Err(e)), + _ => {} + } + // Here nothing was parsed. Err(ParsingError::Unmatched) }