Implement execute blocks
- move conditionals to execute blocks - implement "as" in execute blocks
This commit is contained in:
parent
30bfdaf0a6
commit
a818325ce9
|
@ -6,8 +6,8 @@ pub trait Handler<T> {
|
||||||
|
|
||||||
/// Is a struct that implements [`Handler`] trait by doing nothing with the errors.
|
/// Is a struct that implements [`Handler`] trait by doing nothing with the errors.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
pub struct Dummy;
|
pub struct DummyHandler;
|
||||||
|
|
||||||
impl<T> Handler<T> for Dummy {
|
impl<T> Handler<T> for DummyHandler {
|
||||||
fn receive(&self, _error: T) {}
|
fn receive(&self, _error: T) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,6 @@ mod error;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
|
||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
pub use diagnostic::{Dummy, Handler};
|
pub use diagnostic::{DummyHandler, Handler};
|
||||||
|
|
||||||
pub mod log;
|
pub mod log;
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub enum KeywordKind {
|
||||||
Function,
|
Function,
|
||||||
If,
|
If,
|
||||||
Else,
|
Else,
|
||||||
|
As,
|
||||||
Group,
|
Group,
|
||||||
Run,
|
Run,
|
||||||
Lua,
|
Lua,
|
||||||
|
@ -66,12 +67,19 @@ impl KeywordKind {
|
||||||
Self::Function => "fn",
|
Self::Function => "fn",
|
||||||
Self::If => "if",
|
Self::If => "if",
|
||||||
Self::Else => "else",
|
Self::Else => "else",
|
||||||
|
Self::As => "as",
|
||||||
Self::Group => "group",
|
Self::Group => "group",
|
||||||
Self::Run => "run",
|
Self::Run => "run",
|
||||||
Self::Lua => "lua",
|
Self::Lua => "lua",
|
||||||
Self::Namespace => "namespace",
|
Self::Namespace => "namespace",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the keyword starts an execute block.
|
||||||
|
#[must_use]
|
||||||
|
pub fn starts_execute_block(&self) -> bool {
|
||||||
|
matches!(self, Self::If | Self::As)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is an enumeration containing all kinds of tokens in the Shulkerscript programming language.
|
/// Is an enumeration containing all kinds of tokens in the Shulkerscript programming language.
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub enum SyntaxKind {
|
||||||
Statement,
|
Statement,
|
||||||
Expression,
|
Expression,
|
||||||
Type,
|
Type,
|
||||||
|
ExecuteBlock,
|
||||||
|
ExecuteBlockTail,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A syntax/token is expected but found an other invalid token.
|
/// A syntax/token is expected but found an other invalid token.
|
||||||
|
@ -44,6 +46,8 @@ impl Display for UnexpectedSyntax {
|
||||||
SyntaxKind::Statement => "a statement syntax".to_string(),
|
SyntaxKind::Statement => "a statement syntax".to_string(),
|
||||||
SyntaxKind::Expression => "an expression syntax".to_string(),
|
SyntaxKind::Expression => "an expression syntax".to_string(),
|
||||||
SyntaxKind::Type => "a type syntax".to_string(),
|
SyntaxKind::Type => "a type syntax".to_string(),
|
||||||
|
SyntaxKind::ExecuteBlock => "an execute block syntax".to_string(),
|
||||||
|
SyntaxKind::ExecuteBlockTail => "an execute block tail syntax".to_string(),
|
||||||
};
|
};
|
||||||
let found_binding = match self.found.clone() {
|
let found_binding = match self.found.clone() {
|
||||||
Some(Token::Comment(..)) => "a comment token".to_string(),
|
Some(Token::Comment(..)) => "a comment token".to_string(),
|
||||||
|
|
|
@ -6,7 +6,7 @@ use enum_as_inner::EnumAsInner;
|
||||||
use crate::{
|
use crate::{
|
||||||
base::Handler,
|
base::Handler,
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Identifier, Keyword, KeywordKind, Numeric, Punctuation, Token},
|
token::{Identifier, Keyword, KeywordKind, Numeric, Punctuation, StringLiteral, Token},
|
||||||
token_stream::{Delimited, Delimiter, TokenStream, TokenTree},
|
token_stream::{Delimited, Delimiter, TokenStream, TokenTree},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -388,7 +388,7 @@ impl<'a> Frame<'a> {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// If the next [`Token`] is not an [`Identifier`].
|
/// If the next [`Token`] is not an [`Identifier`].
|
||||||
pub fn parse_numeric(&mut self, handler: &dyn Handler<Error>) -> Option<Numeric> {
|
pub fn parse_numeric(&mut self, handler: &impl Handler<Error>) -> Option<Numeric> {
|
||||||
match self.next_significant_token() {
|
match self.next_significant_token() {
|
||||||
Reading::Atomic(Token::Numeric(ident)) => Some(ident),
|
Reading::Atomic(Token::Numeric(ident)) => Some(ident),
|
||||||
found => {
|
found => {
|
||||||
|
@ -401,6 +401,23 @@ impl<'a> Frame<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expects the next [`Token`] to be an [`StringLiteral`], and returns it.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// If the next [`Token`] is not an [`StringLiteral`].
|
||||||
|
pub fn parse_string_literal(&mut self, handler: &impl Handler<Error>) -> Option<StringLiteral> {
|
||||||
|
match self.next_significant_token() {
|
||||||
|
Reading::Atomic(Token::StringLiteral(literal)) => Some(literal),
|
||||||
|
found => {
|
||||||
|
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::StringLiteral,
|
||||||
|
found: found.into_token(),
|
||||||
|
}));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Expects the next [`Token`] to be a [`Keyword`] of specific kind, and returns it.
|
/// Expects the next [`Token`] to be a [`Keyword`] of specific kind, and returns it.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
@ -408,7 +425,7 @@ impl<'a> Frame<'a> {
|
||||||
pub fn parse_keyword(
|
pub fn parse_keyword(
|
||||||
&mut self,
|
&mut self,
|
||||||
expected: KeywordKind,
|
expected: KeywordKind,
|
||||||
handler: &dyn Handler<Error>,
|
handler: &impl Handler<Error>,
|
||||||
) -> Option<Keyword> {
|
) -> Option<Keyword> {
|
||||||
match self.next_significant_token() {
|
match self.next_significant_token() {
|
||||||
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
|
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
|
||||||
|
@ -432,7 +449,7 @@ impl<'a> Frame<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
expected: char,
|
expected: char,
|
||||||
skip_insignificant: bool,
|
skip_insignificant: bool,
|
||||||
handler: &dyn Handler<Error>,
|
handler: &impl Handler<Error>,
|
||||||
) -> Option<Punctuation> {
|
) -> Option<Punctuation> {
|
||||||
match if skip_insignificant {
|
match if skip_insignificant {
|
||||||
self.next_significant_token()
|
self.next_significant_token()
|
||||||
|
|
|
@ -8,7 +8,7 @@ use getset::Getters;
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
source_file::{SourceElement, Span},
|
source_file::{SourceElement, Span},
|
||||||
Dummy, Handler,
|
DummyHandler, Handler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{Punctuation, StringLiteral, Token},
|
token::{Punctuation, StringLiteral, Token},
|
||||||
|
@ -373,11 +373,11 @@ impl<'a> Parser<'a> {
|
||||||
self.try_parse(|parser| match parser.next_significant_token() {
|
self.try_parse(|parser| match parser.next_significant_token() {
|
||||||
Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation {
|
Reading::Atomic(Token::Punctuation(punc)) => match punc.punctuation {
|
||||||
'&' => {
|
'&' => {
|
||||||
let b = parser.parse_punctuation('&', false, &Dummy)?;
|
let b = parser.parse_punctuation('&', false, &DummyHandler)?;
|
||||||
Some(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
Some(ConditionalBinaryOperator::LogicalAnd(punc, b))
|
||||||
}
|
}
|
||||||
'|' => {
|
'|' => {
|
||||||
let b = parser.parse_punctuation('|', false, &Dummy)?;
|
let b = parser.parse_punctuation('|', false, &DummyHandler)?;
|
||||||
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
|
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -86,23 +86,8 @@ impl<'a> Parser<'a> {
|
||||||
// eat the keyword
|
// eat the keyword
|
||||||
self.forward();
|
self.forward();
|
||||||
|
|
||||||
let namespace_name = match self.stop_at_significant() {
|
let namespace_name = self
|
||||||
Reading::Atomic(Token::StringLiteral(name)) => {
|
.parse_string_literal(handler)
|
||||||
self.forward();
|
|
||||||
Some(name)
|
|
||||||
}
|
|
||||||
unexpected => {
|
|
||||||
self.forward();
|
|
||||||
handler.receive(
|
|
||||||
UnexpectedSyntax {
|
|
||||||
expected: SyntaxKind::StringLiteral,
|
|
||||||
found: unexpected.into_token(),
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.and_then(|name| Namespace::validate_str(name.str_content()).then_some(name))?;
|
.and_then(|name| Namespace::validate_str(name.str_content()).then_some(name))?;
|
||||||
|
|
||||||
let semicolon = self.parse_punctuation(';', true, handler)?;
|
let semicolon = self.parse_punctuation(';', true, handler)?;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
//! Syntax tree nodes for statements.
|
//! Syntax tree nodes for statements.
|
||||||
|
|
||||||
|
pub mod execute_block;
|
||||||
|
|
||||||
|
use derive_more::From;
|
||||||
use getset::Getters;
|
use getset::Getters;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -17,7 +20,9 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{condition::ParenthesizedCondition, expression::Expression};
|
use self::execute_block::ExecuteBlock;
|
||||||
|
|
||||||
|
use super::expression::Expression;
|
||||||
|
|
||||||
/// Syntax Synopsis:
|
/// Syntax Synopsis:
|
||||||
///
|
///
|
||||||
|
@ -34,11 +39,11 @@ use super::{condition::ParenthesizedCondition, expression::Expression};
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, From)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
Block(Block),
|
Block(Block),
|
||||||
LiteralCommand(CommandLiteral),
|
LiteralCommand(CommandLiteral),
|
||||||
Conditional(Conditional),
|
ExecuteBlock(ExecuteBlock),
|
||||||
Grouping(Grouping),
|
Grouping(Grouping),
|
||||||
DocComment(DocComment),
|
DocComment(DocComment),
|
||||||
Semicolon(Semicolon),
|
Semicolon(Semicolon),
|
||||||
|
@ -50,7 +55,7 @@ impl SourceElement for Statement {
|
||||||
match self {
|
match self {
|
||||||
Self::Block(block) => block.span(),
|
Self::Block(block) => block.span(),
|
||||||
Self::LiteralCommand(literal_command) => literal_command.span(),
|
Self::LiteralCommand(literal_command) => literal_command.span(),
|
||||||
Self::Conditional(conditional) => conditional.span(),
|
Self::ExecuteBlock(execute_block) => execute_block.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(),
|
Self::Semicolon(semi) => semi.span(),
|
||||||
|
@ -135,89 +140,6 @@ impl Run {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Syntax Synopsis:
|
|
||||||
///
|
|
||||||
/// ``` ebnf
|
|
||||||
/// Conditional:
|
|
||||||
/// 'if' ParenthizedCondition Block ('else' Block)?
|
|
||||||
/// ;
|
|
||||||
/// ```
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
|
||||||
pub struct Conditional {
|
|
||||||
/// The `if` keyword.
|
|
||||||
#[get = "pub"]
|
|
||||||
if_keyword: Keyword,
|
|
||||||
/// The condition of the conditional.
|
|
||||||
#[get = "pub"]
|
|
||||||
condition: ParenthesizedCondition,
|
|
||||||
/// The block of the conditional.
|
|
||||||
#[get = "pub"]
|
|
||||||
block: Block,
|
|
||||||
/// The `else` statement.
|
|
||||||
#[get = "pub"]
|
|
||||||
r#else: Option<Else>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Conditional {
|
|
||||||
/// Dissolves the [`Conditional`] into its components.
|
|
||||||
#[must_use]
|
|
||||||
pub fn dissolve(self) -> (Keyword, ParenthesizedCondition, Block, Option<Else>) {
|
|
||||||
(self.if_keyword, self.condition, self.block, self.r#else)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SourceElement for Conditional {
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
self.r#else.as_ref().map_or_else(
|
|
||||||
|| {
|
|
||||||
self.if_keyword
|
|
||||||
.span()
|
|
||||||
.join(&self.block.span())
|
|
||||||
.expect("The span of the conditional is invalid.")
|
|
||||||
},
|
|
||||||
|r#else| {
|
|
||||||
self.if_keyword
|
|
||||||
.span()
|
|
||||||
.join(&r#else.span())
|
|
||||||
.expect("The span of the else conditional is invalid.")
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Syntax Synopsis:
|
|
||||||
///
|
|
||||||
/// ``` ebnf
|
|
||||||
/// Else:
|
|
||||||
/// 'else' Block
|
|
||||||
/// ;
|
|
||||||
/// ```
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[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
|
||||||
|
@ -341,45 +263,12 @@ impl<'a> Parser<'a> {
|
||||||
Some(Statement::Block(block))
|
Some(Statement::Block(block))
|
||||||
}
|
}
|
||||||
|
|
||||||
// conditional statement
|
// execute block
|
||||||
Reading::Atomic(Token::Keyword(if_keyword))
|
Reading::Atomic(Token::Keyword(execute_keyword))
|
||||||
if if_keyword.keyword == KeywordKind::If =>
|
if execute_keyword.keyword.starts_execute_block() =>
|
||||||
{
|
{
|
||||||
// eat the if keyword
|
self.parse_execute_block_statement(handler)
|
||||||
self.forward();
|
.map(Statement::ExecuteBlock)
|
||||||
|
|
||||||
let condition = self.parse_parenthesized_condition(handler)?;
|
|
||||||
|
|
||||||
let block = self.parse_block(handler)?;
|
|
||||||
|
|
||||||
match self.stop_at_significant() {
|
|
||||||
// else statement
|
|
||||||
Reading::Atomic(Token::Keyword(else_keyword))
|
|
||||||
if else_keyword.keyword == KeywordKind::Else =>
|
|
||||||
{
|
|
||||||
// eat the else keyword
|
|
||||||
self.forward();
|
|
||||||
|
|
||||||
let else_block = self.parse_block(handler)?;
|
|
||||||
|
|
||||||
Some(Statement::Conditional(Conditional {
|
|
||||||
if_keyword,
|
|
||||||
condition,
|
|
||||||
block,
|
|
||||||
r#else: Some(Else {
|
|
||||||
else_keyword,
|
|
||||||
block: Box::new(else_block),
|
|
||||||
}),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
// no else statement
|
|
||||||
_ => Some(Statement::Conditional(Conditional {
|
|
||||||
if_keyword,
|
|
||||||
condition,
|
|
||||||
block,
|
|
||||||
r#else: None,
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// doc comment
|
// doc comment
|
||||||
|
|
|
@ -0,0 +1,341 @@
|
||||||
|
//! Execute block statement syntax tree.
|
||||||
|
|
||||||
|
use derive_more::From;
|
||||||
|
use enum_as_inner::EnumAsInner;
|
||||||
|
use getset::Getters;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
base::{
|
||||||
|
source_file::{SourceElement, Span},
|
||||||
|
DummyHandler, Handler,
|
||||||
|
},
|
||||||
|
lexical::{
|
||||||
|
token::{Keyword, KeywordKind, Punctuation, StringLiteral, Token},
|
||||||
|
token_stream::Delimiter,
|
||||||
|
},
|
||||||
|
syntax::{
|
||||||
|
error::{Error, SyntaxKind, UnexpectedSyntax},
|
||||||
|
parser::{Parser, Reading},
|
||||||
|
syntax_tree::condition::ParenthesizedCondition,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::Block;
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
/// ```ebnf
|
||||||
|
/// ExecuteBlock:
|
||||||
|
/// ExecuteBlockHead ExecuteBlockTail
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum ExecuteBlock {
|
||||||
|
HeadTail(ExecuteBlockHead, ExecuteBlockTail),
|
||||||
|
IfElse(Conditional, Block, Else),
|
||||||
|
}
|
||||||
|
impl SourceElement for ExecuteBlock {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::HeadTail(head, tail) => head.span().join(&tail.span()).unwrap(),
|
||||||
|
Self::IfElse(conditional, block, else_) => conditional
|
||||||
|
.span()
|
||||||
|
.join(&block.span())
|
||||||
|
.unwrap()
|
||||||
|
.join(&else_.span())
|
||||||
|
.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner, From)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum ExecuteBlockHead {
|
||||||
|
Conditional(Conditional),
|
||||||
|
As(As),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ExecuteBlockHead {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::Conditional(conditional) => conditional.span(),
|
||||||
|
Self::As(as_) => as_.span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner, From)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub enum ExecuteBlockTail {
|
||||||
|
ExecuteBlock(Punctuation, Box<ExecuteBlock>),
|
||||||
|
Block(Block),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for ExecuteBlockTail {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
Self::ExecuteBlock(punc, execute_block) => punc
|
||||||
|
.span
|
||||||
|
.join(&execute_block.span())
|
||||||
|
.expect("The span of the execute block tail is invalid."),
|
||||||
|
Self::Block(block) => block.span(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// Conditional:
|
||||||
|
/// 'if' ParenthizedCondition
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct Conditional {
|
||||||
|
/// The `if` keyword.
|
||||||
|
#[get = "pub"]
|
||||||
|
if_keyword: Keyword,
|
||||||
|
/// The condition of the conditional.
|
||||||
|
#[get = "pub"]
|
||||||
|
condition: ParenthesizedCondition,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Conditional {
|
||||||
|
/// Dissolves the [`Conditional`] into its components.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Keyword, ParenthesizedCondition) {
|
||||||
|
(self.if_keyword, self.condition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for Conditional {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.if_keyword
|
||||||
|
.span()
|
||||||
|
.join(&self.condition.span())
|
||||||
|
.expect("The span of the conditional is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Syntax Synopsis:
|
||||||
|
///
|
||||||
|
/// ``` ebnf
|
||||||
|
/// Else:
|
||||||
|
/// 'else' Block
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[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:
|
||||||
|
///
|
||||||
|
/// ```ebnf
|
||||||
|
/// As:
|
||||||
|
/// 'as' '(' StringLiteral ')'
|
||||||
|
/// ;
|
||||||
|
/// ```
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
|
||||||
|
pub struct As {
|
||||||
|
/// The `as` keyword.
|
||||||
|
#[get = "pub"]
|
||||||
|
as_keyword: Keyword,
|
||||||
|
/// The open parenthesis.
|
||||||
|
#[get = "pub"]
|
||||||
|
open_paren: Punctuation,
|
||||||
|
/// The selector of the as statement.
|
||||||
|
#[get = "pub"]
|
||||||
|
as_selector: StringLiteral,
|
||||||
|
/// The close parenthesis.
|
||||||
|
#[get = "pub"]
|
||||||
|
close_paren: Punctuation,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourceElement for As {
|
||||||
|
fn span(&self) -> Span {
|
||||||
|
self.as_keyword
|
||||||
|
.span()
|
||||||
|
.join(&self.close_paren.span())
|
||||||
|
.expect("The span of the as statement is invalid.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl As {
|
||||||
|
/// Dissolves the [`As`] into its components.
|
||||||
|
#[must_use]
|
||||||
|
pub fn dissolve(self) -> (Keyword, StringLiteral, Punctuation) {
|
||||||
|
(self.as_keyword, self.as_selector, self.close_paren)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Parser<'a> {
|
||||||
|
/// Parses an [`ExecuteBlock`].
|
||||||
|
pub fn parse_execute_block_statement(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<Error>,
|
||||||
|
) -> Option<ExecuteBlock> {
|
||||||
|
match self.stop_at_significant() {
|
||||||
|
Reading::Atomic(Token::Keyword(if_keyword))
|
||||||
|
if if_keyword.keyword == KeywordKind::If =>
|
||||||
|
{
|
||||||
|
// eat the if keyword
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
let condition = self.parse_parenthesized_condition(handler)?;
|
||||||
|
|
||||||
|
let conditional = Conditional {
|
||||||
|
if_keyword,
|
||||||
|
condition,
|
||||||
|
};
|
||||||
|
|
||||||
|
let else_tail = self.try_parse(|parser| {
|
||||||
|
let block = parser.parse_block(&DummyHandler)?;
|
||||||
|
let (else_keyword, else_block) = match parser.stop_at_significant() {
|
||||||
|
// else statement
|
||||||
|
Reading::Atomic(Token::Keyword(else_keyword))
|
||||||
|
if else_keyword.keyword == KeywordKind::Else =>
|
||||||
|
{
|
||||||
|
// eat the else keyword
|
||||||
|
parser.forward();
|
||||||
|
|
||||||
|
let else_block = parser.parse_block(handler)?;
|
||||||
|
|
||||||
|
Some((else_keyword, else_block))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Some((
|
||||||
|
block,
|
||||||
|
Else {
|
||||||
|
else_keyword,
|
||||||
|
block: Box::new(else_block),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some((block, else_tail)) = else_tail {
|
||||||
|
Some(ExecuteBlock::IfElse(conditional, block, else_tail))
|
||||||
|
} else {
|
||||||
|
let tail = self.parse_execute_block_tail(handler)?;
|
||||||
|
Some(ExecuteBlock::HeadTail(
|
||||||
|
ExecuteBlockHead::Conditional(conditional),
|
||||||
|
tail,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Reading::Atomic(Token::Keyword(as_keyword))
|
||||||
|
if as_keyword.keyword == KeywordKind::As =>
|
||||||
|
{
|
||||||
|
// eat the as keyword
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
let as_selector = match self.stop_at_significant() {
|
||||||
|
Reading::IntoDelimited(punc) if punc.punctuation == '(' => self.step_into(
|
||||||
|
Delimiter::Parenthesis,
|
||||||
|
|parser| parser.parse_string_literal(handler),
|
||||||
|
handler,
|
||||||
|
),
|
||||||
|
unexpected => {
|
||||||
|
handler.receive(
|
||||||
|
UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::Punctuation('('),
|
||||||
|
found: unexpected.into_token(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let tail = self.parse_execute_block_tail(handler)?;
|
||||||
|
|
||||||
|
Some(ExecuteBlock::HeadTail(
|
||||||
|
ExecuteBlockHead::As(As {
|
||||||
|
as_keyword,
|
||||||
|
open_paren: as_selector.open,
|
||||||
|
as_selector: as_selector.tree?,
|
||||||
|
close_paren: as_selector.close,
|
||||||
|
}),
|
||||||
|
tail,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// unexpected
|
||||||
|
unexpected => {
|
||||||
|
handler.receive(
|
||||||
|
UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::ExecuteBlock,
|
||||||
|
found: unexpected.into_token(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_execute_block_tail(
|
||||||
|
&mut self,
|
||||||
|
handler: &impl Handler<Error>,
|
||||||
|
) -> Option<ExecuteBlockTail> {
|
||||||
|
match self.stop_at_significant() {
|
||||||
|
// nested execute block
|
||||||
|
Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == ',' => {
|
||||||
|
// eat the comma
|
||||||
|
self.forward();
|
||||||
|
|
||||||
|
let execute_block = self.parse_execute_block_statement(handler)?;
|
||||||
|
|
||||||
|
Some(ExecuteBlockTail::ExecuteBlock(
|
||||||
|
punc,
|
||||||
|
Box::new(execute_block),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// end block
|
||||||
|
Reading::IntoDelimited(punc) if punc.punctuation == '{' => {
|
||||||
|
let block = self.parse_block(handler)?;
|
||||||
|
|
||||||
|
Some(ExecuteBlockTail::Block(block))
|
||||||
|
}
|
||||||
|
|
||||||
|
unexpected => {
|
||||||
|
handler.receive(
|
||||||
|
UnexpectedSyntax {
|
||||||
|
expected: SyntaxKind::ExecuteBlockTail,
|
||||||
|
found: unexpected.into_token(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,10 @@ use crate::{
|
||||||
declaration::Declaration,
|
declaration::Declaration,
|
||||||
expression::{Expression, FunctionCall, Primary},
|
expression::{Expression, FunctionCall, Primary},
|
||||||
program::{Namespace, ProgramFile},
|
program::{Namespace, ProgramFile},
|
||||||
statement::{Conditional, Statement},
|
statement::{
|
||||||
|
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
||||||
|
Statement,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -243,7 +246,7 @@ impl Transpiler {
|
||||||
Statement::Block(_) => {
|
Statement::Block(_) => {
|
||||||
unreachable!("Only literal commands are allowed in functions at this time.")
|
unreachable!("Only literal commands are allowed in functions at this time.")
|
||||||
}
|
}
|
||||||
Statement::Conditional(cond) => self.transpile_conditional(cond, handler),
|
Statement::ExecuteBlock(execute) => self.transpile_execute_block(execute, handler),
|
||||||
Statement::DocComment(doccomment) => {
|
Statement::DocComment(doccomment) => {
|
||||||
let content = doccomment.content();
|
let content = doccomment.content();
|
||||||
Ok(Some(Command::Comment(content.to_string())))
|
Ok(Some(Command::Comment(content.to_string())))
|
||||||
|
@ -295,19 +298,112 @@ impl Transpiler {
|
||||||
Ok(Command::Raw(format!("function {location}")))
|
Ok(Command::Raw(format!("function {location}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn transpile_execute_block(
|
||||||
|
&mut self,
|
||||||
|
execute: &ExecuteBlock,
|
||||||
|
handler: &impl Handler<TranspileError>,
|
||||||
|
) -> TranspileResult<Option<Command>> {
|
||||||
|
self.transpile_execute_block_internal(execute, handler)
|
||||||
|
.map(|ex| ex.map(Command::Execute))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transpile_execute_block_internal(
|
||||||
|
&mut self,
|
||||||
|
execute: &ExecuteBlock,
|
||||||
|
handler: &impl Handler<TranspileError>,
|
||||||
|
) -> TranspileResult<Option<Execute>> {
|
||||||
|
match execute {
|
||||||
|
ExecuteBlock::HeadTail(head, tail) => {
|
||||||
|
let tail = match tail {
|
||||||
|
ExecuteBlockTail::Block(block) => {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let commands = block
|
||||||
|
.statements()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| {
|
||||||
|
self.transpile_statement(s, handler).unwrap_or_else(|err| {
|
||||||
|
errors.push(err);
|
||||||
|
None
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors.remove(0));
|
||||||
|
}
|
||||||
|
if commands.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(Execute::Runs(commands)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecuteBlockTail::ExecuteBlock(_, execute_block) => {
|
||||||
|
self.transpile_execute_block_internal(execute_block, handler)
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
let combined = match head {
|
||||||
|
ExecuteBlockHead::Conditional(cond) => {
|
||||||
|
if let Some(tail) = tail {
|
||||||
|
self.transpile_conditional(cond, tail, None, handler)?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExecuteBlockHead::As(as_) => {
|
||||||
|
let selector = as_.as_selector().str_content();
|
||||||
|
tail.map(|tail| Execute::As(selector.to_string(), Box::new(tail)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(combined)
|
||||||
|
}
|
||||||
|
ExecuteBlock::IfElse(cond, block, el) => {
|
||||||
|
let statements = block.statements();
|
||||||
|
let then = if statements.is_empty() {
|
||||||
|
Some(Execute::Runs(Vec::new()))
|
||||||
|
} else if statements.len() > 1 {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
let commands = statements
|
||||||
|
.iter()
|
||||||
|
.filter_map(|statement| {
|
||||||
|
self.transpile_statement(statement, handler)
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
errors.push(err);
|
||||||
|
None
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return Err(errors.remove(0));
|
||||||
|
}
|
||||||
|
Some(Execute::Runs(commands))
|
||||||
|
} else {
|
||||||
|
self.transpile_statement(&statements[0], handler)?
|
||||||
|
.map(|cmd| Execute::Run(Box::new(cmd)))
|
||||||
|
};
|
||||||
|
|
||||||
|
then.map_or_else(
|
||||||
|
|| Ok(None),
|
||||||
|
|then| self.transpile_conditional(cond, then, Some(el), handler),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn transpile_conditional(
|
fn transpile_conditional(
|
||||||
&mut self,
|
&mut self,
|
||||||
cond: &Conditional,
|
cond: &Conditional,
|
||||||
|
then: Execute,
|
||||||
|
el: Option<&Else>,
|
||||||
handler: &impl Handler<TranspileError>,
|
handler: &impl Handler<TranspileError>,
|
||||||
) -> TranspileResult<Option<Command>> {
|
) -> TranspileResult<Option<Execute>> {
|
||||||
let (_, cond, block, el) = cond.clone().dissolve();
|
let (_, cond) = cond.clone().dissolve();
|
||||||
let (_, cond, _) = cond.dissolve();
|
let (_, cond, _) = cond.dissolve();
|
||||||
let statements = block.statements();
|
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
let el = el
|
let el = el
|
||||||
.and_then(|el| {
|
.and_then(|el| {
|
||||||
let (_, block) = el.dissolve();
|
let (_, block) = el.clone().dissolve();
|
||||||
let statements = block.statements();
|
let statements = block.statements();
|
||||||
if statements.is_empty() {
|
if statements.is_empty() {
|
||||||
None
|
None
|
||||||
|
@ -338,41 +434,10 @@ impl Transpiler {
|
||||||
return Err(errors.remove(0));
|
return Err(errors.remove(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if statements.is_empty() {
|
Ok(Some(Execute::If(
|
||||||
if el.is_none() {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(Command::Execute(Execute::If(
|
|
||||||
datapack::Condition::from(cond),
|
datapack::Condition::from(cond),
|
||||||
Box::new(Execute::Runs(Vec::new())),
|
Box::new(then),
|
||||||
el,
|
el,
|
||||||
))))
|
)))
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let run = if statements.len() > 1 {
|
|
||||||
let commands = statements
|
|
||||||
.iter()
|
|
||||||
.filter_map(|statement| {
|
|
||||||
self.transpile_statement(statement, handler)
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
errors.push(err);
|
|
||||||
None
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Some(Execute::Runs(commands))
|
|
||||||
} else {
|
|
||||||
self.transpile_statement(&statements[0], handler)?
|
|
||||||
.map(|cmd| Execute::Run(Box::new(cmd)))
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(run.map(|run| {
|
|
||||||
Command::Execute(Execute::If(
|
|
||||||
datapack::Condition::from(cond),
|
|
||||||
Box::new(run),
|
|
||||||
el,
|
|
||||||
))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue