implement function calls

This commit is contained in:
Moritz Hölting 2024-04-05 16:16:12 +02:00
parent 2ed6e56ef1
commit 62dd12cab3
7 changed files with 672 additions and 418 deletions

View File

@ -33,6 +33,8 @@ Statement:
Block Block
| LiteralCommand | LiteralCommand
| Conditional | Conditional
| Grouping
| DocComment
; ;
``` ```
@ -59,4 +61,30 @@ ParenthizedCondition:
```ebnf ```ebnf
Condition: Condition:
StringLiteral StringLiteral
```
### Grouping
``` ebnf
Grouping:
'group' Block
;
```
### Expression
```ebnf
Expression:
Primary
```
### Primary
``` ebnf
Primary:
FunctionCall
```
### FunctionCall
``` ebnf
FunctionCall:
Identifier '(' (Expression (',' Expression)*)? ')'
;
``` ```

View File

@ -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<Condition>,
/// The operator of the binary condition.
#[get = "pub"]
operator: ConditionalBinaryOperator,
/// The right operand of the binary condition.
#[get = "pub"]
right_operand: Box<Condition>,
}
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<Condition>,
/// 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<PrimaryCondition>,
}
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<Error>) -> Option<Condition> {
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(&current_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<Error>,
) -> Option<PrimaryCondition> {
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<Error>,
) -> Option<ParenthesizedCondition> {
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<ConditionalBinaryOperator> {
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,
})
}
}

View File

