implement basic while loop
This commit is contained in:
		
							parent
							
								
									218f488e76
								
							
						
					
					
						commit
						7f276a4139
					
				| 
						 | 
				
			
			@ -19,7 +19,7 @@ default = ["fs_access", "lua", "shulkerbox", "zip"]
 | 
			
		|||
fs_access = ["shulkerbox?/fs_access"]
 | 
			
		||||
lua = ["dep:mlua"]
 | 
			
		||||
serde = ["dep:serde", "dep:serde_json", "shulkerbox?/serde"]
 | 
			
		||||
shulkerbox = ["dep:shulkerbox", "dep:chksum-md5", "dep:serde_json"]
 | 
			
		||||
shulkerbox = ["dep:shulkerbox", "dep:chksum-md5", "dep:serde_json", "dep:oxford_join"]
 | 
			
		||||
zip = ["shulkerbox?/zip"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ pathdiff = "0.2.3"
 | 
			
		|||
serde = { version = "1.0.217", features = ["derive"], optional = true }
 | 
			
		||||
serde_json = { version = "1.0.138", optional = true }
 | 
			
		||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
 | 
			
		||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "d4689c696a35328c041bcbbfd203abd5818c46d3", default-features = false, optional = true }
 | 
			
		||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "e1bc953b7a1692c65f1ed2c43fa3b0c607df8090", default-features = false, optional = true }
 | 
			
		||||
strsim = "0.11.1"
 | 
			
		||||
strum = { version = "0.27.0", features = ["derive"] }
 | 
			
		||||
thiserror = "2.0.11"
 | 
			
		||||
| 
						 | 
				
			
			@ -57,3 +57,6 @@ required-features = ["shulkerbox"]
 | 
			
		|||
[[test]]
 | 
			
		||||
name = "transpiling"
 | 
			
		||||
required-features = ["shulkerbox"]
 | 
			
		||||
 | 
			
		||||
[package.metadata.cargo-feature-combinations]
 | 
			
		||||
exclude_features = [ "default" ]
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +148,7 @@ Statement:
 | 
			
		|||
    | Grouping
 | 
			
		||||
    | DocComment
 | 
			
		||||
    | ExecuteBlock
 | 
			
		||||
    | WhileLoop
 | 
			
		||||
    | Semicolon
 | 
			
		||||
    ;
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			@ -215,6 +216,14 @@ Semicolon:
 | 
			
		|||
  ;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## WhileLoop
 | 
			
		||||
 | 
			
		||||
