From e772c4b2c205cd1b9831226826ca5a598ae26447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Fri, 7 Mar 2025 17:36:25 +0100 Subject: [PATCH] implement variable assignments --- src/semantic/mod.rs | 4 ++ src/syntax/syntax_tree/statement.rs | 75 +++++++++++++++++++++++++++-- src/transpile/transpiler.rs | 6 +++ src/transpile/variables.rs | 18 +------ 4 files changed, 82 insertions(+), 21 deletions(-) diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index 91f6e4f..b257e7e 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -307,6 +307,10 @@ impl Semicolon { SemicolonStatement::VariableDeclaration(decl) => { decl.analyze_semantics(function_names, macro_names, handler) } + SemicolonStatement::Assignment(_assignment) => { + // TODO: correctly analyze the semantics of the assignment + Ok(()) + } } } } diff --git a/src/syntax/syntax_tree/statement.rs b/src/syntax/syntax_tree/statement.rs index 33dec6d..86831c5 100644 --- a/src/syntax/syntax_tree/statement.rs +++ b/src/syntax/syntax_tree/statement.rs @@ -93,6 +93,14 @@ impl Statement { }) }) } + SemicolonStatement::Assignment(_) => { + let err = Error::InvalidAnnotation(InvalidAnnotation { + annotation: annotation.assignment.identifier.span, + target: "assignments".to_string(), + }); + + Err(err) + } SemicolonStatement::Expression(_) => { let err = Error::InvalidAnnotation(InvalidAnnotation { annotation: annotation.assignment.identifier.span, @@ -272,7 +280,7 @@ impl Semicolon { /// Syntax Synopsis: /// ``` ebnf /// SemicolonStatement: -/// (Expression | VariableDeclaration) +/// (Expression | VariableDeclaration | Assignment) /// ';' /// ; /// ``` @@ -283,6 +291,8 @@ pub enum SemicolonStatement { Expression(Expression), /// A variable declaration. VariableDeclaration(VariableDeclaration), + /// An assignment. + Assignment(Assignment), } impl SourceElement for SemicolonStatement { @@ -290,6 +300,7 @@ impl SourceElement for SemicolonStatement { match self { Self::Expression(expression) => expression.span(), Self::VariableDeclaration(declaration) => declaration.span(), + Self::Assignment(assignment) => assignment.span(), } } } @@ -669,6 +680,44 @@ impl TagVariableDeclaration { } } +/// Represents an assignment in the syntax tree. +/// +/// Syntax Synopsis: +/// ```ebnf +/// Assignment: +/// Identifier '=' Expression +/// ``` +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct Assignment { + /// The identifier of the assignment. + #[get = "pub"] + identifier: Identifier, + /// The equals sign of the assignment. + #[get = "pub"] + equals: Punctuation, + /// The expression of the assignment. + #[get = "pub"] + expression: Expression, +} + +impl SourceElement for Assignment { + fn span(&self) -> Span { + self.identifier + .span() + .join(&self.expression.span()) + .expect("The span of the assignment is invalid.") + } +} + +impl Assignment { + /// Dissolves the [`Assignment`] into its components. + #[must_use] + pub fn dissolve(self) -> (Identifier, Punctuation, Expression) { + (self.identifier, self.equals, self.expression) + } +} + impl<'a> Parser<'a> { /// Parses a [`Block`]. /// @@ -808,9 +857,27 @@ impl<'a> Parser<'a> { self.parse_variable_declaration(handler) .map(SemicolonStatement::VariableDeclaration) } - _ => self - .parse_expression(handler) - .map(SemicolonStatement::Expression), + _ => { + // try to parse assignment + // TODO: improve + #[expect(clippy::option_if_let_else)] + if let Ok(assignment) = self.try_parse(|p| { + let identifier = p.parse_identifier(&VoidHandler)?; + let equals = p.parse_punctuation('=', true, &VoidHandler)?; + let expression = p.parse_expression(&VoidHandler)?; + + Ok(SemicolonStatement::Assignment(Assignment { + identifier, + equals, + expression, + })) + }) { + Ok(assignment) + } else { + self.parse_expression(handler) + .map(SemicolonStatement::Expression) + } + } }?; let semicolon = self.parse_punctuation(';', true, handler)?; diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index 1f9437b..0ec184a 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -566,6 +566,12 @@ impl Transpiler { SemicolonStatement::VariableDeclaration(decl) => { self.transpile_variable_declaration(decl, program_identifier, scope, handler) } + SemicolonStatement::Assignment(assignment) => self.transpile_assignment( + assignment.identifier(), + assignment.expression(), + scope, + handler, + ), }, } } diff --git a/src/transpile/variables.rs b/src/transpile/variables.rs index 2031243..580ce3e 100644 --- a/src/transpile/variables.rs +++ b/src/transpile/variables.rs @@ -254,22 +254,6 @@ impl Transpiler { ); } KeywordKind::Bool => { - let setup_cmd = Command::Execute(Execute::If( - Condition::Not(Box::new(Condition::Atom( - format!( - "data storage {namespace}:{name} {target}", - namespace = self.main_namespace_name - ) - .into(), - ))), - Box::new(Execute::Run(Box::new(Command::Raw(format!( - r#"data merge storage {namespace}:{name} {{"{target}": 0b}}"#, - namespace = self.main_namespace_name - ))))), - None, - )); - self.setup_cmds.push(setup_cmd); - scope.set_variable( single.identifier().span.str(), VariableData::BooleanStorage { @@ -294,7 +278,7 @@ impl Transpiler { ) } - fn transpile_assignment( + pub(super) fn transpile_assignment( &mut self, identifier: &Identifier, expression: &crate::syntax::syntax_tree::expression::Expression,