@ -1,338 +1,139 @@
//! Syntax tree nodes for expressions. //! Syntax tree nodes for expressions.
use std::{cmp::Ordering, collections::VecDeque};
use enum_as_inner::EnumAsInner; use enum_as_inner::EnumAsInner;
use getset::Getters; use getset::Getters;
use crate::{ use crate::{
base::{ base::{
source_file::{SourceElement, Span}, source_file::{SourceElement, Span},
Dummy, Handler, Handler,
}, },
lexical::{ lexical::{
token::{Punctuation, StringLiteral, Token}, token::{Identifier, Punctuation, Token},
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
error::{Error, SyntaxKind, UnexpectedSyntax}, error::{Error, UnexpectedSyntax},
parser::{Parser, Reading}, parser::{Parser, Reading},
}, },
}; };
/// Syntax Synopsis: use super::ConnectedList;
///
/// ``` 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<Condition>,
/// The operator of the binary condition.
#[get = "pub"]
operator: ConditionalBinaryOperator,
/// The right operand of the binary condition.
#[get = "pub"]
right_operand: Box<Condition>,
}
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<Condition>,
/// 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: /// Syntax Synopsis:
/// ///
/// ```ebnf /// ```ebnf
/// Prefix: /// Expression:
/// ConditionalPrefixOperator StringLiteral /// Primary
/// ;
/// ``` /// ```
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
pub struct ConditionalPrefix { #[allow(missing_docs)]
/// The operator of the prefix. pub enum Expression {
#[get = "pub"] Primary(Primary),
operator: ConditionalPrefixOperator,
/// The operand of the prefix.
#[get = "pub"]
operand: Box<PrimaryCondition>,
} }
impl SourceElement for ConditionalPrefix { impl SourceElement for Expression {
fn span(&self) -> Span { fn span(&self) -> Span {
self.operator.span().join(&self.operand.span()).unwrap() match self {
} Self::Primary(primary) => primary.span(),
} }
impl ConditionalPrefix {
/// Dissolves the conditional prefix into its components
#[must_use]
pub fn dissolve(self) -> (ConditionalPrefixOperator, PrimaryCondition) {
(self.operator, *self.operand)
} }
} }
/// Syntax Synopsis: /// Syntax Synopsis:
/// ///
/// ``` ebnf /// ``` ebnf
/// Condition: PrimaryCondition; /// Primary:
/// FunctionCall
/// ``` /// ```
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
pub enum Condition { #[allow(missing_docs)]
Primary(PrimaryCondition), pub enum Primary {
Binary(BinaryCondition), FunctionCall(FunctionCall),
} }
impl SourceElement for Condition { impl SourceElement for Primary {
fn span(&self) -> Span { fn span(&self) -> Span {
match self { match self {
Self::Primary(primary) => primary.span(), Self::FunctionCall(function_call) => function_call.span(),
Self::Binary(binary) => binary.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<ConnectedList<Box<Expression>, 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> { impl<'a> Parser<'a> {
/// Parses a [`Condition`]. /// Parses an [`Expression`]
pub fn parse_condition(&mut self, handler: &impl Handler<Error>) -> Option<Condition> { pub fn parse_expression(&mut self, handler: &impl Handler<Error>) -> Option<Expression> {
let mut lhs = Condition::Primary(self.parse_primary_condition(handler)?); Some(Expression::Primary(self.parse_primary(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(&current_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`]. /// Parses an [`Primary`]
pub fn parse_primary_condition( pub fn parse_primary(&mut self, handler: &impl Handler<Error>) -> Option<Primary> {
&mut self,
handler: &impl Handler<Error>,
) -> Option<PrimaryCondition> {
match self.stop_at_significant() { match self.stop_at_significant() {
// prefixed expression // identifier expression
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '!' => { Reading::Atomic(Token::Identifier(identifier)) => {
// eat prefix operator // eat the identifier
self.forward(); self.forward();
let operator = match punc.punctuation { // function call
'!' => ConditionalPrefixOperator::LogicalNot(punc), if matches!(self.stop_at_significant(), Reading::IntoDelimited(punc) if punc.punctuation == '(')
_ => unreachable!(), {
}; 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(Primary::FunctionCall(FunctionCall {
identifier,
Some(PrimaryCondition::Prefix(ConditionalPrefix { left_parenthesis: token_tree.open,
operator, right_parenthesis: token_tree.close,
operand, 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 => { unexpected => {
// make progress // make progress
self.forward(); self.forward();
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
expected: SyntaxKind::Expression, expected: crate::syntax::error::SyntaxKind::Expression,
found: unexpected.into_token(), 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<Error>,
) -> Option<ParenthesizedCondition> {
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<ConditionalBinaryOperator> {
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,
})
}
} }

View File

@ -16,6 +16,7 @@ use crate::{
use super::{error::Error, parser::Parser}; use super::{error::Error, parser::Parser};
pub mod condition;
pub mod declaration; pub mod declaration;
pub mod expression; pub mod expression;
pub mod program; pub mod program;

View File

@ -12,12 +12,12 @@ use crate::{
token_stream::Delimiter, token_stream::Delimiter,
}, },
syntax::{ syntax::{
error::{Error, SyntaxKind, UnexpectedSyntax}, error::Error,
parser::{Parser, Reading}, parser::{Parser, Reading},
}, },
}; };
use super::expression::ParenthesizedCondition; use super::{condition::ParenthesizedCondition, expression::Expression};
/// Syntax Synopsis: /// Syntax Synopsis:
/// ///
@ -38,6 +38,7 @@ pub enum Statement {
Conditional(Conditional), Conditional(Conditional),
Grouping(Grouping), Grouping(Grouping),
DocComment(DocComment), DocComment(DocComment),
Semicolon(Semicolon),
} }
impl SourceElement for Statement { impl SourceElement for Statement {
@ -48,6 +49,7 @@ impl SourceElement for Statement {
Self::Conditional(conditional) => conditional.span(), Self::Conditional(conditional) => conditional.span(),
Self::Grouping(grouping) => grouping.span(), Self::Grouping(grouping) => grouping.span(),
Self::DocComment(doc_comment) => doc_comment.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<Block>,
}
impl Else {
/// Dissolves the [`Else`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Box<Block>) {
(self.else_keyword, self.block)
}
}
impl SourceElement for Else {
fn span(&self) -> Span {
self.else_keyword.span().join(&self.block.span()).unwrap()
}
}
/// Syntax Synopsis: /// Syntax Synopsis:
/// ///
/// ``` ebnf /// ``` ebnf
@ -174,33 +207,35 @@ impl SourceElement for Grouping {
} }
/// Syntax Synopsis: /// Syntax Synopsis:
///
/// ``` ebnf /// ``` ebnf
/// Else: /// Semicolon:
/// 'else' Block /// Expression ';'
/// ; /// ;
/// ``` /// ```
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Else { pub struct Semicolon {
/// The `else` keyword. /// The expression of the semicolon statement.
#[get = "pub"] #[get = "pub"]
else_keyword: Keyword, expression: Expression,
/// The block of the else statement. /// The semicolon of the semicolon statement.
#[get = "pub"] #[get = "pub"]
block: Box<Block>, semicolon: Punctuation,
} }
impl Else { impl SourceElement for Semicolon {
/// Dissolves the [`Else`] into its components. fn span(&self) -> Span {
#[must_use] self.expression
pub fn dissolve(self) -> (Keyword, Box<Block>) { .span()
(self.else_keyword, self.block) .join(&self.semicolon.span())
.expect("The span of the semicolon statement is invalid.")
} }
} }
impl SourceElement for Else { impl Semicolon {
fn span(&self) -> Span { /// Dissolves the [`Semicolon`] into its components.
self.else_keyword.span().join(&self.block.span()).unwrap() #[must_use]
pub fn dissolve(self) -> (Expression, Punctuation) {
(self.expression, self.semicolon)
} }
} }
@ -320,14 +355,15 @@ impl<'a> Parser<'a> {
})) }))
} }
// other // semicolon statement
unexpected => { _ => {
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax { let expression = self.parse_expression(handler)?;
expected: SyntaxKind::Statement, let semicolon = self.parse_punctuation(';', true, handler)?;
found: unexpected.into_token(),
}));
None Some(Statement::Semicolon(Semicolon {
expression,
semicolon,
}))
} }
} }
} }