```ebnf
 | 
			
		||||
WhileLoop:
 | 
			
		||||
  'while' '(' Expression ')' Block
 | 
			
		||||
  ;
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## FunctionVariableType
 | 
			
		||||
 | 
			
		||||
```ebnf
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,8 @@ pub enum Error {
 | 
			
		|||
    UnknownIdentifier(#[from] UnknownIdentifier),
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    AssignmentError(#[from] AssignmentError),
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    NeverLoops(#[from] NeverLoops),
 | 
			
		||||
    #[error("Lua is disabled, but a Lua function was used.")]
 | 
			
		||||
    LuaDisabled,
 | 
			
		||||
    #[error("Other: {0}")]
 | 
			
		||||
| 
						 | 
				
			
			@ -228,3 +230,27 @@ impl Display for InvalidFunctionArguments {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl std::error::Error for InvalidFunctionArguments {}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
 | 
			
		||||
pub struct NeverLoops {
 | 
			
		||||
    pub reason: Span,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for NeverLoops {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "{}",
 | 
			
		||||
            Message::new(Severity::Error, "Loop never actually loops.")
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "\n{}",
 | 
			
		||||
            SourceCodeDisplay::new(
 | 
			
		||||
                &self.reason,
 | 
			
		||||
                Some("This statement causes the loop to always terminate.")
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,8 +3,13 @@
 | 
			
		|||
#![allow(clippy::missing_errors_doc)]
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    base::{self, source_file::SourceElement as _, Handler},
 | 
			
		||||
    base::{
 | 
			
		||||
        self,
 | 
			
		||||
        source_file::{SourceElement as _, Span},
 | 
			
		||||
        Handler,
 | 
			
		||||
    },
 | 
			
		||||
    lexical::token::KeywordKind,
 | 
			
		||||
    semantic::error::NeverLoops,
 | 
			
		||||
    syntax::syntax_tree::{
 | 
			
		||||
        declaration::{Declaration, Function, FunctionVariableType, ImportItems},
 | 
			
		||||
        expression::{
 | 
			
		||||
| 
						 | 
				
			
			@ -18,7 +23,7 @@ use crate::{
 | 
			
		|||
                ExecuteBlockTail,
 | 
			
		||||
            },
 | 
			
		||||
            Assignment, AssignmentDestination, Block, Grouping, Semicolon, SemicolonStatement,
 | 
			
		||||
            Statement, VariableDeclaration,
 | 
			
		||||
            Statement, VariableDeclaration, WhileLoop,
 | 
			
		||||
        },
 | 
			
		||||
        AnyStringLiteral,
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -254,6 +259,7 @@ impl Statement {
 | 
			
		|||
                let child_scope = SemanticScope::with_parent(scope);
 | 
			
		||||
                group.analyze_semantics(&child_scope, handler)
 | 
			
		||||
            }
 | 
			
		||||
            Self::WhileLoop(while_loop) => while_loop.analyze_semantics(scope, handler),
 | 
			
		||||
            Self::Semicolon(sem) => sem.analyze_semantics(scope, handler),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -367,6 +373,63 @@ impl Grouping {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn block_contains_unconditional_return(block: &Block) -> Option<Span> {
 | 
			
		||||
    block
 | 
			
		||||
        .statements
 | 
			
		||||
        .iter()
 | 
			
		||||
        .find_map(|statement| match statement {
 | 
			
		||||
            Statement::Semicolon(semicolon) => {
 | 
			
		||||
                let s = semicolon.statement();
 | 
			
		||||
                if s.is_return() {
 | 
			
		||||
                    Some(s.span())
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Statement::Grouping(group) => block_contains_unconditional_return(group.block()),
 | 
			
		||||
            Statement::ExecuteBlock(ex) => execute_block_contains_unconditional_return(ex),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn execute_block_contains_unconditional_return(ex: &ExecuteBlock) -> Option<Span> {
 | 
			
		||||
    match ex {
 | 
			
		||||
        ExecuteBlock::HeadTail(head, tail) => {
 | 
			
		||||
            if head.is_conditional() {
 | 
			
		||||
                None
 | 
			
		||||
            } else {
 | 
			
		||||
                match tail {
 | 
			
		||||
                    ExecuteBlockTail::Block(inner_block) => {
 | 
			
		||||
                        block_contains_unconditional_return(inner_block)
 | 
			
		||||
                    }
 | 
			
		||||
                    ExecuteBlockTail::ExecuteBlock(_, inner_ex) => {
 | 
			
		||||
                        execute_block_contains_unconditional_return(inner_ex)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ExecuteBlock::IfElse(..) => None,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WhileLoop {
 | 
			
		||||
    fn analyze_semantics(
 | 
			
		||||
        &self,
 | 
			
		||||
        scope: &SemanticScope,
 | 
			
		||||
        handler: &impl Handler<base::Error>,
 | 
			
		||||
    ) -> Result<(), error::Error> {
 | 
			
		||||
        self.condition().analyze_semantics(scope, handler)?;
 | 
			
		||||
 | 
			
		||||
        if let Some(reason) = block_contains_unconditional_return(self.block()) {
 | 
			
		||||
            let err = error::Error::NeverLoops(NeverLoops { reason });
 | 
			
		||||
            handler.receive(err.clone());
 | 
			
		||||
            return Err(err);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.block().analyze_semantics(scope, handler)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Semicolon {
 | 
			
		||||
    fn analyze_semantics(
 | 
			
		||||
        &self,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ use crate::{
 | 
			
		|||
    },
 | 
			
		||||
    syntax::{
 | 
			
		||||
        error::{Error, InvalidAnnotation, ParseResult, SyntaxKind, UnexpectedSyntax},
 | 
			
		||||
        parser::{Parser, Reading},
 | 
			
		||||
        parser::{DelimitedTree, Parser, Reading},
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ use super::{expression::Expression, Annotation, AnyStringLiteral};
 | 
			
		|||
///     | Grouping
 | 
			
		||||
///     | DocComment
 | 
			
		||||
///     | ExecuteBlock
 | 
			
		||||
///     | WhileLoop
 | 
			
		||||
///     | Semicolon
 | 
			
		||||
///     ;
 | 
			
		||||
/// ```
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +56,7 @@ pub enum Statement {
 | 
			
		|||
    ExecuteBlock(ExecuteBlock),
 | 
			
		||||
    Grouping(Grouping),
 | 
			
		||||
    DocComment(DocComment),
 | 
			
		||||
    WhileLoop(WhileLoop),
 | 
			
		||||
    Semicolon(Semicolon),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -66,6 +68,7 @@ impl SourceElement for Statement {
 | 
			
		|||
            Self::ExecuteBlock(execute_block) => execute_block.span(),
 | 
			
		||||
            Self::Grouping(grouping) => grouping.span(),
 | 
			
		||||
            Self::DocComment(doc_comment) => doc_comment.span(),
 | 
			
		||||
            Self::WhileLoop(while_loop) => while_loop.span(),
 | 
			
		||||
            Self::Semicolon(semi) => semi.span(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +186,7 @@ pub struct Grouping {
 | 
			
		|||
    /// The `group` keyword.
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    group_keyword: Keyword,
 | 
			
		||||
    /// The block of the conditional.
 | 
			
		||||
    /// The block of the grouping.
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    block: Block,
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -205,6 +208,58 @@ impl SourceElement for Grouping {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a while loop in the syntax tree.
 | 
			
		||||
///
 | 
			
		||||
/// Syntax Synopsis:
 | 
			
		||||
///
 | 
			
		||||
/// ```ebnf
 | 
			
		||||
/// WhileLoop:
 | 
			
		||||
///   'while' '(' Expression ')' Block
 | 
			
		||||
///   ;
 | 
			
		||||
/// ```
 | 
			
		||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
			
		||||
pub struct WhileLoop {
 | 
			
		||||
    /// The `while` keyword.
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    while_keyword: Keyword,
 | 
			
		||||
    /// The opening parenthesis
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    open_paren: Punctuation,
 | 
			
		||||
    /// The condition expression
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    condition: Expression,
 | 
			
		||||
    /// The closing parenthesis
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    close_paren: Punctuation,
 | 
			
		||||
    /// The block of the loop.
 | 
			
		||||
    #[get = "pub"]
 | 
			
		||||
    block: Block,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SourceElement for WhileLoop {
 | 
			
		||||
    fn span(&self) -> Span {
 | 
			
		||||
        self.while_keyword
 | 
			
		||||
            .span
 | 
			
		||||
            .join(&self.block.span())
 | 
			
		||||
            .expect("spans in same file")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl WhileLoop {
 | 
			
		||||
    /// Dissolves the [`WhileLoop`] into its components.
 | 
			
		||||
    #[must_use]
 | 
			
		||||
    pub fn dissolve(self) -> (Keyword, Punctuation, Expression, Punctuation, Block) {
 | 
			
		||||
        (
 | 
			
		||||
            self.while_keyword,
 | 
			
		||||
            self.open_paren,
 | 
			
		||||
            self.condition,
 | 
			
		||||
            self.close_paren,
 | 
			
		||||
            self.block,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a statement that ends with a semicolon in the syntax tree.
 | 
			
		||||
///
 | 
			
		||||
/// Syntax Synopsis:
 | 
			
		||||
| 
						 | 
				
			
			@ -920,11 +975,54 @@ impl Parser<'_> {
 | 
			
		|||
                }))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // while loop
 | 
			
		||||
            Reading::Atomic(Token::Keyword(while_keyword))
 | 
			
		||||
                if while_keyword.keyword == KeywordKind::While =>
 | 
			
		||||
            {
 | 
			
		||||
                self.parse_while_loop(handler).map(Statement::WhileLoop)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // semicolon statement
 | 
			
		||||
            _ => self.parse_semicolon(handler).map(Statement::Semicolon),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Parses a [`WhileLoop`].
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    /// - if the parser is not at a while loop
 | 
			
		||||
    #[tracing::instrument(level = "trace", skip_all)]
 | 
			
		||||
    pub fn parse_while_loop(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        handler: &impl Handler<base::Error>,
 | 
			
		||||
    ) -> ParseResult<WhileLoop> {
 | 
			
		||||
        let while_keyword = self.parse_keyword(KeywordKind::While, handler)?;
 | 
			
		||||
 | 
			
		||||
        self.stop_at_significant();
 | 
			
		||||
 | 
			
		||||
        let DelimitedTree {
 | 
			
		||||
            open: open_paren,
 | 
			
		||||
            tree,
 | 
			
		||||
            close: close_paren,
 | 
			
		||||
        } = self.step_into(
 | 
			
		||||
            Delimiter::Parenthesis,
 | 
			
		||||
            |p| p.parse_expression(handler),
 | 
			
		||||
            handler,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        let condition = tree?;
 | 
			
		||||
 | 
			
		||||
        let block = self.parse_block(handler)?;
 | 
			
		||||
 | 
			
		||||
        Ok(WhileLoop {
 | 
			
		||||
            while_keyword,
 | 
			
		||||
            open_paren,
 | 
			
		||||
            condition,
 | 
			
		||||
            close_paren,
 | 
			
		||||
            block,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Parses a [`Semicolon`].
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,8 @@
 | 
			
		|||
use std::fmt::Display;
 | 
			
		||||
 | 
			
		||||
use getset::Getters;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "shulkerbox")]
 | 
			
		||||
use oxford_join::OxfordJoin as _;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +49,8 @@ pub enum TranspileError {
 | 
			
		|||
    InvalidArgument(#[from] InvalidArgument),
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    NotComptime(#[from] NotComptime),
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    InfiniteLoop(#[from] InfiniteLoop),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The result of a transpilation operation.
 | 
			
		||||
| 
						 | 
				
			
			@ -263,7 +267,11 @@ impl Display for UnknownIdentifier {
 | 
			
		|||
                .iter()
 | 
			
		||||
                .map(|s| format!("`{s}`"))
 | 
			
		||||
                .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "shulkerbox")]
 | 
			
		||||
            let inner = inner.oxford_or();
 | 
			
		||||
            #[cfg(not(feature = "shulkerbox"))]
 | 
			
		||||
            let inner = std::borrow::Cow::<str>::Owned(inner.join(", "));
 | 
			
		||||
 | 
			
		||||
            Some(message + &inner + "?")
 | 
			
		||||
        };
 | 
			
		||||
| 
						 | 
				
			
			@ -436,3 +444,29 @@ impl Display for NotComptime {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl std::error::Error for NotComptime {}
 | 
			
		||||
 | 
			
		||||
/// An error that occurs when a loop never terminates.
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
 | 
			
		||||
pub struct InfiniteLoop {
 | 
			
		||||
    /// The condition making it not terminate.
 | 
			
		||||
    pub span: Span,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Display for InfiniteLoop {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "{}",
 | 
			
		||||
            Message::new(Severity::Error, "Loop never terminates.")
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "\n{}",
 | 
			
		||||
            SourceCodeDisplay::new(
 | 
			
		||||
                &self.span,
 | 
			
		||||
                Some("You may want to use a separate function with the `#[tick]` annotation.")
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ use std::{
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
use itertools::Itertools;
 | 
			
		||||
use shulkerbox::datapack::{self, Command, Datapack, Execute, Group};
 | 
			
		||||
use shulkerbox::datapack::{self, Command, Datapack, Execute, Group, While as WhileCmd};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    base::{
 | 
			
		||||
| 
						 | 
				
			
			@ -22,13 +22,13 @@ use crate::{
 | 
			
		|||
        program::{Namespace, ProgramFile},
 | 
			
		||||
        statement::{
 | 
			
		||||
            execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail},
 | 
			
		||||
            ReturnStatement, SemicolonStatement, Statement,
 | 
			
		||||
            Block, ReturnStatement, SemicolonStatement, Statement,
 | 
			
		||||
        },
 | 
			
		||||
        AnnotationAssignment,
 | 
			
		||||
    },
 | 
			
		||||
    transpile::{
 | 
			
		||||
        conversions::ShulkerboxMacroStringMap,
 | 
			
		||||
        error::IllegalAnnotationContent,
 | 
			
		||||
        error::{IllegalAnnotationContent, InfiniteLoop},
 | 
			
		||||
        expression::DataLocation,
 | 
			
		||||
        util::{MacroString, MacroStringPart},
 | 
			
		||||
        variables::FunctionVariableDataType,
 | 
			
		||||
| 
						 | 
				
			
			@ -365,6 +365,33 @@ impl Transpiler {
 | 
			
		|||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn transpile_block(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        block: &Block,
 | 
			
		||||
        program_identifier: &str,
 | 
			
		||||
        scope: &Arc<Scope>,
 | 
			
		||||
        handler: &impl Handler<base::Error>,
 | 
			
		||||
    ) -> TranspileResult<Vec<Command>> {
 | 
			
		||||
        let child_scope = Scope::with_parent(scope.clone());
 | 
			
		||||
        let statements = block.statements();
 | 
			
		||||
        let mut errors = Vec::new();
 | 
			
		||||
        let commands = statements
 | 
			
		||||
            .iter()
 | 
			
		||||
            .flat_map(|statement| {
 | 
			
		||||
                self.transpile_statement(statement, program_identifier, &child_scope, handler)
 | 
			
		||||
                    .unwrap_or_else(|err| {
 | 
			
		||||
                        errors.push(err);
 | 
			
		||||
                        Vec::new()
 | 
			
		||||
                    })
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
        if !errors.is_empty() {
 | 
			
		||||
            return Err(errors.remove(0));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(commands)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(super) fn transpile_statement(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        statement: &Statement,
 | 
			
		||||
| 
						 | 
				
			
			@ -393,33 +420,46 @@ impl Transpiler {
 | 
			
		|||
                Ok(vec![Command::Comment(content.to_string())])
 | 
			
		||||
            }
 | 
			
		||||
            Statement::Grouping(group) => {
 | 
			
		||||
                let child_scope = Scope::with_parent(scope.clone());
 | 
			
		||||
                let statements = group.block().statements();
 | 
			
		||||
                let mut errors = Vec::new();
 | 
			
		||||
                let commands = statements
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .flat_map(|statement| {
 | 
			
		||||
                        self.transpile_statement(
 | 
			
		||||
                            statement,
 | 
			
		||||
                            program_identifier,
 | 
			
		||||
                            &child_scope,
 | 
			
		||||
                            handler,
 | 
			
		||||
                        )
 | 
			
		||||
                        .unwrap_or_else(|err| {
 | 
			
		||||
                            errors.push(err);
 | 
			
		||||
                            Vec::new()
 | 
			
		||||
                        })
 | 
			
		||||
                    })
 | 
			
		||||
                    .collect::<Vec<_>>();
 | 
			
		||||
                if !errors.is_empty() {
 | 
			
		||||
                    return Err(errors.remove(0));
 | 
			
		||||
                }
 | 
			
		||||
                let commands =
 | 
			
		||||
                    self.transpile_block(group.block(), program_identifier, scope, handler)?;
 | 
			
		||||
                if commands.is_empty() {
 | 
			
		||||
                    Ok(Vec::new())
 | 
			
		||||
                } else {
 | 
			
		||||
                    Ok(vec![Command::Group(Group::new(commands))])
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Statement::WhileLoop(while_loop) => {
 | 
			
		||||
                let (mut condition_commands, prepare_variables, condition) =
 | 
			
		||||
                    self.transpile_expression_as_condition(while_loop.condition(), scope, handler)?;
 | 
			
		||||
 | 
			
		||||
                match condition {
 | 
			
		||||
                    ExtendedCondition::Comptime(false) => Ok(Vec::new()),
 | 
			
		||||
                    ExtendedCondition::Comptime(true) => {
 | 
			
		||||
                        let err = TranspileError::InfiniteLoop(InfiniteLoop {
 | 
			
		||||
                            span: while_loop.condition().span(),
 | 
			
		||||
                        });
 | 
			
		||||
                        handler.receive(Box::new(err.clone()));
 | 
			
		||||
                        Err(err)
 | 
			
		||||
                    }
 | 
			
		||||
                    ExtendedCondition::Runtime(condition) => {
 | 
			
		||||
                        let loop_commands = self.transpile_block(
 | 
			
		||||
                            while_loop.block(),
 | 
			
		||||
                            program_identifier,
 | 
			
		||||
                            scope,
 | 
			
		||||
                            handler,
 | 
			
		||||
                        )?;
 | 
			
		||||
 | 
			
		||||
                        condition_commands
 | 
			
		||||
                            .push(Command::While(WhileCmd::new(condition, loop_commands)));
 | 
			
		||||
 | 
			
		||||
                        self.transpile_commands_with_variable_macros(
 | 
			
		||||
                            condition_commands,
 | 
			
		||||
                            prepare_variables,
 | 
			
		||||
                            handler,
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Statement::Semicolon(semi) => match semi.statement() {
 | 
			
		||||
                SemicolonStatement::Expression(expr) => match expr {
 | 
			
		||||
                    Expression::Primary(Primary::FunctionCall(func)) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,10 @@
 | 
			
		|||
//! Utility methods for transpiling
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "shulkerbox")]
 | 
			
		||||
use std::{collections::BTreeMap, sync::Arc};
 | 
			
		||||
use std::{
 | 
			
		||||
    collections::{BTreeMap, HashMap},
 | 
			
		||||
    sync::Arc,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "shulkerbox")]
 | 
			
		||||
use shulkerbox::prelude::Command;
 | 
			
		||||
| 
						 | 
				
			
			@ -347,6 +350,7 @@ impl TemplateStringLiteral {
 | 
			
		|||
    ) -> TranspileResult<MacroString> {
 | 
			
		||||
        if self.contains_expression() {
 | 
			
		||||
            let mut prepare_variables = BTreeMap::new();
 | 
			
		||||
            let mut prepare_variables_reverse = HashMap::<DataLocation, String>::new();
 | 
			
		||||
 | 
			
		||||
            let parts = self
 | 
			
		||||
                .parts()
 | 
			
		||||
| 
						 | 
				
			
			@ -400,10 +404,25 @@ impl TemplateStringLiteral {
 | 
			
		|||
                                            path: path.to_owned(),
 | 
			
		||||
                                            r#type: StorageType::Boolean,
 | 
			
		||||
                                        };
 | 
			
		||||
 | 
			
		||||
                                        let macro_name = if let Some(prev_macro_name) =
 | 
			
		||||
                                            prepare_variables_reverse.get(&data_location)
 | 
			
		||||
                                        {
 | 
			
		||||
                                            prev_macro_name.to_string()
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            prepare_variables.insert(
 | 
			
		||||
                                                macro_name.clone(),
 | 
			
		||||
                                            (data_location, Vec::new(), expression.span()),
 | 
			
		||||
                                                (
 | 
			
		||||
                                                    data_location.clone(),
 | 
			
		||||
                                                    Vec::new(),
 | 
			
		||||
                                                    expression.span(),
 | 
			
		||||
                                                ),
 | 
			
		||||
                                            );
 | 
			
		||||
                                            prepare_variables_reverse
 | 
			
		||||
                                                .insert(data_location, macro_name.clone());
 | 
			
		||||
 | 
			
		||||
                                            macro_name
 | 
			
		||||
                                        };
 | 
			
		||||
 | 
			
		||||
                                        Ok(vec![MacroStringPart::MacroUsage(macro_name)])
 | 
			
		||||
                                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -421,10 +440,24 @@ impl TemplateStringLiteral {
 | 
			
		|||
                                            objective: objective.to_owned(),
 | 
			
		||||
                                            target: target.to_owned(),
 | 
			
		||||
                                        };
 | 
			
		||||
                                        let macro_name = if let Some(prev_macro_name) =
 | 
			
		||||
                                            prepare_variables_reverse.get(&data_location)
 | 
			
		||||
                                        {
 | 
			
		||||
                                            prev_macro_name.to_string()
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            prepare_variables.insert(
 | 
			
		||||
                                                macro_name.clone(),
 | 
			
		||||
                                            (data_location, Vec::new(), expression.span()),
 | 
			
		||||
                                                (
 | 
			
		||||
                                                    data_location.clone(),
 | 
			
		||||
                                                    Vec::new(),
 | 
			
		||||
                                                    expression.span(),
 | 
			
		||||
                                                ),
 | 
			
		||||
                                            );
 | 
			
		||||
                                            prepare_variables_reverse
 | 
			
		||||
                                                .insert(data_location, macro_name.clone());
 | 
			
		||||
 | 
			
		||||
                                            macro_name
 | 
			
		||||
                                        };
 | 
			
		||||
 | 
			
		||||
                                        Ok(vec![MacroStringPart::MacroUsage(macro_name)])
 | 
			
		||||
                                    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue