implement function calls
This commit is contained in:
parent
2ed6e56ef1
commit
62dd12cab3
28
grammar.md
28
grammar.md
|
@ -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)*)? ')'
|
||||||
|
;
|
||||||
```
|
```
|
|
@ -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(¤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<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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(¤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`].
|
/// 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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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}")))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue