From 62dd12cab3f308b3b52a8ed14b01351f3c685bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:16:12 +0200 Subject: [PATCH] implement function calls --- grammar.md | 28 ++ src/syntax/syntax_tree/condition.rs | 378 +++++++++++++++++++++++++ src/syntax/syntax_tree/expression.rs | 396 ++++++--------------------- src/syntax/syntax_tree/mod.rs | 1 + src/syntax/syntax_tree/statement.rs | 88 ++++-- src/transpile/conversions.rs | 2 +- src/transpile/transpiler.rs | 197 ++++++++----- 7 files changed, 672 insertions(+), 418 deletions(-) create mode 100644 src/syntax/syntax_tree/condition.rs diff --git a/grammar.md b/grammar.md index c573a7f..d444ee9 100644 --- a/grammar.md +++ b/grammar.md @@ -33,6 +33,8 @@ Statement: Block | LiteralCommand | Conditional + | Grouping + | DocComment ; ``` @@ -59,4 +61,30 @@ ParenthizedCondition: ```ebnf Condition: StringLiteral +``` + +### Grouping +``` ebnf +Grouping: + 'group' Block +; +``` + +### Expression +```ebnf +Expression: + Primary +``` + +### Primary +``` ebnf +Primary: + FunctionCall +``` + +### FunctionCall +``` ebnf +FunctionCall: + Identifier '(' (Expression (',' Expression)*)? ')' + ; ``` \ No newline at end of file diff --git a/src/syntax/syntax_tree/condition.rs b/src/syntax/syntax_tree/condition.rs new file mode 100644 index 0000000..4aa40ba --- /dev/null +++ b/src/syntax/syntax_tree/condition.rs @@ -0,0 +1,378 @@ +//! Syntax tree nodes for conditions. + +use std::{cmp::Ordering, collections::VecDeque}; + +use enum_as_inner::EnumAsInner; +use getset::Getters; + +use crate::{ + base::{ + source_file::{SourceElement, Span}, + Dummy, Handler, + }, + lexical::{ + token::{Punctuation, StringLiteral, Token}, + token_stream::Delimiter, + }, + syntax::{ + error::{Error, SyntaxKind, UnexpectedSyntax}, + parser::{Parser, Reading}, + }, +}; + +/// Syntax Synopsis: +/// +/// ``` ebnf +/// Expression: +/// Prefix +/// | Parenthesized +/// | StringLiteral +/// ``` +#[allow(missing_docs)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] +pub enum PrimaryCondition { + Prefix(ConditionalPrefix), + Parenthesized(ParenthesizedCondition), + StringLiteral(StringLiteral), +} + +impl SourceElement for PrimaryCondition { + fn span(&self) -> Span { + match self { + Self::Prefix(prefix) => prefix.span(), + Self::Parenthesized(parenthesized) => parenthesized.span(), + Self::StringLiteral(literal) => literal.span(), + } + } +} + +/// Syntax Synopsis: +/// +/// ``` ebnf +/// BinaryCondition: +/// Condition ConditionalBinaryOperator Condition +/// ; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct BinaryCondition { + /// The left operand of the binary condition. + #[get = "pub"] + left_operand: Box, + /// The operator of the binary condition. + #[get = "pub"] + operator: ConditionalBinaryOperator, + /// The right operand of the binary condition. + #[get = "pub"] + right_operand: Box, +} + +impl SourceElement for BinaryCondition { + fn span(&self) -> Span { + self.left_operand + .span() + .join(&self.right_operand.span()) + .unwrap() + } +} + +impl BinaryCondition { + /// Dissolves the binary condition into its components + #[must_use] + pub fn dissolve(self) -> (Condition, ConditionalBinaryOperator, Condition) { + (*self.left_operand, self.operator, *self.right_operand) + } +} + +/// Syntax Synopsis: +/// +/// ``` ebnf +/// BinaryOperator: +/// '&&' +/// | '||' +/// ; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] +#[allow(missing_docs)] +pub enum ConditionalBinaryOperator { + LogicalAnd(Punctuation, Punctuation), + LogicalOr(Punctuation, Punctuation), +} + +impl ConditionalBinaryOperator { + /// Gets the precedence of the operator (the higher the number, the first it will be evaluated) + /// + /// The least operator has precedence 1. + #[must_use] + pub fn get_precedence(&self) -> u8 { + match self { + Self::LogicalOr(..) => 1, + Self::LogicalAnd(..) => 2, + } + } +} + +impl SourceElement for ConditionalBinaryOperator { + fn span(&self) -> Span { + match self { + Self::LogicalAnd(a, b) | Self::LogicalOr(a, b) => a + .span + .join(&b.span) + .expect("Invalid tokens for ConditionalBinaryOperator"), + } + } +} + +/// Syntax Synopsis: +/// +/// ``` ebnf +/// ParenthesizedCondition: +/// '(' Condition ')'; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct ParenthesizedCondition { + /// The opening parenthesis. + #[get = "pub"] + pub open_paren: Punctuation, + /// The condition within the parenthesis. + #[get = "pub"] + pub condition: Box, + /// The closing parenthesis. + #[get = "pub"] + pub close_paren: Punctuation, +} + +impl ParenthesizedCondition { + /// Dissolves the parenthesized condition into its components + #[must_use] + pub fn dissolve(self) -> (Punctuation, Condition, Punctuation) { + (self.open_paren, *self.condition, self.close_paren) + } +} + +impl SourceElement for ParenthesizedCondition { + fn span(&self) -> Span { + self.open_paren + .span() + .join(&self.close_paren.span()) + .expect("The span of the parenthesis is invalid.") + } +} + +/// Syntax Synopsis: +/// +/// ``` ebnf +/// PrefixOperator: '!'; +/// ``` +#[allow(missing_docs)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] +pub enum ConditionalPrefixOperator { + LogicalNot(Punctuation), +} + +impl SourceElement for ConditionalPrefixOperator { + fn span(&self) -> Span { + match self { + Self::LogicalNot(token) => token.span.clone(), + } + } +} + +/// Syntax Synopsis: +/// +/// ```ebnf +/// Prefix: +/// ConditionalPrefixOperator StringLiteral +/// ; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct ConditionalPrefix { + /// The operator of the prefix. + #[get = "pub"] + operator: ConditionalPrefixOperator, + /// The operand of the prefix. + #[get = "pub"] + operand: Box, +} + +impl SourceElement for ConditionalPrefix { + fn span(&self) -> Span { + self.operator.span().join(&self.operand.span()).unwrap() + } +} +impl ConditionalPrefix { + /// Dissolves the conditional prefix into its components + #[must_use] + pub fn dissolve(self) -> (ConditionalPrefixOperator, PrimaryCondition) { + (self.operator, *self.operand) + } +} + +/// Syntax Synopsis: +/// +/// ``` ebnf +/// Condition: PrimaryCondition; +/// ``` +#[allow(missing_docs)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] +pub enum Condition { + Primary(PrimaryCondition), + Binary(BinaryCondition), +} + +impl SourceElement for Condition { + fn span(&self) -> Span { + match self { + Self::Primary(primary) => primary.span(), + Self::Binary(binary) => binary.span(), + } + } +} + +impl<'a> Parser<'a> { + /// Parses a [`Condition`]. + pub fn parse_condition(&mut self, handler: &impl Handler) -> Option { + let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?); + let mut expressions = VecDeque::new(); + + // Parses a list of binary operators and expressions + while let Some(binary_operator) = self.try_parse_conditional_binary_operator() { + expressions.push_back(( + binary_operator, + Some(Condition::Primary(self.parse_primary_condition(handler)?)), + )); + } + + let mut candidate_index = 0; + let mut current_precedence; + + while !expressions.is_empty() { + // reset precedence + current_precedence = 0; + + for (index, (binary_op, _)) in expressions.iter().enumerate() { + let new_precedence = binary_op.get_precedence(); + match new_precedence.cmp(¤t_precedence) { + // Clear the candidate indices and set the current precedence to the + // precedence of the current binary operator. + Ordering::Greater => { + current_precedence = new_precedence; + candidate_index = index; + } + + Ordering::Less | Ordering::Equal => (), + } + } + + // ASSUMPTION: The assignments have 1 precedence and are right associative. + assert!(current_precedence > 0); + + if candidate_index == 0 { + let (binary_op, rhs) = expressions.pop_front().expect("No binary operator found"); + + // fold the first expression + lhs = Condition::Binary(BinaryCondition { + left_operand: Box::new(lhs), + operator: binary_op, + right_operand: Box::new(rhs.unwrap()), + }); + } else { + let (binary_op, rhs) = expressions + .remove(candidate_index) + .expect("No binary operator found"); + + // fold the expression at candidate_index + expressions[candidate_index - 1].1 = Some(Condition::Binary(BinaryCondition { + left_operand: Box::new(expressions[candidate_index - 1].1.take().unwrap()), + operator: binary_op, + right_operand: Box::new(rhs.unwrap()), + })); + } + } + + Some(lhs) + } + + /// Parses a [`PrimaryCondition`]. + pub fn parse_primary_condition( + &mut self, + handler: &impl Handler, + ) -> Option { + match self.stop_at_significant() { + // prefixed expression + Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '!' => { + // eat prefix operator + self.forward(); + + let operator = match punc.punctuation { + '!' => ConditionalPrefixOperator::LogicalNot(punc), + _ => unreachable!(), + }; + + let operand = Box::new(self.parse_primary_condition(handler)?); + + Some(PrimaryCondition::Prefix(ConditionalPrefix { + operator, + operand, + })) + } + + // string literal + Reading::Atomic(Token::StringLiteral(literal)) => { + self.forward(); + Some(PrimaryCondition::StringLiteral(literal)) + } + + // parenthesized condition + Reading::IntoDelimited(punc) if punc.punctuation == '(' => self + .parse_parenthesized_condition(handler) + .map(PrimaryCondition::Parenthesized), + + unexpected => { + // make progress + self.forward(); + + handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { + expected: SyntaxKind::Expression, + found: unexpected.into_token(), + })); + + None + } + } + } + + /// Parses a [`ParenthesizedCondition`]. + pub fn parse_parenthesized_condition( + &mut self, + handler: &impl Handler, + ) -> Option { + let token_tree = self.step_into( + Delimiter::Parenthesis, + |parser| parser.parse_condition(handler), + handler, + )?; + + Some(ParenthesizedCondition { + open_paren: token_tree.open, + condition: Box::new(token_tree.tree?), + close_paren: token_tree.close, + }) + } + + fn try_parse_conditional_binary_operator(&mut self) -> Option { + self.try_parse(|parser| match parser.next_significant_token() { + Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation { + '&' => { + let b = parser.parse_punctuation('&', false, &Dummy)?; + Some(ConditionalBinaryOperator::LogicalAnd(punc, b)) + } + '|' => { + let b = parser.parse_punctuation('|', false, &Dummy)?; + Some(ConditionalBinaryOperator::LogicalOr(punc, b)) + } + _ => None, + }, + _ => None, + }) + } +} diff --git a/src/syntax/syntax_tree/expression.rs b/src/syntax/syntax_tree/expression.rs index f3900b3..1f7ac74 100644 --- a/src/syntax/syntax_tree/expression.rs +++ b/src/syntax/syntax_tree/expression.rs @@ -1,338 +1,139 @@ //! Syntax tree nodes for expressions. -use std::{cmp::Ordering, collections::VecDeque}; - use enum_as_inner::EnumAsInner; use getset::Getters; use crate::{ base::{ source_file::{SourceElement, Span}, - Dummy, Handler, + Handler, }, lexical::{ - token::{Punctuation, StringLiteral, Token}, + token::{Identifier, Punctuation, Token}, token_stream::Delimiter, }, syntax::{ - error::{Error, SyntaxKind, UnexpectedSyntax}, + error::{Error, UnexpectedSyntax}, parser::{Parser, Reading}, }, }; -/// Syntax Synopsis: -/// -/// ``` ebnf -/// Expression: -/// Prefix -/// | Parenthesized -/// | StringLiteral -/// ``` -#[allow(missing_docs)] -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] -pub enum PrimaryCondition { - Prefix(ConditionalPrefix), - Parenthesized(ParenthesizedCondition), - StringLiteral(StringLiteral), -} - -impl SourceElement for PrimaryCondition { - fn span(&self) -> Span { - match self { - Self::Prefix(prefix) => prefix.span(), - Self::Parenthesized(parenthesized) => parenthesized.span(), - Self::StringLiteral(literal) => literal.span(), - } - } -} - -/// Syntax Synopsis: -/// -/// ``` ebnf -/// BinaryCondition: -/// Condition ConditionalBinaryOperator Condition -/// ; -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] -pub struct BinaryCondition { - /// The left operand of the binary condition. - #[get = "pub"] - left_operand: Box, - /// The operator of the binary condition. - #[get = "pub"] - operator: ConditionalBinaryOperator, - /// The right operand of the binary condition. - #[get = "pub"] - right_operand: Box, -} - -impl SourceElement for BinaryCondition { - fn span(&self) -> Span { - self.left_operand - .span() - .join(&self.right_operand.span()) - .unwrap() - } -} - -impl BinaryCondition { - /// Dissolves the binary condition into its components - #[must_use] - pub fn dissolve(self) -> (Condition, ConditionalBinaryOperator, Condition) { - (*self.left_operand, self.operator, *self.right_operand) - } -} - -/// Syntax Synopsis: -/// -/// ``` ebnf -/// BinaryOperator: -/// '&&' -/// | '||' -/// ; -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] -#[allow(missing_docs)] -pub enum ConditionalBinaryOperator { - LogicalAnd(Punctuation, Punctuation), - LogicalOr(Punctuation, Punctuation), -} - -impl ConditionalBinaryOperator { - /// Gets the precedence of the operator (the higher the number, the first it will be evaluated) - /// - /// The least operator has precedence 1. - #[must_use] - pub fn get_precedence(&self) -> u8 { - match self { - Self::LogicalOr(..) => 1, - Self::LogicalAnd(..) => 2, - } - } -} - -impl SourceElement for ConditionalBinaryOperator { - fn span(&self) -> Span { - match self { - Self::LogicalAnd(a, b) | Self::LogicalOr(a, b) => a - .span - .join(&b.span) - .expect("Invalid tokens for ConditionalBinaryOperator"), - } - } -} - -/// Syntax Synopsis: -/// -/// ``` ebnf -/// ParenthesizedCondition: -/// '(' Condition ')'; -/// ``` -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] -pub struct ParenthesizedCondition { - /// The opening parenthesis. - #[get = "pub"] - pub open_paren: Punctuation, - /// The condition within the parenthesis. - #[get = "pub"] - pub condition: Box, - /// The closing parenthesis. - #[get = "pub"] - pub close_paren: Punctuation, -} - -impl ParenthesizedCondition { - /// Dissolves the parenthesized condition into its components - #[must_use] - pub fn dissolve(self) -> (Punctuation, Condition, Punctuation) { - (self.open_paren, *self.condition, self.close_paren) - } -} - -impl SourceElement for ParenthesizedCondition { - fn span(&self) -> Span { - self.open_paren - .span() - .join(&self.close_paren.span()) - .expect("The span of the parenthesis is invalid.") - } -} - -/// Syntax Synopsis: -/// -/// ``` ebnf -/// PrefixOperator: '!'; -/// ``` -#[allow(missing_docs)] -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] -pub enum ConditionalPrefixOperator { - LogicalNot(Punctuation), -} - -impl SourceElement for ConditionalPrefixOperator { - fn span(&self) -> Span { - match self { - Self::LogicalNot(token) => token.span.clone(), - } - } -} +use super::ConnectedList; /// Syntax Synopsis: /// /// ```ebnf -/// Prefix: -/// ConditionalPrefixOperator StringLiteral -/// ; +/// Expression: +/// Primary /// ``` -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] -pub struct ConditionalPrefix { - /// The operator of the prefix. - #[get = "pub"] - operator: ConditionalPrefixOperator, - /// The operand of the prefix. - #[get = "pub"] - operand: Box, +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] +#[allow(missing_docs)] +pub enum Expression { + Primary(Primary), } -impl SourceElement for ConditionalPrefix { +impl SourceElement for Expression { fn span(&self) -> Span { - self.operator.span().join(&self.operand.span()).unwrap() - } -} -impl ConditionalPrefix { - /// Dissolves the conditional prefix into its components - #[must_use] - pub fn dissolve(self) -> (ConditionalPrefixOperator, PrimaryCondition) { - (self.operator, *self.operand) + match self { + Self::Primary(primary) => primary.span(), + } } } /// Syntax Synopsis: /// /// ``` ebnf -/// Condition: PrimaryCondition; +/// Primary: +/// FunctionCall /// ``` -#[allow(missing_docs)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] -pub enum Condition { - Primary(PrimaryCondition), - Binary(BinaryCondition), +#[allow(missing_docs)] +pub enum Primary { + FunctionCall(FunctionCall), } -impl SourceElement for Condition { +impl SourceElement for Primary { fn span(&self) -> Span { match self { - Self::Primary(primary) => primary.span(), - Self::Binary(binary) => binary.span(), + Self::FunctionCall(function_call) => function_call.span(), } } } +/// Syntax Synopsis: +/// +/// ``` ebnf +/// FunctionCall: +/// Identifier '(' (Expression (',' Expression)*)? ')' +/// ; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct FunctionCall { + /// The identifier of the function. + #[get = "pub"] + identifier: Identifier, + /// The left parenthesis of the function call. + #[get = "pub"] + left_parenthesis: Punctuation, + /// The arguments of the function call. + #[get = "pub"] + arguments: Option, Punctuation>>, + /// The right parenthesis of the function call. + #[get = "pub"] + right_parenthesis: Punctuation, +} + +impl SourceElement for FunctionCall { + fn span(&self) -> Span { + self.identifier + .span() + .join(&self.right_parenthesis.span) + .unwrap() + } +} + impl<'a> Parser<'a> { - /// Parses a [`Condition`]. - pub fn parse_condition(&mut self, handler: &impl Handler) -> Option { - let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?); - let mut expressions = VecDeque::new(); - - // Parses a list of binary operators and expressions - while let Some(binary_operator) = self.try_parse_conditional_binary_operator() { - expressions.push_back(( - binary_operator, - Some(Condition::Primary(self.parse_primary_condition(handler)?)), - )); - } - - let mut candidate_index = 0; - let mut current_precedence; - - while !expressions.is_empty() { - // reset precedence - current_precedence = 0; - - for (index, (binary_op, _)) in expressions.iter().enumerate() { - let new_precedence = binary_op.get_precedence(); - match new_precedence.cmp(¤t_precedence) { - // Clear the candidate indices and set the current precedence to the - // precedence of the current binary operator. - Ordering::Greater => { - current_precedence = new_precedence; - candidate_index = index; - } - - Ordering::Less | Ordering::Equal => (), - } - } - - // ASSUMPTION: The assignments have 1 precedence and are right associative. - assert!(current_precedence > 0); - - if candidate_index == 0 { - let (binary_op, rhs) = expressions.pop_front().expect("No binary operator found"); - - // fold the first expression - lhs = Condition::Binary(BinaryCondition { - left_operand: Box::new(lhs), - operator: binary_op, - right_operand: Box::new(rhs.unwrap()), - }); - } else { - let (binary_op, rhs) = expressions - .remove(candidate_index) - .expect("No binary operator found"); - - // fold the expression at candidate_index - expressions[candidate_index - 1].1 = Some(Condition::Binary(BinaryCondition { - left_operand: Box::new(expressions[candidate_index - 1].1.take().unwrap()), - operator: binary_op, - right_operand: Box::new(rhs.unwrap()), - })); - } - } - - Some(lhs) + /// Parses an [`Expression`] + pub fn parse_expression(&mut self, handler: &impl Handler) -> Option { + Some(Expression::Primary(self.parse_primary(handler)?)) } - /// Parses a [`PrimaryCondition`]. - pub fn parse_primary_condition( - &mut self, - handler: &impl Handler, - ) -> Option { + /// Parses an [`Primary`] + pub fn parse_primary(&mut self, handler: &impl Handler) -> Option { match self.stop_at_significant() { - // prefixed expression - Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '!' => { - // eat prefix operator + // identifier expression + Reading::Atomic(Token::Identifier(identifier)) => { + // eat the identifier self.forward(); - let operator = match punc.punctuation { - '!' => ConditionalPrefixOperator::LogicalNot(punc), - _ => unreachable!(), - }; + // function call + if matches!(self.stop_at_significant(), Reading::IntoDelimited(punc) if punc.punctuation == '(') + { + let token_tree = self.parse_enclosed_list( + Delimiter::Parenthesis, + ',', + |parser| parser.parse_expression(handler).map(Box::new), + handler, + )?; - let operand = Box::new(self.parse_primary_condition(handler)?); - - Some(PrimaryCondition::Prefix(ConditionalPrefix { - operator, - operand, - })) + Some(Primary::FunctionCall(FunctionCall { + identifier, + left_parenthesis: token_tree.open, + right_parenthesis: token_tree.close, + arguments: token_tree.list, + })) + } else { + // insert parser for regular identifier here + None + } } - // string literal - Reading::Atomic(Token::StringLiteral(literal)) => { - self.forward(); - Some(PrimaryCondition::StringLiteral(literal)) - } - - // parenthesized condition - Reading::IntoDelimited(punc) if punc.punctuation == '(' => self - .parse_parenthesized_condition(handler) - .map(PrimaryCondition::Parenthesized), - unexpected => { // make progress self.forward(); handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { - expected: SyntaxKind::Expression, + expected: crate::syntax::error::SyntaxKind::Expression, found: unexpected.into_token(), })); @@ -340,39 +141,4 @@ impl<'a> Parser<'a> { } } } - - /// Parses a [`ParenthesizedCondition`]. - pub fn parse_parenthesized_condition( - &mut self, - handler: &impl Handler, - ) -> Option { - let token_tree = self.step_into( - Delimiter::Parenthesis, - |parser| parser.parse_condition(handler), - handler, - )?; - - Some(ParenthesizedCondition { - open_paren: token_tree.open, - condition: Box::new(token_tree.tree?), - close_paren: token_tree.close, - }) - } - - fn try_parse_conditional_binary_operator(&mut self) -> Option { - self.try_parse(|parser| match parser.next_significant_token() { - Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation { - '&' => { - let b = parser.parse_punctuation('&', false, &Dummy)?; - Some(ConditionalBinaryOperator::LogicalAnd(punc, b)) - } - '|' => { - let b = parser.parse_punctuation('|', false, &Dummy)?; - Some(ConditionalBinaryOperator::LogicalOr(punc, b)) - } - _ => None, - }, - _ => None, - }) - } } diff --git a/src/syntax/syntax_tree/mod.rs b/src/syntax/syntax_tree/mod.rs index 4891a42..31969ef 100644 --- a/src/syntax/syntax_tree/mod.rs +++ b/src/syntax/syntax_tree/mod.rs @@ -16,6 +16,7 @@ use crate::{ use super::{error::Error, parser::Parser}; +pub mod condition; pub mod declaration; pub mod expression; pub mod program; diff --git a/src/syntax/syntax_tree/statement.rs b/src/syntax/syntax_tree/statement.rs index 179e51f..18b308b 100644 --- a/src/syntax/syntax_tree/statement.rs +++ b/src/syntax/syntax_tree/statement.rs @@ -12,12 +12,12 @@ use crate::{ token_stream::Delimiter, }, syntax::{ - error::{Error, SyntaxKind, UnexpectedSyntax}, + error::Error, parser::{Parser, Reading}, }, }; -use super::expression::ParenthesizedCondition; +use super::{condition::ParenthesizedCondition, expression::Expression}; /// Syntax Synopsis: /// @@ -38,6 +38,7 @@ pub enum Statement { Conditional(Conditional), Grouping(Grouping), DocComment(DocComment), + Semicolon(Semicolon), } impl SourceElement for Statement { @@ -48,6 +49,7 @@ impl SourceElement for Statement { Self::Conditional(conditional) => conditional.span(), Self::Grouping(grouping) => grouping.span(), Self::DocComment(doc_comment) => doc_comment.span(), + Self::Semicolon(semi) => semi.span(), } } } @@ -139,6 +141,37 @@ impl SourceElement for Conditional { } } +/// Syntax Synopsis: +/// +/// ``` ebnf +/// Else: +/// 'else' Block +/// ; +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct Else { + /// The `else` keyword. + #[get = "pub"] + else_keyword: Keyword, + /// The block of the else statement. + #[get = "pub"] + block: Box, +} + +impl Else { + /// Dissolves the [`Else`] into its components. + #[must_use] + pub fn dissolve(self) -> (Keyword, Box) { + (self.else_keyword, self.block) + } +} + +impl SourceElement for Else { + fn span(&self) -> Span { + self.else_keyword.span().join(&self.block.span()).unwrap() + } +} + /// Syntax Synopsis: /// /// ``` ebnf @@ -174,33 +207,35 @@ impl SourceElement for Grouping { } /// Syntax Synopsis: -/// /// ``` ebnf -/// Else: -/// 'else' Block -/// ; +/// Semicolon: +/// Expression ';' +/// ; /// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] -pub struct Else { - /// The `else` keyword. +pub struct Semicolon { + /// The expression of the semicolon statement. #[get = "pub"] - else_keyword: Keyword, - /// The block of the else statement. + expression: Expression, + /// The semicolon of the semicolon statement. #[get = "pub"] - block: Box, + semicolon: Punctuation, } -impl Else { - /// Dissolves the [`Else`] into its components. - #[must_use] - pub fn dissolve(self) -> (Keyword, Box) { - (self.else_keyword, self.block) +impl SourceElement for Semicolon { + fn span(&self) -> Span { + self.expression + .span() + .join(&self.semicolon.span()) + .expect("The span of the semicolon statement is invalid.") } } -impl SourceElement for Else { - fn span(&self) -> Span { - self.else_keyword.span().join(&self.block.span()).unwrap() +impl Semicolon { + /// Dissolves the [`Semicolon`] into its components. + #[must_use] + pub fn dissolve(self) -> (Expression, Punctuation) { + (self.expression, self.semicolon) } } @@ -320,14 +355,15 @@ impl<'a> Parser<'a> { })) } - // other - unexpected => { - handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { - expected: SyntaxKind::Statement, - found: unexpected.into_token(), - })); + // semicolon statement + _ => { + let expression = self.parse_expression(handler)?; + let semicolon = self.parse_punctuation(';', true, handler)?; - None + Some(Statement::Semicolon(Semicolon { + expression, + semicolon, + })) } } } diff --git a/src/transpile/conversions.rs b/src/transpile/conversions.rs index 2360e0f..3ead89d 100644 --- a/src/transpile/conversions.rs +++ b/src/transpile/conversions.rs @@ -2,7 +2,7 @@ use shulkerbox::datapack::Condition as DpCondition; -use crate::syntax::syntax_tree::expression::{ +use crate::syntax::syntax_tree::condition::{ BinaryCondition, Condition, ConditionalBinaryOperator, ConditionalPrefixOperator, PrimaryCondition, }; diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index 9d8ed0b..baa5df5 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -1,22 +1,27 @@ //! Compiler for `ShulkerScript` -use std::collections::HashMap; +use std::{collections::HashMap, sync::RwLock}; use shulkerbox::datapack::{self, Command, Datapack, Execute}; use crate::{ base::{source_file::SourceElement, Handler}, - syntax::syntax_tree::{declaration::Declaration, program::Program, statement::Statement}, + syntax::syntax_tree::{ + declaration::Declaration, + expression::{Expression, Primary}, + program::Program, + statement::{Conditional, Statement}, + }, }; use super::error::{self, TranspileError}; /// A transpiler for `ShulkerScript`. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Transpiler { datapack: shulkerbox::datapack::Datapack, - functions: HashMap, - function_locations: HashMap, + functions: RwLock>, + function_locations: RwLock>, } #[derive(Debug, Clone)] @@ -32,8 +37,8 @@ impl Transpiler { pub fn new(pack_name: &str, pack_format: u8) -> Self { Self { datapack: shulkerbox::datapack::Datapack::new(pack_name, pack_format), - functions: HashMap::new(), - function_locations: HashMap::new(), + functions: RwLock::new(HashMap::new()), + function_locations: RwLock::new(HashMap::new()), } } @@ -82,7 +87,7 @@ impl Transpiler { ) }) .collect(); - self.functions.insert( + self.functions.write().unwrap().insert( name, FunctionData { namespace: "shulkerscript".to_string(), @@ -95,11 +100,23 @@ impl Transpiler { } /// Gets the function at the given path, or transpiles it if it hasn't been transpiled yet. - fn get_or_transpile_function(&mut self, path: &str) -> Option<&str> { - let already_transpiled = self.function_locations.get(path); - if already_transpiled.is_none() { - let function_data = self.functions.get(path)?; - let commands = compile_function(&function_data.statements); + /// Returns the location of the function or None if the function does not exist. + #[allow(clippy::significant_drop_tightening)] + fn get_or_transpile_function(&mut self, path: &str) -> Option { + let already_transpiled = { + let locations = self.function_locations.read().unwrap(); + locations.get(path).is_some() + }; + if !already_transpiled { + let statements = { + let functions = self.functions.read().unwrap(); + let function_data = functions.get(path)?; + function_data.statements.clone() + }; + let commands = self.transpile_function(&statements); + + let functions = self.functions.read().unwrap(); + let function_data = functions.get(path)?; let function = self .datapack @@ -118,86 +135,114 @@ impl Transpiler { } self.function_locations + .write() + .unwrap() .insert(path.to_string(), function_location); } - self.function_locations.get(path).map(String::as_str) + let locations = self.function_locations.read().unwrap(); + locations.get(path).map(String::to_owned) } -} -fn compile_function(statements: &[Statement]) -> Vec { - let mut commands = Vec::new(); - for statement in statements { - commands.extend(compile_statement(statement)); - } - commands -} - -fn compile_statement(statement: &Statement) -> Option { - match statement { - Statement::LiteralCommand(literal_command) => Some(literal_command.clean_command().into()), - Statement::Block(_) => { - unreachable!("Only literal commands are allowed in functions at this time.") + fn transpile_function(&mut self, statements: &[Statement]) -> Vec { + let mut commands = Vec::new(); + for statement in statements { + commands.extend(self.transpile_statement(statement)); } - Statement::Conditional(cond) => { - let (_, cond, block, el) = cond.clone().dissolve(); - let (_, cond, _) = cond.dissolve(); - let statements = block.statements(); + commands + } - let el = el - .and_then(|el| { - let (_, block) = el.dissolve(); - let statements = block.statements(); - if statements.is_empty() { - None - } else if statements.len() == 1 { - compile_statement(&statements[0]).map(|cmd| Execute::Run(Box::new(cmd))) - } else { - let commands = statements.iter().filter_map(compile_statement).collect(); - Some(Execute::Runs(commands)) - } - }) - .map(Box::new); - - if statements.is_empty() { - if el.is_none() { + fn transpile_statement(&mut self, statement: &Statement) -> Option { + match statement { + Statement::LiteralCommand(literal_command) => { + Some(literal_command.clean_command().into()) + } + Statement::Block(_) => { + unreachable!("Only literal commands are allowed in functions at this time.") + } + Statement::Conditional(cond) => self.transpile_conditional(cond), + Statement::DocComment(doccomment) => { + let content = doccomment.content(); + Some(Command::Comment(content.to_string())) + } + Statement::Grouping(group) => { + let statements = group.block().statements(); + let commands = statements + .iter() + .filter_map(|statement| self.transpile_statement(statement)) + .collect::>(); + if commands.is_empty() { None } else { - Some(Command::Execute(Execute::If( - datapack::Condition::from(cond), - Box::new(Execute::Runs(Vec::new())), - el, - ))) + Some(Command::Group(commands)) } - } else { - let run = if statements.len() > 1 { - let commands = statements.iter().filter_map(compile_statement).collect(); - Execute::Runs(commands) - } else { - Execute::Run(Box::new(compile_statement(&statements[0])?)) - }; + } + Statement::Semicolon(semi) => match semi.expression() { + Expression::Primary(primary) => self.transpile_primary_expression(primary), + }, + } + } + fn transpile_conditional(&mut self, cond: &Conditional) -> Option { + let (_, cond, block, el) = cond.clone().dissolve(); + let (_, cond, _) = cond.dissolve(); + let statements = block.statements(); + + let el = el + .and_then(|el| { + let (_, block) = el.dissolve(); + let statements = block.statements(); + if statements.is_empty() { + None + } else if statements.len() == 1 { + self.transpile_statement(&statements[0]) + .map(|cmd| Execute::Run(Box::new(cmd))) + } else { + let commands = statements + .iter() + .filter_map(|statement| self.transpile_statement(statement)) + .collect(); + Some(Execute::Runs(commands)) + } + }) + .map(Box::new); + + if statements.is_empty() { + if el.is_none() { + None + } else { Some(Command::Execute(Execute::If( datapack::Condition::from(cond), - Box::new(run), + Box::new(Execute::Runs(Vec::new())), el, ))) } - } - Statement::DocComment(doccomment) => { - let content = doccomment.content(); - Some(Command::Comment(content.to_string())) - } - Statement::Grouping(group) => { - let statements = group.block().statements(); - let commands = statements - .iter() - .filter_map(compile_statement) - .collect::>(); - if commands.is_empty() { - None + } else { + let run = if statements.len() > 1 { + let commands = statements + .iter() + .filter_map(|statement| self.transpile_statement(statement)) + .collect(); + Execute::Runs(commands) } else { - Some(Command::Group(commands)) + Execute::Run(Box::new(self.transpile_statement(&statements[0])?)) + }; + + Some(Command::Execute(Execute::If( + datapack::Condition::from(cond), + Box::new(run), + el, + ))) + } + } + + fn transpile_primary_expression(&mut self, primary: &Primary) -> Option { + match primary { + Primary::FunctionCall(func) => { + let identifier = func.identifier().span(); + let identifier = identifier.str(); + let location = self.get_or_transpile_function(identifier)?; + Some(Command::Raw(format!("function {location}"))) } } }