View File

@ -2,7 +2,7 @@
use shulkerbox::datapack::Condition as DpCondition; use shulkerbox::datapack::Condition as DpCondition;
use crate::syntax::syntax_tree::expression::{ use crate::syntax::syntax_tree::condition::{
BinaryCondition, Condition, ConditionalBinaryOperator, ConditionalPrefixOperator, BinaryCondition, Condition, ConditionalBinaryOperator, ConditionalPrefixOperator,
PrimaryCondition, PrimaryCondition,
}; };

View File

@ -1,22 +1,27 @@
//! Compiler for `ShulkerScript` //! Compiler for `ShulkerScript`
use std::collections::HashMap; use std::{collections::HashMap, sync::RwLock};
use shulkerbox::datapack::{self, Command, Datapack, Execute}; use shulkerbox::datapack::{self, Command, Datapack, Execute};
use crate::{ use crate::{
base::{source_file::SourceElement, Handler}, 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}; use super::error::{self, TranspileError};
/// A transpiler for `ShulkerScript`. /// A transpiler for `ShulkerScript`.
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct Transpiler { pub struct Transpiler {
datapack: shulkerbox::datapack::Datapack, datapack: shulkerbox::datapack::Datapack,
functions: HashMap<String, FunctionData>, functions: RwLock<HashMap<String, FunctionData>>,
function_locations: HashMap<String, String>, function_locations: RwLock<HashMap<String, String>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -32,8 +37,8 @@ impl Transpiler {
pub fn new(pack_name: &str, pack_format: u8) -> Self { pub fn new(pack_name: &str, pack_format: u8) -> Self {
Self { Self {
datapack: shulkerbox::datapack::Datapack::new(pack_name, pack_format), datapack: shulkerbox::datapack::Datapack::new(pack_name, pack_format),
functions: HashMap::new(), functions: RwLock::new(HashMap::new()),
function_locations: HashMap::new(), function_locations: RwLock::new(HashMap::new()),
} }
} }
@ -82,7 +87,7 @@ impl Transpiler {
) )
}) })
.collect(); .collect();
self.functions.insert( self.functions.write().unwrap().insert(
name, name,
FunctionData { FunctionData {
namespace: "shulkerscript".to_string(), 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. /// 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> { /// Returns the location of the function or None if the function does not exist.
let already_transpiled = self.function_locations.get(path); #[allow(clippy::significant_drop_tightening)]
if already_transpiled.is_none() { fn get_or_transpile_function(&mut self, path: &str) -> Option<String> {
let function_data = self.functions.get(path)?; let already_transpiled = {
let commands = compile_function(&function_data.statements); 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 let function = self
.datapack .datapack
@ -118,86 +135,114 @@ impl Transpiler {
} }
self.function_locations self.function_locations
.write()
.unwrap()
.insert(path.to_string(), function_location); .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<Command> { fn transpile_function(&mut self, statements: &[Statement]) -> Vec<Command> {
let mut commands = Vec::new(); let mut commands = Vec::new();
for statement in statements { for statement in statements {
commands.extend(compile_statement(statement)); commands.extend(self.transpile_statement(statement));
}
commands
}
fn compile_statement(statement: &Statement) -> Option<Command> {
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) => { commands
let (_, cond, block, el) = cond.clone().dissolve(); }
let (_, cond, _) = cond.dissolve();
let statements = block.statements();
let el = el fn transpile_statement(&mut self, statement: &Statement) -> Option<Command> {
.and_then(|el| { match statement {
let (_, block) = el.dissolve(); Statement::LiteralCommand(literal_command) => {
let statements = block.statements(); Some(literal_command.clean_command().into())
if statements.is_empty() { }
None Statement::Block(_) => {
} else if statements.len() == 1 { unreachable!("Only literal commands are allowed in functions at this time.")
compile_statement(&statements[0]).map(|cmd| Execute::Run(Box::new(cmd))) }
} else { Statement::Conditional(cond) => self.transpile_conditional(cond),
let commands = statements.iter().filter_map(compile_statement).collect(); Statement::DocComment(doccomment) => {
Some(Execute::Runs(commands)) let content = doccomment.content();
} Some(Command::Comment(content.to_string()))
}) }
.map(Box::new); Statement::Grouping(group) => {
let statements = group.block().statements();
if statements.is_empty() { let commands = statements
if el.is_none() { .iter()
.filter_map(|statement| self.transpile_statement(statement))
.collect::<Vec<_>>();
if commands.is_empty() {
None None
} else { } else {
Some(Command::Execute(Execute::If( Some(Command::Group(commands))
datapack::Condition::from(cond),
Box::new(Execute::Runs(Vec::new())),
el,
)))
} }
} else { }
let run = if statements.len() > 1 { Statement::Semicolon(semi) => match semi.expression() {
let commands = statements.iter().filter_map(compile_statement).collect(); Expression::Primary(primary) => self.transpile_primary_expression(primary),
Execute::Runs(commands) },
} else { }
Execute::Run(Box::new(compile_statement(&statements[0])?)) }
};
fn transpile_conditional(&mut self, cond: &Conditional) -> Option<Command> {
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( Some(Command::Execute(Execute::If(
datapack::Condition::from(cond), datapack::Condition::from(cond),
Box::new(run), Box::new(Execute::Runs(Vec::new())),
el, el,
))) )))
} }
} } else {
Statement::DocComment(doccomment) => { let run = if statements.len() > 1 {
let content = doccomment.content(); let commands = statements
Some(Command::Comment(content.to_string())) .iter()
} .filter_map(|statement| self.transpile_statement(statement))
Statement::Grouping(group) => { .collect();
let statements = group.block().statements(); Execute::Runs(commands)
let commands = statements
.iter()
.filter_map(compile_statement)
.collect::<Vec<_>>();
if commands.is_empty() {
None
} else { } 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<Command> {
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}")))
} }
} }
} }