Implement execute blocks

- move conditionals to execute blocks
- implement "as" in execute blocks
This commit is contained in:
Moritz Hölting 2024-04-09 20:42:11 +02:00
parent 30bfdaf0a6
commit a818325ce9
10 changed files with 504 additions and 195 deletions

View File

@ -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) {}
} }

View File

@ -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;

View File

@ -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.

View File

@ -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(),

View File

@ -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()

View File

@ -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,

View File

@ -86,24 +86,9 @@ 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(); .and_then(|name| Namespace::validate_str(name.str_content()).then_some(name))?;
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))?;
let semicolon = self.parse_punctuation(';', true, handler)?; let semicolon = self.parse_punctuation(';', true, handler)?;

View File

@ -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

View File

@ -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
}
}
}
}

View File

@ -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() { datapack::Condition::from(cond),
Ok(None) Box::new(then),
} else { el,
Ok(Some(Command::Execute(Execute::If( )))
datapack::Condition::from(cond),
Box::new(Execute::Runs(Vec::new())),
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,
))
}))
}
} }
} }