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.
|
||||
#[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) {}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ mod error;
|
|||
pub use error::{Error, Result};
|
||||
|
||||
mod diagnostic;
|
||||
pub use diagnostic::{Dummy, Handler};
|
||||
pub use diagnostic::{DummyHandler, Handler};
|
||||
|
||||
pub mod log;
|
||||
|
|
|
@ -21,6 +21,7 @@ pub enum KeywordKind {
|
|||
Function,
|
||||
If,
|
||||
Else,
|
||||
As,
|
||||
Group,
|
||||
Run,
|
||||
Lua,
|
||||
|
@ -66,12 +67,19 @@ impl KeywordKind {
|
|||
Self::Function => "fn",
|
||||
Self::If => "if",
|
||||
Self::Else => "else",
|
||||
Self::As => "as",
|
||||
Self::Group => "group",
|
||||
Self::Run => "run",
|
||||
Self::Lua => "lua",
|
||||
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.
|
||||
|
|
|
@ -20,6 +20,8 @@ pub enum SyntaxKind {
|
|||
Statement,
|
||||
Expression,
|
||||
Type,
|
||||
ExecuteBlock,
|
||||
ExecuteBlockTail,
|
||||
}
|
||||
|
||||
/// 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::Expression => "an expression 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() {
|
||||
Some(Token::Comment(..)) => "a comment token".to_string(),
|
||||
|
|
|
@ -6,7 +6,7 @@ use enum_as_inner::EnumAsInner;
|
|||
use crate::{
|
||||
base::Handler,
|
||||
lexical::{
|
||||
token::{Identifier, Keyword, KeywordKind, Numeric, Punctuation, Token},
|
||||
token::{Identifier, Keyword, KeywordKind, Numeric, Punctuation, StringLiteral, Token},
|
||||
token_stream::{Delimited, Delimiter, TokenStream, TokenTree},
|
||||
},
|
||||
};
|
||||
|
@ -388,7 +388,7 @@ impl<'a> Frame<'a> {
|
|||
///
|
||||
/// # Errors
|
||||
/// 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() {
|
||||
Reading::Atomic(Token::Numeric(ident)) => Some(ident),
|
||||
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.
|
||||
///
|
||||
/// # Errors
|
||||
|
@ -408,7 +425,7 @@ impl<'a> Frame<'a> {
|
|||
pub fn parse_keyword(
|
||||
&mut self,
|
||||
expected: KeywordKind,
|
||||
handler: &dyn Handler<Error>,
|
||||
handler: &impl Handler<Error>,
|
||||
) -> Option<Keyword> {
|
||||
match self.next_significant_token() {
|
||||
Reading::Atomic(Token::Keyword(keyword_token)) if keyword_token.keyword == expected => {
|
||||
|
@ -432,7 +449,7 @@ impl<'a> Frame<'a> {
|
|||
&mut self,
|
||||
expected: char,
|
||||
skip_insignificant: bool,
|
||||
handler: &dyn Handler<Error>,
|
||||
handler: &impl Handler<Error>,
|
||||
) -> Option<Punctuation> {
|
||||
match if skip_insignificant {
|
||||
self.next_significant_token()
|
||||
|
|
|
@ -8,7 +8,7 @@ use getset::Getters;
|
|||
use crate::{
|
||||
base::{
|
||||
source_file::{SourceElement, Span},
|
||||
Dummy, Handler,
|
||||
DummyHandler, Handler,
|
||||
},
|
||||
lexical::{
|
||||
token::{Punctuation, StringLiteral, Token},
|
||||
|
@ -373,11 +373,11 @@ impl<'a> Parser<'a> {
|
|||
self.try_parse(|parser| match parser.next_significant_token() {
|
||||
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))
|
||||
}
|
||||
'|' => {
|
||||
let b = parser.parse_punctuation('|', false, &Dummy)?;
|
||||
let b = parser.parse_punctuation('|', false, &DummyHandler)?;
|
||||
Some(ConditionalBinaryOperator::LogicalOr(punc, b))
|
||||
}
|
||||
_ => None,
|
||||
|
|
|
@ -86,24 +86,9 @@ impl<'a> Parser<'a> {
|
|||
// eat the keyword
|
||||
self.forward();
|
||||
|
||||
let namespace_name = match self.stop_at_significant() {
|
||||
Reading::Atomic(Token::StringLiteral(name)) => {
|
||||
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))?;
|
||||
let namespace_name = self
|
||||
.parse_string_literal(handler)
|
||||
.and_then(|name| Namespace::validate_str(name.str_content()).then_some(name))?;
|
||||
|
||||
let semicolon = self.parse_punctuation(';', true, handler)?;
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
//! Syntax tree nodes for statements.
|
||||
|
||||
pub mod execute_block;
|
||||
|
||||
use derive_more::From;
|
||||
use getset::Getters;
|
||||
|
||||
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:
|
||||
///
|
||||
|
@ -34,11 +39,11 @@ use super::{condition::ParenthesizedCondition, expression::Expression};
|
|||
/// ```
|
||||
#[allow(missing_docs)]
|
||||
#[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 {
|
||||
Block(Block),
|
||||
LiteralCommand(CommandLiteral),
|
||||
Conditional(Conditional),
|
||||
ExecuteBlock(ExecuteBlock),
|
||||
Grouping(Grouping),
|
||||
DocComment(DocComment),
|
||||
Semicolon(Semicolon),
|
||||
|
@ -50,7 +55,7 @@ impl SourceElement for Statement {
|
|||
match self {
|
||||
Self::Block(block) => block.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::DocComment(doc_comment) => doc_comment.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:
|
||||
///
|
||||
/// ``` ebnf
|
||||
|
@ -341,45 +263,12 @@ impl<'a> Parser<'a> {
|
|||
Some(Statement::Block(block))
|
||||
}
|
||||
|
||||
// conditional statement
|
||||
Reading::Atomic(Token::Keyword(if_keyword))
|
||||
if if_keyword.keyword == KeywordKind::If =>
|
||||
// execute block
|
||||
Reading::Atomic(Token::Keyword(execute_keyword))
|
||||
if execute_keyword.keyword.starts_execute_block() =>
|
||||
{
|
||||
// eat the if keyword
|
||||
self.forward();
|
||||
|
||||
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,
|
||||
})),
|
||||
}
|
||||
self.parse_execute_block_statement(handler)
|
||||
.map(Statement::ExecuteBlock)
|
||||
}
|
||||
|
||||
// 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,
|
||||
expression::{Expression, FunctionCall, Primary},
|
||||
program::{Namespace, ProgramFile},
|
||||
statement::{Conditional, Statement},
|
||||
statement::{
|
||||
execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
|
||||
Statement,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -243,7 +246,7 @@ impl Transpiler {
|
|||
Statement::Block(_) => {
|
||||
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) => {
|
||||
let content = doccomment.content();
|
||||
Ok(Some(Command::Comment(content.to_string())))
|
||||
|
@ -295,19 +298,112 @@ impl Transpiler {
|
|||
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(
|
||||
&mut self,
|
||||
cond: &Conditional,
|
||||
then: Execute,
|
||||
el: Option<&Else>,
|
||||
handler: &impl Handler<TranspileError>,
|
||||
) -> TranspileResult<Option<Command>> {
|
||||
let (_, cond, block, el) = cond.clone().dissolve();
|
||||
) -> TranspileResult<Option<Execute>> {
|
||||
let (_, cond) = cond.clone().dissolve();
|
||||
let (_, cond, _) = cond.dissolve();
|
||||
let statements = block.statements();
|
||||
let mut errors = Vec::new();
|
||||
|
||||
let el = el
|
||||
.and_then(|el| {
|
||||
let (_, block) = el.dissolve();
|
||||
let (_, block) = el.clone().dissolve();
|
||||
let statements = block.statements();
|
||||
if statements.is_empty() {
|
||||
None
|
||||
|
@ -338,41 +434,10 @@ impl Transpiler {
|
|||
return Err(errors.remove(0));
|
||||
}
|
||||
|
||||
if statements.is_empty() {
|
||||
if el.is_none() {
|
||||
Ok(None)
|
||||
} else {
|
||||
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,
|
||||
))
|
||||
}))
|
||||
}
|
||||
Ok(Some(Execute::If(
|
||||
datapack::Condition::from(cond),
|
||||
Box::new(then),
|
||||
el,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue