Compare commits
	
		
			4 Commits
		
	
	
		
			f3b3d5d3b6
			...
			469b8d3875
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						469b8d3875 | |
| 
							
							
								 | 
						ab76b1d43e | |
| 
							
							
								 | 
						f808fef3f1 | |
| 
							
							
								 | 
						32d453ebef | 
| 
						 | 
					@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Changed
 | 
					### Changed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Change the syntax to set the type of a tag from `tag "[name]" of "[type]"` to `tag<"[type]"> "[name]"`
 | 
				
			||||||
 | 
					    - Remove the keyword `of`
 | 
				
			||||||
- Option to deduplicate source files during serialization when using `SerdeWrapper`
 | 
					- Option to deduplicate source files during serialization when using `SerdeWrapper`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Removed
 | 
					### Removed
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,11 +48,11 @@ pub enum KeywordKind {
 | 
				
			||||||
    From,
 | 
					    From,
 | 
				
			||||||
    Import,
 | 
					    Import,
 | 
				
			||||||
    Tag,
 | 
					    Tag,
 | 
				
			||||||
    Of,
 | 
					 | 
				
			||||||
    Replace,
 | 
					    Replace,
 | 
				
			||||||
    Int,
 | 
					    Int,
 | 
				
			||||||
    Bool,
 | 
					    Bool,
 | 
				
			||||||
    Macro,
 | 
					    Macro,
 | 
				
			||||||
 | 
					    Val,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Display for KeywordKind {
 | 
					impl Display for KeywordKind {
 | 
				
			||||||
| 
						 | 
					@ -114,11 +114,11 @@ impl KeywordKind {
 | 
				
			||||||
            Self::From => "from",
 | 
					            Self::From => "from",
 | 
				
			||||||
            Self::Import => "import",
 | 
					            Self::Import => "import",
 | 
				
			||||||
            Self::Tag => "tag",
 | 
					            Self::Tag => "tag",
 | 
				
			||||||
            Self::Of => "of",
 | 
					 | 
				
			||||||
            Self::Replace => "replace",
 | 
					            Self::Replace => "replace",
 | 
				
			||||||
            Self::Int => "int",
 | 
					            Self::Int => "int",
 | 
				
			||||||
            Self::Bool => "bool",
 | 
					            Self::Bool => "bool",
 | 
				
			||||||
            Self::Macro => "macro",
 | 
					            Self::Macro => "macro",
 | 
				
			||||||
 | 
					            Self::Val => "val",
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,10 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#![allow(missing_docs)]
 | 
					#![allow(missing_docs)]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{collections::HashSet, fmt::Display};
 | 
					use std::fmt::Display;
 | 
				
			||||||
 | 
					 | 
				
			||||||
use getset::Getters;
 | 
					 | 
				
			||||||
use itertools::Itertools as _;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    base::{
 | 
					    base::{
 | 
				
			||||||
| 
						 | 
					@ -14,6 +11,10 @@ use crate::{
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    lexical::token::StringLiteral,
 | 
					    lexical::token::StringLiteral,
 | 
				
			||||||
    syntax::syntax_tree::expression::Expression,
 | 
					    syntax::syntax_tree::expression::Expression,
 | 
				
			||||||
 | 
					    transpile::error::{
 | 
				
			||||||
 | 
					        AssignmentError, IllegalIndexing, MismatchedTypes, MissingFunctionDeclaration,
 | 
				
			||||||
 | 
					        UnknownIdentifier,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
 | 
				
			||||||
| 
						 | 
					@ -31,77 +32,20 @@ pub enum Error {
 | 
				
			||||||
    UnresolvedMacroUsage(#[from] UnresolvedMacroUsage),
 | 
					    UnresolvedMacroUsage(#[from] UnresolvedMacroUsage),
 | 
				
			||||||
    #[error(transparent)]
 | 
					    #[error(transparent)]
 | 
				
			||||||
    IncompatibleFunctionAnnotation(#[from] IncompatibleFunctionAnnotation),
 | 
					    IncompatibleFunctionAnnotation(#[from] IncompatibleFunctionAnnotation),
 | 
				
			||||||
 | 
					    #[error(transparent)]
 | 
				
			||||||
 | 
					    IllegalIndexing(#[from] IllegalIndexing),
 | 
				
			||||||
 | 
					    #[error(transparent)]
 | 
				
			||||||
 | 
					    MismatchedTypes(#[from] MismatchedTypes),
 | 
				
			||||||
 | 
					    #[error(transparent)]
 | 
				
			||||||
 | 
					    UnknownIdentifier(#[from] UnknownIdentifier),
 | 
				
			||||||
 | 
					    #[error(transparent)]
 | 
				
			||||||
 | 
					    AssignmentError(#[from] AssignmentError),
 | 
				
			||||||
 | 
					    #[error("Lua is disabled, but a Lua function was used.")]
 | 
				
			||||||
 | 
					    LuaDisabled,
 | 
				
			||||||
 | 
					    #[error("Other: {0}")]
 | 
				
			||||||
 | 
					    Other(String),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: remove duplicate error (also in transpile)
 | 
					 | 
				
			||||||
/// An error that occurs when a function declaration is missing.
 | 
					 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
 | 
					 | 
				
			||||||
pub struct MissingFunctionDeclaration {
 | 
					 | 
				
			||||||
    #[get = "pub"]
 | 
					 | 
				
			||||||
    span: Span,
 | 
					 | 
				
			||||||
    #[get = "pub"]
 | 
					 | 
				
			||||||
    alternatives: Vec<String>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl MissingFunctionDeclaration {
 | 
					 | 
				
			||||||
    #[expect(dead_code)]
 | 
					 | 
				
			||||||
    pub(super) fn from_context(identifier_span: Span, functions: &HashSet<String>) -> Self {
 | 
					 | 
				
			||||||
        let own_name = identifier_span.str();
 | 
					 | 
				
			||||||
        let alternatives = functions
 | 
					 | 
				
			||||||
            .iter()
 | 
					 | 
				
			||||||
            .filter_map(|function_name| {
 | 
					 | 
				
			||||||
                let normalized_distance =
 | 
					 | 
				
			||||||
                    strsim::normalized_damerau_levenshtein(own_name, function_name);
 | 
					 | 
				
			||||||
                (normalized_distance > 0.8
 | 
					 | 
				
			||||||
                    || strsim::damerau_levenshtein(own_name, function_name) < 3)
 | 
					 | 
				
			||||||
                    .then_some((normalized_distance, function_name))
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            .sorted_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal))
 | 
					 | 
				
			||||||
            .map(|(_, data)| data)
 | 
					 | 
				
			||||||
            .take(8)
 | 
					 | 
				
			||||||
            .cloned()
 | 
					 | 
				
			||||||
            .collect::<Vec<_>>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Self {
 | 
					 | 
				
			||||||
            alternatives,
 | 
					 | 
				
			||||||
            span: identifier_span,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Display for MissingFunctionDeclaration {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					 | 
				
			||||||
        use std::fmt::Write;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let message = format!(
 | 
					 | 
				
			||||||
            "no matching function declaration found for invocation of function `{}`",
 | 
					 | 
				
			||||||
            self.span.str()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        write!(f, "{}", Message::new(Severity::Error, message))?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let help_message = if self.alternatives.is_empty() {
 | 
					 | 
				
			||||||
            None
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            let mut message = String::from("did you mean ");
 | 
					 | 
				
			||||||
            for (i, alternative) in self.alternatives.iter().enumerate() {
 | 
					 | 
				
			||||||
                if i > 0 {
 | 
					 | 
				
			||||||
                    message.push_str(", ");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                write!(message, "`{alternative}`")?;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Some(message + "?")
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        write!(
 | 
					 | 
				
			||||||
            f,
 | 
					 | 
				
			||||||
            "\n{}",
 | 
					 | 
				
			||||||
            SourceCodeDisplay::new(&self.span, help_message.as_ref())
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl std::error::Error for MissingFunctionDeclaration {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// An error that occurs when a function declaration is missing.
 | 
					/// An error that occurs when a function declaration is missing.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub struct UnexpectedExpression(pub Expression);
 | 
					pub struct UnexpectedExpression(pub Expression);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1058
									
								
								src/semantic/mod.rs
								
								
								
								
							
							
						
						
									
										1058
									
								
								src/semantic/mod.rs
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -0,0 +1,97 @@
 | 
				
			||||||
 | 
					use std::{collections::HashMap, sync::RwLock};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Type of variable
 | 
				
			||||||
 | 
					#[derive(Clone, Debug, Copy, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub enum VariableType {
 | 
				
			||||||
 | 
					    /// A function.
 | 
				
			||||||
 | 
					    Function,
 | 
				
			||||||
 | 
					    /// A macro function parameter.
 | 
				
			||||||
 | 
					    MacroParameter,
 | 
				
			||||||
 | 
					    /// A scoreboard.
 | 
				
			||||||
 | 
					    Scoreboard,
 | 
				
			||||||
 | 
					    /// A scoreboard value.
 | 
				
			||||||
 | 
					    ScoreboardValue,
 | 
				
			||||||
 | 
					    /// Multiple values stored in scoreboard.
 | 
				
			||||||
 | 
					    ScoreboardArray,
 | 
				
			||||||
 | 
					    /// A tag applied to entities.
 | 
				
			||||||
 | 
					    Tag,
 | 
				
			||||||
 | 
					    /// A boolean stored in a data storage.
 | 
				
			||||||
 | 
					    BooleanStorage,
 | 
				
			||||||
 | 
					    /// Multiple booleans stored in a data storage array.
 | 
				
			||||||
 | 
					    BooleanStorageArray,
 | 
				
			||||||
 | 
					    /// Compiler internal function.
 | 
				
			||||||
 | 
					    InternalFunction,
 | 
				
			||||||
 | 
					    /// Compiler internal value.
 | 
				
			||||||
 | 
					    ComptimeValue,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A scope that stores variables.
 | 
				
			||||||
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
 | 
					pub struct SemanticScope<'a> {
 | 
				
			||||||
 | 
					    /// Parent scope where variables are inherited from.
 | 
				
			||||||
 | 
					    parent: Option<&'a Self>,
 | 
				
			||||||
 | 
					    /// Variables stored in the scope.
 | 
				
			||||||
 | 
					    variables: RwLock<HashMap<String, VariableType>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a> SemanticScope<'a> {
 | 
				
			||||||
 | 
					    /// Creates a new scope.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        let scope = Self::default();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scope.set_variable("print", VariableType::InternalFunction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scope
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Creates a new scope with a parent.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn with_parent(parent: &'a Self) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            parent: Some(parent),
 | 
				
			||||||
 | 
					            ..Default::default()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets a variable from the scope.
 | 
				
			||||||
 | 
					    pub fn get_variable(&self, name: &str) -> Option<VariableType> {
 | 
				
			||||||
 | 
					        let var = self.variables.read().unwrap().get(name).copied();
 | 
				
			||||||
 | 
					        if var.is_some() {
 | 
				
			||||||
 | 
					            var
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            self.parent
 | 
				
			||||||
 | 
					                .as_ref()
 | 
				
			||||||
 | 
					                .and_then(|parent| parent.get_variable(name))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Sets a variable in the scope.
 | 
				
			||||||
 | 
					    pub fn set_variable(&self, name: &str, var: VariableType) {
 | 
				
			||||||
 | 
					        self.variables
 | 
				
			||||||
 | 
					            .write()
 | 
				
			||||||
 | 
					            .unwrap()
 | 
				
			||||||
 | 
					            .insert(name.to_string(), var);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the variables stored in the current scope.
 | 
				
			||||||
 | 
					    pub fn get_local_variables(&self) -> &RwLock<HashMap<String, VariableType>> {
 | 
				
			||||||
 | 
					        &self.variables
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets all variables stored in the scope.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// This function does not return a reference to the variables, but clones them.
 | 
				
			||||||
 | 
					    pub fn get_all_variables(&self) -> HashMap<String, VariableType> {
 | 
				
			||||||
 | 
					        let mut variables = self.variables.read().unwrap().clone();
 | 
				
			||||||
 | 
					        if let Some(parent) = self.parent.as_ref() {
 | 
				
			||||||
 | 
					            variables.extend(parent.get_all_variables());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        variables
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Gets the parent scope.
 | 
				
			||||||
 | 
					    pub fn get_parent(&self) -> Option<&Self> {
 | 
				
			||||||
 | 
					        self.parent
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -253,7 +253,7 @@ impl SourceElement for Import {
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// ``` ebnf
 | 
					/// ``` ebnf
 | 
				
			||||||
/// TagDeclaration:
 | 
					/// TagDeclaration:
 | 
				
			||||||
///     'tag' StringLiteral ('of' StringLiteral)? 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']'
 | 
					///     'tag' ('<' StringLiteral '>')? StringLiteral 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']'
 | 
				
			||||||
///     ;
 | 
					///     ;
 | 
				
			||||||
/// ```
 | 
					/// ```
 | 
				
			||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
| 
						 | 
					@ -262,9 +262,9 @@ pub struct Tag {
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    tag_keyword: Keyword,
 | 
					    tag_keyword: Keyword,
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    name: StringLiteral,
 | 
					    of_type: Option<(Punctuation, StringLiteral, Punctuation)>,
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    of_type: Option<(Keyword, StringLiteral)>,
 | 
					    name: StringLiteral,
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    replace: Option<Keyword>,
 | 
					    replace: Option<Keyword>,
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
| 
						 | 
					@ -278,15 +278,15 @@ impl Tag {
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
    ) -> (
 | 
					    ) -> (
 | 
				
			||||||
        Keyword,
 | 
					        Keyword,
 | 
				
			||||||
 | 
					        Option<(Punctuation, StringLiteral, Punctuation)>,
 | 
				
			||||||
        StringLiteral,
 | 
					        StringLiteral,
 | 
				
			||||||
        Option<(Keyword, StringLiteral)>,
 | 
					 | 
				
			||||||
        Option<Keyword>,
 | 
					        Option<Keyword>,
 | 
				
			||||||
        DelimitedList<StringLiteral>,
 | 
					        DelimitedList<StringLiteral>,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
            self.tag_keyword,
 | 
					            self.tag_keyword,
 | 
				
			||||||
            self.name,
 | 
					 | 
				
			||||||
            self.of_type,
 | 
					            self.of_type,
 | 
				
			||||||
 | 
					            self.name,
 | 
				
			||||||
            self.replace,
 | 
					            self.replace,
 | 
				
			||||||
            self.entries,
 | 
					            self.entries,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					@ -299,7 +299,7 @@ impl Tag {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.of_type
 | 
					        self.of_type
 | 
				
			||||||
            .as_ref()
 | 
					            .as_ref()
 | 
				
			||||||
            .map_or(TagType::Function, |(_, tag_type)| {
 | 
					            .map_or(TagType::Function, |(_, tag_type, _)| {
 | 
				
			||||||
                match tag_type.str_content().as_ref() {
 | 
					                match tag_type.str_content().as_ref() {
 | 
				
			||||||
                    "function" => TagType::Function,
 | 
					                    "function" => TagType::Function,
 | 
				
			||||||
                    "block" => TagType::Block,
 | 
					                    "block" => TagType::Block,
 | 
				
			||||||
| 
						 | 
					@ -419,18 +419,24 @@ impl Parser<'_> {
 | 
				
			||||||
                // eat the tag keyword
 | 
					                // eat the tag keyword
 | 
				
			||||||
                self.forward();
 | 
					                self.forward();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let of_type = match self.stop_at_significant() {
 | 
				
			||||||
 | 
					                    Reading::Atomic(Token::Punctuation(punc)) if punc.punctuation == '<' => {
 | 
				
			||||||
 | 
					                        // eat the open bracket
 | 
				
			||||||
 | 
					                        self.forward();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        let of_type = self.parse_string_literal(handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // eat the close bracket
 | 
				
			||||||
 | 
					                        let closing = self.parse_punctuation('>', true, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Some((punc, of_type, closing))
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    _ => None,
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // parse the name
 | 
					                // parse the name
 | 
				
			||||||
                let name = self.parse_string_literal(handler)?;
 | 
					                let name = self.parse_string_literal(handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let of_type = self
 | 
					 | 
				
			||||||
                    .try_parse(|parser| {
 | 
					 | 
				
			||||||
                        let of_keyword = parser.parse_keyword(KeywordKind::Of, &VoidHandler)?;
 | 
					 | 
				
			||||||
                        let of_type = parser.parse_string_literal(handler)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                        Ok((of_keyword, of_type))
 | 
					 | 
				
			||||||
                    })
 | 
					 | 
				
			||||||
                    .ok();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let replace = self
 | 
					                let replace = self
 | 
				
			||||||
                    .try_parse(|parser| parser.parse_keyword(KeywordKind::Replace, &VoidHandler))
 | 
					                    .try_parse(|parser| parser.parse_keyword(KeywordKind::Replace, &VoidHandler))
 | 
				
			||||||
                    .ok();
 | 
					                    .ok();
 | 
				
			||||||
| 
						 | 
					@ -444,8 +450,8 @@ impl Parser<'_> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Ok(Declaration::Tag(Tag {
 | 
					                Ok(Declaration::Tag(Tag {
 | 
				
			||||||
                    tag_keyword,
 | 
					                    tag_keyword,
 | 
				
			||||||
                    name,
 | 
					 | 
				
			||||||
                    of_type,
 | 
					                    of_type,
 | 
				
			||||||
 | 
					                    name,
 | 
				
			||||||
                    replace,
 | 
					                    replace,
 | 
				
			||||||
                    entries,
 | 
					                    entries,
 | 
				
			||||||
                }))
 | 
					                }))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -247,6 +247,14 @@ impl SourceElement for Parenthesized {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::ops::Deref for Parenthesized {
 | 
				
			||||||
 | 
					    type Target = Expression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn deref(&self) -> &Self::Target {
 | 
				
			||||||
 | 
					        &self.expression
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents a indexed expression in the syntax tree.
 | 
					/// Represents a indexed expression in the syntax tree.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Syntax Synopsis:    
 | 
					/// Syntax Synopsis:    
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -325,6 +325,7 @@ pub enum VariableDeclaration {
 | 
				
			||||||
    Array(ArrayVariableDeclaration),
 | 
					    Array(ArrayVariableDeclaration),
 | 
				
			||||||
    Score(ScoreVariableDeclaration),
 | 
					    Score(ScoreVariableDeclaration),
 | 
				
			||||||
    Tag(TagVariableDeclaration),
 | 
					    Tag(TagVariableDeclaration),
 | 
				
			||||||
 | 
					    ComptimeValue(ComptimeValueDeclaration),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SourceElement for VariableDeclaration {
 | 
					impl SourceElement for VariableDeclaration {
 | 
				
			||||||
| 
						 | 
					@ -334,6 +335,7 @@ impl SourceElement for VariableDeclaration {
 | 
				
			||||||
            Self::Array(declaration) => declaration.span(),
 | 
					            Self::Array(declaration) => declaration.span(),
 | 
				
			||||||
            Self::Score(declaration) => declaration.span(),
 | 
					            Self::Score(declaration) => declaration.span(),
 | 
				
			||||||
            Self::Tag(declaration) => declaration.span(),
 | 
					            Self::Tag(declaration) => declaration.span(),
 | 
				
			||||||
 | 
					            Self::ComptimeValue(declaration) => declaration.span(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -347,6 +349,7 @@ impl VariableDeclaration {
 | 
				
			||||||
            Self::Array(declaration) => &declaration.identifier,
 | 
					            Self::Array(declaration) => &declaration.identifier,
 | 
				
			||||||
            Self::Score(declaration) => &declaration.identifier,
 | 
					            Self::Score(declaration) => &declaration.identifier,
 | 
				
			||||||
            Self::Tag(declaration) => &declaration.identifier,
 | 
					            Self::Tag(declaration) => &declaration.identifier,
 | 
				
			||||||
 | 
					            Self::ComptimeValue(declaration) => &declaration.identifier,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -358,6 +361,7 @@ impl VariableDeclaration {
 | 
				
			||||||
            Self::Array(declaration) => &declaration.variable_type,
 | 
					            Self::Array(declaration) => &declaration.variable_type,
 | 
				
			||||||
            Self::Score(declaration) => &declaration.int_keyword,
 | 
					            Self::Score(declaration) => &declaration.int_keyword,
 | 
				
			||||||
            Self::Tag(declaration) => &declaration.bool_keyword,
 | 
					            Self::Tag(declaration) => &declaration.bool_keyword,
 | 
				
			||||||
 | 
					            Self::ComptimeValue(declaration) => &declaration.val_keyword,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -383,6 +387,14 @@ impl VariableDeclaration {
 | 
				
			||||||
                declaration.annotations.push_front(annotation);
 | 
					                declaration.annotations.push_front(annotation);
 | 
				
			||||||
                Ok(Self::Tag(declaration))
 | 
					                Ok(Self::Tag(declaration))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Self::ComptimeValue(_) => {
 | 
				
			||||||
 | 
					                let err = Error::InvalidAnnotation(InvalidAnnotation {
 | 
				
			||||||
 | 
					                    annotation: annotation.assignment.identifier.span,
 | 
				
			||||||
 | 
					                    target: "comptime values".to_string(),
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Err(err)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -681,6 +693,53 @@ impl TagVariableDeclaration {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Represents a compile time value declaration in the syntax tree.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Syntax Synopsis:
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ```ebnf
 | 
				
			||||||
 | 
					/// ComptimeValueDeclaration:
 | 
				
			||||||
 | 
					///     'val' identifier VariableDeclarationAssignment?
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
				
			||||||
 | 
					pub struct ComptimeValueDeclaration {
 | 
				
			||||||
 | 
					    /// The type of the variable.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    val_keyword: Keyword,
 | 
				
			||||||
 | 
					    /// The identifier of the variable.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    identifier: Identifier,
 | 
				
			||||||
 | 
					    /// The optional assignment of the variable.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    assignment: Option<VariableDeclarationAssignment>,
 | 
				
			||||||
 | 
					    /// The annotations of the variable declaration.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    annotations: VecDeque<Annotation>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SourceElement for ComptimeValueDeclaration {
 | 
				
			||||||
 | 
					    fn span(&self) -> Span {
 | 
				
			||||||
 | 
					        self.val_keyword
 | 
				
			||||||
 | 
					            .span()
 | 
				
			||||||
 | 
					            .join(
 | 
				
			||||||
 | 
					                &self
 | 
				
			||||||
 | 
					                    .assignment
 | 
				
			||||||
 | 
					                    .as_ref()
 | 
				
			||||||
 | 
					                    .map_or_else(|| self.identifier.span(), SourceElement::span),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .expect("The span of the single variable declaration is invalid.")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ComptimeValueDeclaration {
 | 
				
			||||||
 | 
					    /// Dissolves the [`ComptimeValueDeclaration`] into its components.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn dissolve(self) -> (Keyword, Identifier, Option<VariableDeclarationAssignment>) {
 | 
				
			||||||
 | 
					        (self.val_keyword, self.identifier, self.assignment)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents an assignment in the syntax tree.
 | 
					/// Represents an assignment in the syntax tree.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Syntax Synopsis:
 | 
					/// Syntax Synopsis:
 | 
				
			||||||
| 
						 | 
					@ -883,7 +942,10 @@ impl Parser<'_> {
 | 
				
			||||||
    ) -> ParseResult<Semicolon> {
 | 
					    ) -> ParseResult<Semicolon> {
 | 
				
			||||||
        let statement = match self.stop_at_significant() {
 | 
					        let statement = match self.stop_at_significant() {
 | 
				
			||||||
            Reading::Atomic(Token::Keyword(keyword))
 | 
					            Reading::Atomic(Token::Keyword(keyword))
 | 
				
			||||||
                if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
 | 
					                if matches!(
 | 
				
			||||||
 | 
					                    keyword.keyword,
 | 
				
			||||||
 | 
					                    KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
 | 
				
			||||||
 | 
					                ) =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                self.parse_variable_declaration(handler)
 | 
					                self.parse_variable_declaration(handler)
 | 
				
			||||||
                    .map(SemicolonStatement::VariableDeclaration)
 | 
					                    .map(SemicolonStatement::VariableDeclaration)
 | 
				
			||||||
| 
						 | 
					@ -895,18 +957,20 @@ impl Parser<'_> {
 | 
				
			||||||
                    let destination = {
 | 
					                    let destination = {
 | 
				
			||||||
                        let identifier = p.parse_identifier(&VoidHandler)?;
 | 
					                        let identifier = p.parse_identifier(&VoidHandler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if let Ok(tree) = p.step_into(
 | 
					                        match p.stop_at_significant() {
 | 
				
			||||||
                            Delimiter::Bracket,
 | 
					                            Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
 | 
				
			||||||
                            |pp| pp.parse_expression(&VoidHandler),
 | 
					                                let tree = p.step_into(
 | 
				
			||||||
                            &VoidHandler,
 | 
					                                    Delimiter::Bracket,
 | 
				
			||||||
                        ) {
 | 
					                                    |pp| pp.parse_expression(&VoidHandler),
 | 
				
			||||||
                            let open = tree.open;
 | 
					                                    &VoidHandler,
 | 
				
			||||||
                            let close = tree.close;
 | 
					                                )?;
 | 
				
			||||||
                            let expression = tree.tree?;
 | 
					                                let open = tree.open;
 | 
				
			||||||
 | 
					                                let close = tree.close;
 | 
				
			||||||
 | 
					                                let expression = tree.tree?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            AssignmentDestination::Indexed(identifier, open, expression, close)
 | 
					                                AssignmentDestination::Indexed(identifier, open, expression, close)
 | 
				
			||||||
                        } else {
 | 
					                            }
 | 
				
			||||||
                            AssignmentDestination::Identifier(identifier)
 | 
					                            _ => AssignmentDestination::Identifier(identifier),
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    let equals = p.parse_punctuation('=', true, &VoidHandler)?;
 | 
					                    let equals = p.parse_punctuation('=', true, &VoidHandler)?;
 | 
				
			||||||
| 
						 | 
					@ -948,7 +1012,10 @@ impl Parser<'_> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let variable_type = match self.stop_at_significant() {
 | 
					        let variable_type = match self.stop_at_significant() {
 | 
				
			||||||
            Reading::Atomic(Token::Keyword(keyword))
 | 
					            Reading::Atomic(Token::Keyword(keyword))
 | 
				
			||||||
                if matches!(keyword.keyword, KeywordKind::Int | KeywordKind::Bool) =>
 | 
					                if matches!(
 | 
				
			||||||
 | 
					                    keyword.keyword,
 | 
				
			||||||
 | 
					                    KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
 | 
				
			||||||
 | 
					                ) =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                self.forward();
 | 
					                self.forward();
 | 
				
			||||||
                keyword
 | 
					                keyword
 | 
				
			||||||
| 
						 | 
					@ -983,7 +1050,10 @@ impl Parser<'_> {
 | 
				
			||||||
        let identifier = self.parse_identifier(handler)?;
 | 
					        let identifier = self.parse_identifier(handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match self.stop_at_significant() {
 | 
					        match self.stop_at_significant() {
 | 
				
			||||||
            Reading::IntoDelimited(punc) if punc.punctuation == '[' => {
 | 
					            Reading::IntoDelimited(punc)
 | 
				
			||||||
 | 
					                if punc.punctuation == '['
 | 
				
			||||||
 | 
					                    && matches!(variable_type.keyword, KeywordKind::Int | KeywordKind::Bool) =>
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
                let tree = self.step_into(
 | 
					                let tree = self.step_into(
 | 
				
			||||||
                    Delimiter::Bracket,
 | 
					                    Delimiter::Bracket,
 | 
				
			||||||
                    |p| {
 | 
					                    |p| {
 | 
				
			||||||
| 
						 | 
					@ -1071,12 +1141,23 @@ impl Parser<'_> {
 | 
				
			||||||
                let expression = self.parse_expression(handler)?;
 | 
					                let expression = self.parse_expression(handler)?;
 | 
				
			||||||
                let assignment = VariableDeclarationAssignment { equals, expression };
 | 
					                let assignment = VariableDeclarationAssignment { equals, expression };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Ok(VariableDeclaration::Single(SingleVariableDeclaration {
 | 
					                if variable_type.keyword == KeywordKind::Val {
 | 
				
			||||||
                    variable_type,
 | 
					                    Ok(VariableDeclaration::ComptimeValue(
 | 
				
			||||||
                    identifier,
 | 
					                        ComptimeValueDeclaration {
 | 
				
			||||||
                    assignment: Some(assignment),
 | 
					                            val_keyword: variable_type,
 | 
				
			||||||
                    annotations: VecDeque::new(),
 | 
					                            identifier,
 | 
				
			||||||
                }))
 | 
					                            assignment: Some(assignment),
 | 
				
			||||||
 | 
					                            annotations: VecDeque::new(),
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                    ))
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Ok(VariableDeclaration::Single(SingleVariableDeclaration {
 | 
				
			||||||
 | 
					                        variable_type,
 | 
				
			||||||
 | 
					                        identifier,
 | 
				
			||||||
 | 
					                        assignment: Some(assignment),
 | 
				
			||||||
 | 
					                        annotations: VecDeque::new(),
 | 
				
			||||||
 | 
					                    }))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            // SingleVariableDeclaration without Assignment
 | 
					            // SingleVariableDeclaration without Assignment
 | 
				
			||||||
            _ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
 | 
					            _ => Ok(VariableDeclaration::Single(SingleVariableDeclaration {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,5 @@
 | 
				
			||||||
//! Execute block statement syntax tree.
 | 
					//! Execute block statement syntax tree.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::collections::HashSet;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use derive_more::From;
 | 
					use derive_more::From;
 | 
				
			||||||
use enum_as_inner::EnumAsInner;
 | 
					use enum_as_inner::EnumAsInner;
 | 
				
			||||||
use getset::Getters;
 | 
					use getset::Getters;
 | 
				
			||||||
| 
						 | 
					@ -998,10 +996,10 @@ pub trait ExecuteBlockHeadItem {
 | 
				
			||||||
    #[expect(clippy::missing_errors_doc)]
 | 
					    #[expect(clippy::missing_errors_doc)]
 | 
				
			||||||
    fn analyze_semantics(
 | 
					    fn analyze_semantics(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        macro_names: &HashSet<String>,
 | 
					        scope: &crate::semantic::SemanticScope,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> Result<(), crate::semantic::error::Error> {
 | 
					    ) -> Result<(), crate::semantic::error::Error> {
 | 
				
			||||||
        self.selector().analyze_semantics(macro_names, handler)
 | 
					        self.selector().analyze_semantics(scope, handler)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,10 @@ pub enum TranspileError {
 | 
				
			||||||
    MissingValue(#[from] MissingValue),
 | 
					    MissingValue(#[from] MissingValue),
 | 
				
			||||||
    #[error(transparent)]
 | 
					    #[error(transparent)]
 | 
				
			||||||
    IllegalIndexing(#[from] IllegalIndexing),
 | 
					    IllegalIndexing(#[from] IllegalIndexing),
 | 
				
			||||||
 | 
					    #[error(transparent)]
 | 
				
			||||||
 | 
					    InvalidArgument(#[from] InvalidArgument),
 | 
				
			||||||
 | 
					    #[error(transparent)]
 | 
				
			||||||
 | 
					    NotComptime(#[from] NotComptime),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The result of a transpilation operation.
 | 
					/// The result of a transpilation operation.
 | 
				
			||||||
| 
						 | 
					@ -52,8 +56,10 @@ pub type TranspileResult<T> = Result<T, TranspileError>;
 | 
				
			||||||
/// An error that occurs when a function declaration is missing.
 | 
					/// An error that occurs when a function declaration is missing.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
				
			||||||
pub struct MissingFunctionDeclaration {
 | 
					pub struct MissingFunctionDeclaration {
 | 
				
			||||||
 | 
					    /// The span of the identifier that is missing.
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    span: Span,
 | 
					    span: Span,
 | 
				
			||||||
 | 
					    /// Possible alternatives for the missing function declaration.
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    alternatives: Vec<FunctionData>,
 | 
					    alternatives: Vec<FunctionData>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -127,11 +133,26 @@ impl Display for MissingFunctionDeclaration {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl std::error::Error for MissingFunctionDeclaration {}
 | 
					impl std::error::Error for MissingFunctionDeclaration {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::hash::Hash for MissingFunctionDeclaration {
 | 
				
			||||||
 | 
					    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
				
			||||||
 | 
					        self.span.hash(state);
 | 
				
			||||||
 | 
					        for alternative in &self.alternatives {
 | 
				
			||||||
 | 
					            alternative.identifier_span.hash(state);
 | 
				
			||||||
 | 
					            alternative.namespace.hash(state);
 | 
				
			||||||
 | 
					            alternative.parameters.hash(state);
 | 
				
			||||||
 | 
					            alternative.public.hash(state);
 | 
				
			||||||
 | 
					            alternative.statements.hash(state);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when a function declaration is missing.
 | 
					/// An error that occurs when a function declaration is missing.
 | 
				
			||||||
#[allow(clippy::module_name_repetitions)]
 | 
					#[allow(clippy::module_name_repetitions)]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
pub struct LuaRuntimeError {
 | 
					pub struct LuaRuntimeError {
 | 
				
			||||||
 | 
					    /// The span of the code block that caused the error.
 | 
				
			||||||
    pub code_block: Span,
 | 
					    pub code_block: Span,
 | 
				
			||||||
 | 
					    /// The error message of the Lua runtime.
 | 
				
			||||||
    pub error_message: String,
 | 
					    pub error_message: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,6 +176,8 @@ impl std::error::Error for LuaRuntimeError {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "lua")]
 | 
					#[cfg(feature = "lua")]
 | 
				
			||||||
impl LuaRuntimeError {
 | 
					impl LuaRuntimeError {
 | 
				
			||||||
 | 
					    /// Creates a new Lua runtime error from an mlua error.
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn from_lua_err(err: &mlua::Error, span: Span) -> Self {
 | 
					    pub fn from_lua_err(err: &mlua::Error, span: Span) -> Self {
 | 
				
			||||||
        let err_string = err.to_string();
 | 
					        let err_string = err.to_string();
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
| 
						 | 
					@ -168,9 +191,13 @@ impl LuaRuntimeError {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when an annotation has an illegal content.
 | 
					/// An error that occurs when an annotation has an illegal content.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
				
			||||||
pub struct IllegalAnnotationContent {
 | 
					pub struct IllegalAnnotationContent {
 | 
				
			||||||
 | 
					    /// The span of the annotation.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub annotation: Span,
 | 
					    pub annotation: Span,
 | 
				
			||||||
 | 
					    /// The error message.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub message: String,
 | 
					    pub message: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,9 +221,13 @@ impl Display for IllegalAnnotationContent {
 | 
				
			||||||
impl std::error::Error for IllegalAnnotationContent {}
 | 
					impl std::error::Error for IllegalAnnotationContent {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when an expression can not evaluate to the wanted type.
 | 
					/// An error that occurs when an expression can not evaluate to the wanted type.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
 | 
				
			||||||
pub struct MismatchedTypes {
 | 
					pub struct MismatchedTypes {
 | 
				
			||||||
 | 
					    /// The expression that can not evaluate to the wanted type.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub expression: Span,
 | 
					    pub expression: Span,
 | 
				
			||||||
 | 
					    /// The expected type.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub expected_type: ExpectedType,
 | 
					    pub expected_type: ExpectedType,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,9 +247,13 @@ impl Display for MismatchedTypes {
 | 
				
			||||||
impl std::error::Error for MismatchedTypes {}
 | 
					impl std::error::Error for MismatchedTypes {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when an expression can not evaluate to the wanted type.
 | 
					/// An error that occurs when an expression can not evaluate to the wanted type.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
				
			||||||
pub struct FunctionArgumentsNotAllowed {
 | 
					pub struct FunctionArgumentsNotAllowed {
 | 
				
			||||||
 | 
					    /// The arguments that are not allowed.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub arguments: Span,
 | 
					    pub arguments: Span,
 | 
				
			||||||
 | 
					    /// The error message.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub message: String,
 | 
					    pub message: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -237,9 +272,13 @@ impl Display for FunctionArgumentsNotAllowed {
 | 
				
			||||||
impl std::error::Error for FunctionArgumentsNotAllowed {}
 | 
					impl std::error::Error for FunctionArgumentsNotAllowed {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when an expression can not evaluate to the wanted type.
 | 
					/// An error that occurs when an expression can not evaluate to the wanted type.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
 | 
				
			||||||
pub struct AssignmentError {
 | 
					pub struct AssignmentError {
 | 
				
			||||||
 | 
					    /// The identifier that is assigned to.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub identifier: Span,
 | 
					    pub identifier: Span,
 | 
				
			||||||
 | 
					    /// The error message.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub message: String,
 | 
					    pub message: String,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -258,8 +297,10 @@ impl Display for AssignmentError {
 | 
				
			||||||
impl std::error::Error for AssignmentError {}
 | 
					impl std::error::Error for AssignmentError {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when an unknown identifier is used.
 | 
					/// An error that occurs when an unknown identifier is used.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
 | 
				
			||||||
pub struct UnknownIdentifier {
 | 
					pub struct UnknownIdentifier {
 | 
				
			||||||
 | 
					    /// The unknown identifier.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub identifier: Span,
 | 
					    pub identifier: Span,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -285,8 +326,10 @@ impl Display for UnknownIdentifier {
 | 
				
			||||||
impl std::error::Error for UnknownIdentifier {}
 | 
					impl std::error::Error for UnknownIdentifier {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when there is a value expected but none provided.
 | 
					/// An error that occurs when there is a value expected but none provided.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
				
			||||||
pub struct MissingValue {
 | 
					pub struct MissingValue {
 | 
				
			||||||
 | 
					    /// The expression that is missing a value.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub expression: Span,
 | 
					    pub expression: Span,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -312,9 +355,13 @@ impl Display for MissingValue {
 | 
				
			||||||
impl std::error::Error for MissingValue {}
 | 
					impl std::error::Error for MissingValue {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when an indexing operation is not permitted.
 | 
					/// An error that occurs when an indexing operation is not permitted.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, Getters)]
 | 
				
			||||||
pub struct IllegalIndexing {
 | 
					pub struct IllegalIndexing {
 | 
				
			||||||
 | 
					    /// The reason why the indexing operation is not permitted.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub reason: IllegalIndexingReason,
 | 
					    pub reason: IllegalIndexingReason,
 | 
				
			||||||
 | 
					    /// The expression that is the reason for the indexing being illegal.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
    pub expression: Span,
 | 
					    pub expression: Span,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -333,11 +380,24 @@ impl Display for IllegalIndexing {
 | 
				
			||||||
impl std::error::Error for IllegalIndexing {}
 | 
					impl std::error::Error for IllegalIndexing {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The reason why an indexing operation is not permitted.
 | 
					/// The reason why an indexing operation is not permitted.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum IllegalIndexingReason {
 | 
					pub enum IllegalIndexingReason {
 | 
				
			||||||
 | 
					    /// The expression is not an identifier.
 | 
				
			||||||
    NotIdentifier,
 | 
					    NotIdentifier,
 | 
				
			||||||
    InvalidComptimeType { expected: ExpectedType },
 | 
					    /// The expression cannot be indexed.
 | 
				
			||||||
    IndexOutOfBounds { index: usize, length: usize },
 | 
					    NotIndexable,
 | 
				
			||||||
 | 
					    /// The expression can only be indexed with a specific type that can be evaluated at compile time.
 | 
				
			||||||
 | 
					    InvalidComptimeType {
 | 
				
			||||||
 | 
					        /// The expected type.
 | 
				
			||||||
 | 
					        expected: ExpectedType,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    /// The index is out of bounds.
 | 
				
			||||||
 | 
					    IndexOutOfBounds {
 | 
				
			||||||
 | 
					        /// The index that is out of bounds.
 | 
				
			||||||
 | 
					        index: usize,
 | 
				
			||||||
 | 
					        /// The length indexed object.
 | 
				
			||||||
 | 
					        length: usize,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Display for IllegalIndexingReason {
 | 
					impl Display for IllegalIndexingReason {
 | 
				
			||||||
| 
						 | 
					@ -346,6 +406,9 @@ impl Display for IllegalIndexingReason {
 | 
				
			||||||
            Self::NotIdentifier => {
 | 
					            Self::NotIdentifier => {
 | 
				
			||||||
                write!(f, "The expression is not an identifier.")
 | 
					                write!(f, "The expression is not an identifier.")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Self::NotIndexable => {
 | 
				
			||||||
 | 
					                write!(f, "The expression cannot be indexed.")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            Self::InvalidComptimeType { expected } => {
 | 
					            Self::InvalidComptimeType { expected } => {
 | 
				
			||||||
                write!(
 | 
					                write!(
 | 
				
			||||||
                    f,
 | 
					                    f,
 | 
				
			||||||
| 
						 | 
					@ -361,3 +424,57 @@ impl Display for IllegalIndexingReason {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// An error that occurs when an indexing operation is not permitted.
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
				
			||||||
 | 
					pub struct InvalidArgument {
 | 
				
			||||||
 | 
					    /// The span of the argument.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    pub span: Span,
 | 
				
			||||||
 | 
					    /// The reason why the argument is invalid.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    pub reason: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Display for InvalidArgument {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        write!(f, "{}", Message::new(Severity::Error, &self.reason))?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        write!(
 | 
				
			||||||
 | 
					            f,
 | 
				
			||||||
 | 
					            "\n{}",
 | 
				
			||||||
 | 
					            SourceCodeDisplay::new(&self.span, Option::<u8>::None)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::error::Error for InvalidArgument {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// An error that occurs when an indexing operation is not permitted.
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
				
			||||||
 | 
					pub struct NotComptime {
 | 
				
			||||||
 | 
					    /// The expression that cannot be evaluated at compile time.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    pub expression: Span,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Display for NotComptime {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        write!(
 | 
				
			||||||
 | 
					            f,
 | 
				
			||||||
 | 
					            "{}",
 | 
				
			||||||
 | 
					            Message::new(
 | 
				
			||||||
 | 
					                Severity::Error,
 | 
				
			||||||
 | 
					                "The expression cannot be evaluated at compile time but is required to."
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        write!(
 | 
				
			||||||
 | 
					            f,
 | 
				
			||||||
 | 
					            "\n{}",
 | 
				
			||||||
 | 
					            SourceCodeDisplay::new(&self.expression, Option::<u8>::None)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl std::error::Error for NotComptime {}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,8 @@
 | 
				
			||||||
//! The expression transpiler.
 | 
					//! The expression transpiler.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{fmt::Display, string::ToString, sync::Arc};
 | 
					use std::{fmt::Display, string::ToString};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{util::MacroString, Scope, VariableData};
 | 
					use super::util::MacroString;
 | 
				
			||||||
use crate::{
 | 
					 | 
				
			||||||
    base::{self, Handler, VoidHandler},
 | 
					 | 
				
			||||||
    lexical::token::MacroStringLiteralPart,
 | 
					 | 
				
			||||||
    syntax::syntax_tree::expression::{
 | 
					 | 
				
			||||||
        Binary, BinaryOperator, Expression, PrefixOperator, Primary,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
use enum_as_inner::EnumAsInner;
 | 
					use enum_as_inner::EnumAsInner;
 | 
				
			||||||
| 
						 | 
					@ -19,17 +12,25 @@ use shulkerbox::prelude::{Command, Condition, Execute};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    error::{IllegalIndexing, IllegalIndexingReason, MismatchedTypes, UnknownIdentifier},
 | 
					    error::{
 | 
				
			||||||
    TranspileResult, Transpiler,
 | 
					        IllegalIndexing, IllegalIndexingReason, MismatchedTypes, NotComptime, UnknownIdentifier,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Scope, TranspileResult, Transpiler, VariableData,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    base::source_file::SourceElement,
 | 
					    base::{self, source_file::SourceElement, Handler, VoidHandler},
 | 
				
			||||||
 | 
					    lexical::token::MacroStringLiteralPart,
 | 
				
			||||||
 | 
					    syntax::syntax_tree::expression::{
 | 
				
			||||||
 | 
					        Binary, BinaryOperator, Expression, PrefixOperator, Primary,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    transpile::{
 | 
					    transpile::{
 | 
				
			||||||
        error::{FunctionArgumentsNotAllowed, MissingValue},
 | 
					        error::{FunctionArgumentsNotAllowed, MissingValue},
 | 
				
			||||||
        TranspileError,
 | 
					        TranspileError,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Compile-time evaluated value
 | 
					/// Compile-time evaluated value
 | 
				
			||||||
#[allow(missing_docs)]
 | 
					#[allow(missing_docs)]
 | 
				
			||||||
| 
						 | 
					@ -85,7 +86,7 @@ impl Display for ValueType {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[allow(missing_docs)]
 | 
					#[allow(missing_docs)]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 | 
				
			||||||
pub enum ExpectedType {
 | 
					pub enum ExpectedType {
 | 
				
			||||||
    Boolean,
 | 
					    Boolean,
 | 
				
			||||||
    Integer,
 | 
					    Integer,
 | 
				
			||||||
| 
						 | 
					@ -227,6 +228,7 @@ pub enum ExtendedCondition {
 | 
				
			||||||
    Comptime(bool),
 | 
					    Comptime(bool),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl Expression {
 | 
					impl Expression {
 | 
				
			||||||
    /// Returns whether the expression can yield a certain type.
 | 
					    /// Returns whether the expression can yield a certain type.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
| 
						 | 
					@ -238,12 +240,14 @@ impl Expression {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Evaluate at compile-time.
 | 
					    /// Evaluate at compile-time.
 | 
				
			||||||
    #[must_use]
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Errors
 | 
				
			||||||
 | 
					    /// - If the expression is not compile-time evaluatable.
 | 
				
			||||||
    pub fn comptime_eval(
 | 
					    pub fn comptime_eval(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> Option<ComptimeValue> {
 | 
					    ) -> Result<ComptimeValue, NotComptime> {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::Primary(primary) => primary.comptime_eval(scope, handler),
 | 
					            Self::Primary(primary) => primary.comptime_eval(scope, handler),
 | 
				
			||||||
            Self::Binary(binary) => binary.comptime_eval(scope, handler),
 | 
					            Self::Binary(binary) => binary.comptime_eval(scope, handler),
 | 
				
			||||||
| 
						 | 
					@ -251,6 +255,7 @@ impl Expression {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl Primary {
 | 
					impl Primary {
 | 
				
			||||||
    /// Returns whether the primary can yield a certain type.
 | 
					    /// Returns whether the primary can yield a certain type.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
| 
						 | 
					@ -332,19 +337,40 @@ impl Primary {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Evaluate at compile-time.
 | 
					    /// Evaluate at compile-time.
 | 
				
			||||||
    #[must_use]
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Errors
 | 
				
			||||||
 | 
					    /// - If the expression is not compile-time evaluatable.
 | 
				
			||||||
    pub fn comptime_eval(
 | 
					    pub fn comptime_eval(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> Option<ComptimeValue> {
 | 
					    ) -> Result<ComptimeValue, NotComptime> {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
 | 
					            Self::Boolean(boolean) => Ok(ComptimeValue::Boolean(boolean.value())),
 | 
				
			||||||
            Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
 | 
					            Self::Integer(int) => Ok(ComptimeValue::Integer(int.as_i64())),
 | 
				
			||||||
            Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
 | 
					            Self::StringLiteral(string_literal) => Ok(ComptimeValue::String(
 | 
				
			||||||
                string_literal.str_content().to_string(),
 | 
					                string_literal.str_content().to_string(),
 | 
				
			||||||
            )),
 | 
					            )),
 | 
				
			||||||
            Self::Identifier(_) | Self::FunctionCall(_) | Self::Indexed(_) => None,
 | 
					            Self::Identifier(ident) => scope.get_variable(ident.span.str()).map_or_else(
 | 
				
			||||||
 | 
					                || {
 | 
				
			||||||
 | 
					                    Err(NotComptime {
 | 
				
			||||||
 | 
					                        expression: self.span(),
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                |var| match var.as_ref() {
 | 
				
			||||||
 | 
					                    VariableData::ComptimeValue { value } => {
 | 
				
			||||||
 | 
					                        value.read().unwrap().clone().ok_or_else(|| NotComptime {
 | 
				
			||||||
 | 
					                            expression: ident.span.clone(),
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    _ => Err(NotComptime {
 | 
				
			||||||
 | 
					                        expression: self.span(),
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            Self::FunctionCall(_) | Self::Indexed(_) => Err(NotComptime {
 | 
				
			||||||
 | 
					                expression: self.span(),
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
            Self::Parenthesized(parenthesized) => {
 | 
					            Self::Parenthesized(parenthesized) => {
 | 
				
			||||||
                parenthesized.expression().comptime_eval(scope, handler)
 | 
					                parenthesized.expression().comptime_eval(scope, handler)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -354,12 +380,14 @@ impl Primary {
 | 
				
			||||||
                    .comptime_eval(scope, handler)
 | 
					                    .comptime_eval(scope, handler)
 | 
				
			||||||
                    .and_then(|val| match (prefix.operator(), val) {
 | 
					                    .and_then(|val| match (prefix.operator(), val) {
 | 
				
			||||||
                        (PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
 | 
					                        (PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
 | 
				
			||||||
                            Some(ComptimeValue::Boolean(!boolean))
 | 
					                            Ok(ComptimeValue::Boolean(!boolean))
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        (PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
 | 
					                        (PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
 | 
				
			||||||
                            Some(ComptimeValue::Integer(-int))
 | 
					                            Ok(ComptimeValue::Integer(-int))
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        _ => None,
 | 
					                        _ => Err(NotComptime {
 | 
				
			||||||
 | 
					                            expression: prefix.span(),
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::Lua(lua) => lua
 | 
					            Self::Lua(lua) => lua
 | 
				
			||||||
| 
						 | 
					@ -367,25 +395,28 @@ impl Primary {
 | 
				
			||||||
                .inspect_err(|err| {
 | 
					                .inspect_err(|err| {
 | 
				
			||||||
                    handler.receive(err.clone());
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
                .ok()
 | 
					                .map_err(|_| NotComptime {
 | 
				
			||||||
                .flatten(),
 | 
					                    expression: lua.span(),
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .and_then(|val| val),
 | 
				
			||||||
            Self::MacroStringLiteral(macro_string_literal) => {
 | 
					            Self::MacroStringLiteral(macro_string_literal) => {
 | 
				
			||||||
                if macro_string_literal
 | 
					                if macro_string_literal
 | 
				
			||||||
                    .parts()
 | 
					                    .parts()
 | 
				
			||||||
                    .iter()
 | 
					                    .iter()
 | 
				
			||||||
                    .any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
 | 
					                    .any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Some(ComptimeValue::MacroString(
 | 
					                    Ok(ComptimeValue::MacroString(
 | 
				
			||||||
                        macro_string_literal.clone().into(),
 | 
					                        macro_string_literal.clone().into(),
 | 
				
			||||||
                    ))
 | 
					                    ))
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Some(ComptimeValue::String(macro_string_literal.str_content()))
 | 
					                    Ok(ComptimeValue::String(macro_string_literal.str_content()))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl Binary {
 | 
					impl Binary {
 | 
				
			||||||
    /// Returns whether the binary can yield a certain type.
 | 
					    /// Returns whether the binary can yield a certain type.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
| 
						 | 
					@ -428,12 +459,14 @@ impl Binary {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Evaluate at compile-time.
 | 
					    /// Evaluate at compile-time.
 | 
				
			||||||
    #[must_use]
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Errors
 | 
				
			||||||
 | 
					    /// - If the expression is not compile-time evaluatable.
 | 
				
			||||||
    pub fn comptime_eval(
 | 
					    pub fn comptime_eval(
 | 
				
			||||||
        &self,
 | 
					        &self,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> Option<ComptimeValue> {
 | 
					    ) -> Result<ComptimeValue, NotComptime> {
 | 
				
			||||||
        let left = self.left_operand().comptime_eval(scope, handler)?;
 | 
					        let left = self.left_operand().comptime_eval(scope, handler)?;
 | 
				
			||||||
        let right = self.right_operand().comptime_eval(scope, handler)?;
 | 
					        let right = self.right_operand().comptime_eval(scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -447,7 +480,7 @@ impl Binary {
 | 
				
			||||||
                        .right_operand()
 | 
					                        .right_operand()
 | 
				
			||||||
                        .can_yield_type(ValueType::Boolean, scope) =>
 | 
					                        .can_yield_type(ValueType::Boolean, scope) =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Some(ComptimeValue::Boolean(true))
 | 
					                Ok(ComptimeValue::Boolean(true))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            (ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
 | 
					            (ComptimeValue::Boolean(false), _) | (_, ComptimeValue::Boolean(false))
 | 
				
			||||||
                if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
 | 
					                if matches!(self.operator(), BinaryOperator::LogicalAnd(..))
 | 
				
			||||||
| 
						 | 
					@ -458,15 +491,17 @@ impl Binary {
 | 
				
			||||||
                        .right_operand()
 | 
					                        .right_operand()
 | 
				
			||||||
                        .can_yield_type(ValueType::Boolean, scope) =>
 | 
					                        .can_yield_type(ValueType::Boolean, scope) =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Some(ComptimeValue::Boolean(false))
 | 
					                Ok(ComptimeValue::Boolean(false))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            (ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
 | 
					            (ComptimeValue::Boolean(left), ComptimeValue::Boolean(right)) => {
 | 
				
			||||||
                match self.operator() {
 | 
					                match self.operator() {
 | 
				
			||||||
                    BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
 | 
					                    BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
 | 
				
			||||||
                    BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
 | 
					                    BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
 | 
				
			||||||
                    BinaryOperator::LogicalAnd(..) => Some(ComptimeValue::Boolean(left && right)),
 | 
					                    BinaryOperator::LogicalAnd(..) => Ok(ComptimeValue::Boolean(left && right)),
 | 
				
			||||||
                    BinaryOperator::LogicalOr(..) => Some(ComptimeValue::Boolean(left || right)),
 | 
					                    BinaryOperator::LogicalOr(..) => Ok(ComptimeValue::Boolean(left || right)),
 | 
				
			||||||
                    _ => None,
 | 
					                    _ => Err(NotComptime {
 | 
				
			||||||
 | 
					                        expression: self.span(),
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            (ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
 | 
					            (ComptimeValue::Integer(left), ComptimeValue::Integer(right)) => {
 | 
				
			||||||
| 
						 | 
					@ -496,14 +531,37 @@ impl Binary {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    _ => None,
 | 
					                    _ => None,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                .ok_or_else(|| NotComptime {
 | 
				
			||||||
 | 
					                    expression: self.span(),
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            (ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
 | 
					            (ComptimeValue::String(left), ComptimeValue::String(right)) => match self.operator() {
 | 
				
			||||||
                BinaryOperator::Add(..) => Some(ComptimeValue::String(left + &right)),
 | 
					                BinaryOperator::Add(..) => Ok(ComptimeValue::String(left + &right)),
 | 
				
			||||||
                BinaryOperator::Equal(..) => Some(ComptimeValue::Boolean(left == right)),
 | 
					                BinaryOperator::Equal(..) => Ok(ComptimeValue::Boolean(left == right)),
 | 
				
			||||||
                BinaryOperator::NotEqual(..) => Some(ComptimeValue::Boolean(left != right)),
 | 
					                BinaryOperator::NotEqual(..) => Ok(ComptimeValue::Boolean(left != right)),
 | 
				
			||||||
                _ => None,
 | 
					                _ => Err(NotComptime {
 | 
				
			||||||
 | 
					                    expression: self.span(),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            _ => None,
 | 
					            // TODO: also allow macro strings
 | 
				
			||||||
 | 
					            (
 | 
				
			||||||
 | 
					                left @ ComptimeValue::String(_),
 | 
				
			||||||
 | 
					                right @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            | (
 | 
				
			||||||
 | 
					                left @ (ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)),
 | 
				
			||||||
 | 
					                right @ ComptimeValue::String(_),
 | 
				
			||||||
 | 
					            ) => match self.operator() {
 | 
				
			||||||
 | 
					                BinaryOperator::Add(_) => Ok(ComptimeValue::String(
 | 
				
			||||||
 | 
					                    left.to_string_no_macro().unwrap() + &right.to_string_no_macro().unwrap(),
 | 
				
			||||||
 | 
					                )),
 | 
				
			||||||
 | 
					                _ => Err(NotComptime {
 | 
				
			||||||
 | 
					                    expression: self.span(),
 | 
				
			||||||
 | 
					                }),
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            _ => Err(NotComptime {
 | 
				
			||||||
 | 
					                expression: self.span(),
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -639,7 +697,7 @@ impl Transpiler {
 | 
				
			||||||
            Primary::Lua(lua) =>
 | 
					            Primary::Lua(lua) =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                #[expect(clippy::option_if_let_else)]
 | 
					                #[expect(clippy::option_if_let_else)]
 | 
				
			||||||
                if let Some(value) = lua.eval_comptime(scope, handler)? {
 | 
					                if let Ok(value) = lua.eval_comptime(scope, handler)? {
 | 
				
			||||||
                    self.store_comptime_value(&value, target, lua, handler)
 | 
					                    self.store_comptime_value(&value, target, lua, handler)
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    let err = TranspileError::MissingValue(MissingValue {
 | 
					                    let err = TranspileError::MissingValue(MissingValue {
 | 
				
			||||||
| 
						 | 
					@ -801,7 +859,7 @@ impl Transpiler {
 | 
				
			||||||
                if let Some(variable) = variable.as_deref() {
 | 
					                if let Some(variable) = variable.as_deref() {
 | 
				
			||||||
                    let from = match variable {
 | 
					                    let from = match variable {
 | 
				
			||||||
                        VariableData::Scoreboard { objective } => {
 | 
					                        VariableData::Scoreboard { objective } => {
 | 
				
			||||||
                            if let Some(ComptimeValue::String(target)) =
 | 
					                            if let Ok(ComptimeValue::String(target)) =
 | 
				
			||||||
                                indexed.index().comptime_eval(scope, handler)
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                Ok(DataLocation::ScoreboardValue {
 | 
					                                Ok(DataLocation::ScoreboardValue {
 | 
				
			||||||
| 
						 | 
					@ -820,7 +878,7 @@ impl Transpiler {
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        VariableData::ScoreboardArray { objective, targets } => {
 | 
					                        VariableData::ScoreboardArray { objective, targets } => {
 | 
				
			||||||
                            if let Some(ComptimeValue::Integer(index)) =
 | 
					                            if let Ok(ComptimeValue::Integer(index)) =
 | 
				
			||||||
                                indexed.index().comptime_eval(scope, handler)
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                if let Some(target) = usize::try_from(index)
 | 
					                                if let Some(target) = usize::try_from(index)
 | 
				
			||||||
| 
						 | 
					@ -858,7 +916,7 @@ impl Transpiler {
 | 
				
			||||||
                            storage_name,
 | 
					                            storage_name,
 | 
				
			||||||
                            paths,
 | 
					                            paths,
 | 
				
			||||||
                        } => {
 | 
					                        } => {
 | 
				
			||||||
                            if let Some(ComptimeValue::Integer(index)) =
 | 
					                            if let Ok(ComptimeValue::Integer(index)) =
 | 
				
			||||||
                                indexed.index().comptime_eval(scope, handler)
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                if let Some(path) = usize::try_from(index)
 | 
					                                if let Some(path) = usize::try_from(index)
 | 
				
			||||||
| 
						 | 
					@ -915,14 +973,14 @@ impl Transpiler {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn transpile_binary_expression(
 | 
					    pub(super) fn transpile_binary_expression(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        binary: &Binary,
 | 
					        binary: &Binary,
 | 
				
			||||||
        target: &DataLocation,
 | 
					        target: &DataLocation,
 | 
				
			||||||
        scope: &Arc<super::Scope>,
 | 
					        scope: &Arc<super::Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<Vec<Command>> {
 | 
					    ) -> TranspileResult<Vec<Command>> {
 | 
				
			||||||
        if let Some(value) = binary.comptime_eval(scope, handler) {
 | 
					        if let Ok(value) = binary.comptime_eval(scope, handler) {
 | 
				
			||||||
            self.store_comptime_value(&value, target, binary, handler)
 | 
					            self.store_comptime_value(&value, target, binary, handler)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            match binary.operator() {
 | 
					            match binary.operator() {
 | 
				
			||||||
| 
						 | 
					@ -1085,7 +1143,7 @@ impl Transpiler {
 | 
				
			||||||
                            storage_name,
 | 
					                            storage_name,
 | 
				
			||||||
                            paths,
 | 
					                            paths,
 | 
				
			||||||
                        } => {
 | 
					                        } => {
 | 
				
			||||||
                            if let Some(ComptimeValue::Integer(index)) =
 | 
					                            if let Ok(ComptimeValue::Integer(index)) =
 | 
				
			||||||
                                indexed.index().comptime_eval(scope, handler)
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                if let Some(path) = usize::try_from(index)
 | 
					                                if let Some(path) = usize::try_from(index)
 | 
				
			||||||
| 
						 | 
					@ -1166,15 +1224,15 @@ impl Transpiler {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
 | 
					            Primary::Lua(lua) => match lua.eval_comptime(scope, handler)? {
 | 
				
			||||||
                Some(ComptimeValue::String(value)) => Ok((
 | 
					                Ok(ComptimeValue::String(value)) => Ok((
 | 
				
			||||||
                    Vec::new(),
 | 
					                    Vec::new(),
 | 
				
			||||||
                    ExtendedCondition::Runtime(Condition::Atom(value.into())),
 | 
					                    ExtendedCondition::Runtime(Condition::Atom(value.into())),
 | 
				
			||||||
                )),
 | 
					                )),
 | 
				
			||||||
                Some(ComptimeValue::MacroString(value)) => Ok((
 | 
					                Ok(ComptimeValue::MacroString(value)) => Ok((
 | 
				
			||||||
                    Vec::new(),
 | 
					                    Vec::new(),
 | 
				
			||||||
                    ExtendedCondition::Runtime(Condition::Atom(value.into())),
 | 
					                    ExtendedCondition::Runtime(Condition::Atom(value.into())),
 | 
				
			||||||
                )),
 | 
					                )),
 | 
				
			||||||
                Some(ComptimeValue::Boolean(boolean)) => {
 | 
					                Ok(ComptimeValue::Boolean(boolean)) => {
 | 
				
			||||||
                    Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
 | 
					                    Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                _ => {
 | 
					                _ => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,6 +102,7 @@ impl Transpiler {
 | 
				
			||||||
                    TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
 | 
					                    TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
 | 
				
			||||||
                    TranspileAnnotationValue::Expression(expr) => expr
 | 
					                    TranspileAnnotationValue::Expression(expr) => expr
 | 
				
			||||||
                        .comptime_eval(scope, handler)
 | 
					                        .comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                        .ok()
 | 
				
			||||||
                        .and_then(|val| val.to_string_no_macro())
 | 
					                        .and_then(|val| val.to_string_no_macro())
 | 
				
			||||||
                        .ok_or_else(|| {
 | 
					                        .ok_or_else(|| {
 | 
				
			||||||
                            let err = TranspileError::IllegalAnnotationContent(
 | 
					                            let err = TranspileError::IllegalAnnotationContent(
 | 
				
			||||||
| 
						 | 
					@ -293,13 +294,10 @@ impl Transpiler {
 | 
				
			||||||
                    let value = match expression {
 | 
					                    let value = match expression {
 | 
				
			||||||
                        Expression::Primary(Primary::Lua(lua)) => {
 | 
					                        Expression::Primary(Primary::Lua(lua)) => {
 | 
				
			||||||
                            lua.eval_comptime(scope, handler).and_then(|val| match val {
 | 
					                            lua.eval_comptime(scope, handler).and_then(|val| match val {
 | 
				
			||||||
                                Some(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
 | 
					                                Ok(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
 | 
				
			||||||
                                Some(val) => Ok(Parameter::Static(val.to_macro_string())),
 | 
					                                Ok(val) => Ok(Parameter::Static(val.to_macro_string())),
 | 
				
			||||||
                                None => {
 | 
					                                Err(err) => {
 | 
				
			||||||
                                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                                    let err = TranspileError::NotComptime(err);
 | 
				
			||||||
                                        expression: expression.span(),
 | 
					 | 
				
			||||||
                                        expected_type: ExpectedType::String,
 | 
					 | 
				
			||||||
                                    });
 | 
					 | 
				
			||||||
                                    handler.receive(err.clone());
 | 
					                                    handler.receive(err.clone());
 | 
				
			||||||
                                    Err(err)
 | 
					                                    Err(err)
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
| 
						 | 
					@ -361,7 +359,18 @@ impl Transpiler {
 | 
				
			||||||
                                        path: std::mem::take(&mut temp_path[0]),
 | 
					                                        path: std::mem::take(&mut temp_path[0]),
 | 
				
			||||||
                                    })
 | 
					                                    })
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                                _ => todo!("other variable types"),
 | 
					                                _ => {
 | 
				
			||||||
 | 
					                                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                                        expression: expression.span(),
 | 
				
			||||||
 | 
					                                        expected_type: ExpectedType::AnyOf(vec![
 | 
				
			||||||
 | 
					                                            ExpectedType::Integer,
 | 
				
			||||||
 | 
					                                            ExpectedType::Boolean,
 | 
				
			||||||
 | 
					                                            ExpectedType::String,
 | 
				
			||||||
 | 
					                                        ]),
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
 | 
					                                    handler.receive(err.clone());
 | 
				
			||||||
 | 
					                                    Err(err)
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Expression::Primary(
 | 
					                        Expression::Primary(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ use std::{
 | 
				
			||||||
    sync::Arc,
 | 
					    sync::Arc,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use cfg_if::cfg_if;
 | 
				
			||||||
use shulkerbox::prelude::{Command, Execute};
 | 
					use shulkerbox::prelude::{Command, Execute};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use serde_json::{json, Value as JsonValue};
 | 
					use serde_json::{json, Value as JsonValue};
 | 
				
			||||||
| 
						 | 
					@ -15,8 +16,8 @@ use crate::{
 | 
				
			||||||
    semantic::error::{InvalidFunctionArguments, UnexpectedExpression},
 | 
					    semantic::error::{InvalidFunctionArguments, UnexpectedExpression},
 | 
				
			||||||
    syntax::syntax_tree::expression::{Expression, FunctionCall, Primary},
 | 
					    syntax::syntax_tree::expression::{Expression, FunctionCall, Primary},
 | 
				
			||||||
    transpile::{
 | 
					    transpile::{
 | 
				
			||||||
        error::{IllegalIndexing, IllegalIndexingReason, LuaRuntimeError, UnknownIdentifier},
 | 
					        error::{IllegalIndexing, IllegalIndexingReason, UnknownIdentifier},
 | 
				
			||||||
        expression::{ComptimeValue, DataLocation, StorageType},
 | 
					        expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
 | 
				
			||||||
        util::MacroString,
 | 
					        util::MacroString,
 | 
				
			||||||
        TranspileError,
 | 
					        TranspileError,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -182,17 +183,17 @@ fn print_function(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let args = get_args_assert_in_range(call, 1..=2)?;
 | 
					    let args = get_args_assert_in_range(call, 1..=2)?;
 | 
				
			||||||
    let first = args.first().expect("checked range");
 | 
					    let first = args.first().expect("checked range");
 | 
				
			||||||
    let (target, message_expression) = args.get(1).map_or_else(
 | 
					    let (target, message_expression) = if let Some(second) = args.get(1) {
 | 
				
			||||||
        || ("@a".into(), first),
 | 
					        (
 | 
				
			||||||
        |second| {
 | 
					            first
 | 
				
			||||||
            (
 | 
					                .comptime_eval(scope, &VoidHandler)
 | 
				
			||||||
                first
 | 
					                .map(|val| val.to_macro_string())
 | 
				
			||||||
                    .comptime_eval(scope, &VoidHandler)
 | 
					                .map_err(TranspileError::NotComptime)?,
 | 
				
			||||||
                    .map_or_else(|| "@a".into(), |val| val.to_macro_string()),
 | 
					            second,
 | 
				
			||||||
                second,
 | 
					        )
 | 
				
			||||||
            )
 | 
					    } else {
 | 
				
			||||||
        },
 | 
					        ("@a".into(), first)
 | 
				
			||||||
    );
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut contains_macro = matches!(target, MacroString::MacroString(_));
 | 
					    let mut contains_macro = matches!(target, MacroString::MacroString(_));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -210,17 +211,24 @@ fn print_function(
 | 
				
			||||||
                Vec::new(),
 | 
					                Vec::new(),
 | 
				
			||||||
                vec![JsonValue::String(string.str_content().to_string())],
 | 
					                vec![JsonValue::String(string.str_content().to_string())],
 | 
				
			||||||
            )),
 | 
					            )),
 | 
				
			||||||
 | 
					            #[cfg_attr(not(feature = "lua"), expect(unused_variables))]
 | 
				
			||||||
            Primary::Lua(lua) => {
 | 
					            Primary::Lua(lua) => {
 | 
				
			||||||
                let (ret, _lua) = lua.eval(scope, &VoidHandler)?;
 | 
					                cfg_if! {
 | 
				
			||||||
                Ok((
 | 
					                    if #[cfg(feature = "lua")] {
 | 
				
			||||||
                    Vec::new(),
 | 
					                        let (ret, _lua) = lua.eval(scope, &VoidHandler)?;
 | 
				
			||||||
                    vec![JsonValue::String(ret.to_string().map_err(|err| {
 | 
					                        Ok((
 | 
				
			||||||
                        TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err(
 | 
					                            Vec::new(),
 | 
				
			||||||
                            &err,
 | 
					                            vec![JsonValue::String(ret.to_string().map_err(|err| {
 | 
				
			||||||
                            lua.span(),
 | 
					                                TranspileError::LuaRuntimeError(super::error::LuaRuntimeError::from_lua_err(
 | 
				
			||||||
 | 
					                                    &err,
 | 
				
			||||||
 | 
					                                    lua.span(),
 | 
				
			||||||
 | 
					                                ))
 | 
				
			||||||
 | 
					                            })?)],
 | 
				
			||||||
                        ))
 | 
					                        ))
 | 
				
			||||||
                    })?)],
 | 
					                    } else {
 | 
				
			||||||
                ))
 | 
					                        Err(TranspileError::LuaDisabled)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Primary::Identifier(ident) => {
 | 
					            Primary::Identifier(ident) => {
 | 
				
			||||||
                let (cur_contains_macro, cmd, part) =
 | 
					                let (cur_contains_macro, cmd, part) =
 | 
				
			||||||
| 
						 | 
					@ -232,7 +240,7 @@ fn print_function(
 | 
				
			||||||
                Primary::Identifier(ident) => {
 | 
					                Primary::Identifier(ident) => {
 | 
				
			||||||
                    match scope.get_variable(ident.span.str()).as_deref() {
 | 
					                    match scope.get_variable(ident.span.str()).as_deref() {
 | 
				
			||||||
                        Some(VariableData::Scoreboard { objective }) => {
 | 
					                        Some(VariableData::Scoreboard { objective }) => {
 | 
				
			||||||
                            if let Some(ComptimeValue::String(index)) =
 | 
					                            if let Ok(ComptimeValue::String(index)) =
 | 
				
			||||||
                                indexed.index().comptime_eval(scope, &VoidHandler)
 | 
					                                indexed.index().comptime_eval(scope, &VoidHandler)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                let (cmd, value) = get_data_location(
 | 
					                                let (cmd, value) = get_data_location(
 | 
				
			||||||
| 
						 | 
					@ -244,11 +252,17 @@ fn print_function(
 | 
				
			||||||
                                );
 | 
					                                );
 | 
				
			||||||
                                Ok((cmd.into_iter().collect(), vec![value]))
 | 
					                                Ok((cmd.into_iter().collect(), vec![value]))
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                todo!("allow macro string, but throw error when index is not constant string")
 | 
					                                // TODO: allow macro string, but throw error when index is not constant string
 | 
				
			||||||
 | 
					                                Err(TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
 | 
					                                    reason: IllegalIndexingReason::InvalidComptimeType {
 | 
				
			||||||
 | 
					                                        expected: ExpectedType::String,
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                    expression: indexed.index().span(),
 | 
				
			||||||
 | 
					                                }))
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Some(VariableData::ScoreboardArray { objective, targets }) => {
 | 
					                        Some(VariableData::ScoreboardArray { objective, targets }) => {
 | 
				
			||||||
                            if let Some(ComptimeValue::Integer(index)) =
 | 
					                            if let Ok(ComptimeValue::Integer(index)) =
 | 
				
			||||||
                                indexed.index().comptime_eval(scope, &VoidHandler)
 | 
					                                indexed.index().comptime_eval(scope, &VoidHandler)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                #[expect(clippy::option_if_let_else)]
 | 
					                                #[expect(clippy::option_if_let_else)]
 | 
				
			||||||
| 
						 | 
					@ -274,14 +288,19 @@ fn print_function(
 | 
				
			||||||
                                    }))
 | 
					                                    }))
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                todo!("throw error when index is not constant integer")
 | 
					                                Err(TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
 | 
					                                    reason: IllegalIndexingReason::InvalidComptimeType {
 | 
				
			||||||
 | 
					                                        expected: ExpectedType::Integer,
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                    expression: indexed.index().span(),
 | 
				
			||||||
 | 
					                                }))
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Some(VariableData::BooleanStorageArray {
 | 
					                        Some(VariableData::BooleanStorageArray {
 | 
				
			||||||
                            storage_name,
 | 
					                            storage_name,
 | 
				
			||||||
                            paths,
 | 
					                            paths,
 | 
				
			||||||
                        }) => {
 | 
					                        }) => {
 | 
				
			||||||
                            if let Some(ComptimeValue::Integer(index)) =
 | 
					                            if let Ok(ComptimeValue::Integer(index)) =
 | 
				
			||||||
                                indexed.index().comptime_eval(scope, &VoidHandler)
 | 
					                                indexed.index().comptime_eval(scope, &VoidHandler)
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                #[expect(clippy::option_if_let_else)]
 | 
					                                #[expect(clippy::option_if_let_else)]
 | 
				
			||||||
| 
						 | 
					@ -308,10 +327,18 @@ fn print_function(
 | 
				
			||||||
                                    }))
 | 
					                                    }))
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            } else {
 | 
					                            } else {
 | 
				
			||||||
                                todo!("throw error when index is not constant integer")
 | 
					                                Err(TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
 | 
					                                    reason: IllegalIndexingReason::InvalidComptimeType {
 | 
				
			||||||
 | 
					                                        expected: ExpectedType::Integer,
 | 
				
			||||||
 | 
					                                    },
 | 
				
			||||||
 | 
					                                    expression: indexed.index().span(),
 | 
				
			||||||
 | 
					                                }))
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        _ => todo!("catch illegal indexing"),
 | 
					                        _ => Err(TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
 | 
					                            reason: IllegalIndexingReason::NotIndexable,
 | 
				
			||||||
 | 
					                            expression: indexed.object().span(),
 | 
				
			||||||
 | 
					                        })),
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                _ => Err(TranspileError::IllegalIndexing(IllegalIndexing {
 | 
					                _ => Err(TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
| 
						 | 
					@ -339,9 +366,43 @@ fn print_function(
 | 
				
			||||||
                Ok((cmds, parts))
 | 
					                Ok((cmds, parts))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _ => todo!("print_function Primary"),
 | 
					            primary => {
 | 
				
			||||||
 | 
					                let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1);
 | 
				
			||||||
 | 
					                let location = DataLocation::Storage {
 | 
				
			||||||
 | 
					                    storage_name,
 | 
				
			||||||
 | 
					                    path: std::mem::take(&mut storage_paths[0]),
 | 
				
			||||||
 | 
					                    r#type: StorageType::Int,
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                let cmds = transpiler.transpile_primary_expression(
 | 
				
			||||||
 | 
					                    primary,
 | 
				
			||||||
 | 
					                    &location,
 | 
				
			||||||
 | 
					                    scope,
 | 
				
			||||||
 | 
					                    &VoidHandler,
 | 
				
			||||||
 | 
					                )?;
 | 
				
			||||||
 | 
					                let (cmd, part) = get_data_location(&location, transpiler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Ok((
 | 
				
			||||||
 | 
					                    cmds.into_iter().chain(cmd.into_iter()).collect(),
 | 
				
			||||||
 | 
					                    vec![part],
 | 
				
			||||||
 | 
					                ))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        Expression::Binary(_) => todo!("print_function Binary"),
 | 
					        Expression::Binary(binary) => {
 | 
				
			||||||
 | 
					            let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1);
 | 
				
			||||||
 | 
					            let location = DataLocation::Storage {
 | 
				
			||||||
 | 
					                storage_name,
 | 
				
			||||||
 | 
					                path: std::mem::take(&mut storage_paths[0]),
 | 
				
			||||||
 | 
					                r#type: StorageType::Int,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            let cmds =
 | 
				
			||||||
 | 
					                transpiler.transpile_binary_expression(binary, &location, scope, &VoidHandler)?;
 | 
				
			||||||
 | 
					            let (cmd, part) = get_data_location(&location, transpiler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok((
 | 
				
			||||||
 | 
					                cmds.into_iter().chain(cmd.into_iter()).collect(),
 | 
				
			||||||
 | 
					                vec![part],
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }?;
 | 
					    }?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO: prepend prefix with datapack name to parts and remove following
 | 
					    // TODO: prepend prefix with datapack name to parts and remove following
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
//! Executes the Lua code and returns the resulting command.
 | 
					//! Executes the Lua code and returns the resulting command.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "lua")]
 | 
					#[cfg(all(feature = "lua", feature = "shulkerbox"))]
 | 
				
			||||||
mod enabled {
 | 
					mod enabled {
 | 
				
			||||||
    use std::sync::Arc;
 | 
					    use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,8 +12,8 @@ mod enabled {
 | 
				
			||||||
        syntax::syntax_tree::expression::LuaCode,
 | 
					        syntax::syntax_tree::expression::LuaCode,
 | 
				
			||||||
        transpile::{
 | 
					        transpile::{
 | 
				
			||||||
            error::{
 | 
					            error::{
 | 
				
			||||||
                LuaRuntimeError, MismatchedTypes, TranspileError, TranspileResult,
 | 
					                InvalidArgument, LuaRuntimeError, MismatchedTypes, NotComptime, TranspileError,
 | 
				
			||||||
                UnknownIdentifier,
 | 
					                TranspileResult, UnknownIdentifier,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            expression::{ComptimeValue, ExpectedType},
 | 
					            expression::{ComptimeValue, ExpectedType},
 | 
				
			||||||
            Scope, VariableData,
 | 
					            Scope, VariableData,
 | 
				
			||||||
| 
						 | 
					@ -86,11 +86,15 @@ mod enabled {
 | 
				
			||||||
            &self,
 | 
					            &self,
 | 
				
			||||||
            scope: &Arc<Scope>,
 | 
					            scope: &Arc<Scope>,
 | 
				
			||||||
            handler: &impl Handler<base::Error>,
 | 
					            handler: &impl Handler<base::Error>,
 | 
				
			||||||
        ) -> TranspileResult<Option<ComptimeValue>> {
 | 
					        ) -> TranspileResult<Result<ComptimeValue, NotComptime>> {
 | 
				
			||||||
            // required to keep the lua instance alive
 | 
					            // required to keep the lua instance alive
 | 
				
			||||||
            let (lua_result, _lua) = self.eval(scope, handler)?;
 | 
					            let (lua_result, _lua) = self.eval(scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.handle_lua_result(lua_result, handler)
 | 
					            self.handle_lua_result(lua_result, handler).map(|res| {
 | 
				
			||||||
 | 
					                res.ok_or_else(|| NotComptime {
 | 
				
			||||||
 | 
					                    expression: self.span(),
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
 | 
					        fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> TranspileResult<()> {
 | 
				
			||||||
| 
						 | 
					@ -262,8 +266,28 @@ mod enabled {
 | 
				
			||||||
                        .map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
 | 
					                        .map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
 | 
				
			||||||
                    Value::Table(table)
 | 
					                    Value::Table(table)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                Some(VariableData::ComptimeValue { value }) => {
 | 
				
			||||||
 | 
					                    let value = value.read().unwrap();
 | 
				
			||||||
 | 
					                    match &*value {
 | 
				
			||||||
 | 
					                        Some(ComptimeValue::Boolean(b)) => Value::Boolean(*b),
 | 
				
			||||||
 | 
					                        Some(ComptimeValue::Integer(i)) => Value::Integer(*i),
 | 
				
			||||||
 | 
					                        Some(ComptimeValue::String(s)) => Value::String(
 | 
				
			||||||
 | 
					                            lua.create_string(s)
 | 
				
			||||||
 | 
					                                .map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                        Some(ComptimeValue::MacroString(s)) => Value::String(
 | 
				
			||||||
 | 
					                            lua.create_string(s.to_string())
 | 
				
			||||||
 | 
					                                .map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?,
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                        None => Value::Nil,
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
 | 
					                Some(VariableData::Function { .. } | VariableData::InternalFunction { .. }) => {
 | 
				
			||||||
                    todo!("(internal) functions are not supported yet");
 | 
					                    // TODO: add support for functions
 | 
				
			||||||
 | 
					                    return Err(TranspileError::InvalidArgument(InvalidArgument {
 | 
				
			||||||
 | 
					                        reason: "functions cannot be passed to Lua".to_string(),
 | 
				
			||||||
 | 
					                        span: identifier.span(),
 | 
				
			||||||
 | 
					                    }));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                None => {
 | 
					                None => {
 | 
				
			||||||
                    return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
					                    return Err(TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
				
			||||||
| 
						 | 
					@ -374,7 +398,7 @@ mod enabled {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(not(feature = "lua"))]
 | 
					#[cfg(all(not(feature = "lua"), feature = "shulkerbox"))]
 | 
				
			||||||
mod disabled {
 | 
					mod disabled {
 | 
				
			||||||
    use std::sync::Arc;
 | 
					    use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -397,7 +421,7 @@ mod disabled {
 | 
				
			||||||
            &self,
 | 
					            &self,
 | 
				
			||||||
            scope: &Arc<Scope>,
 | 
					            scope: &Arc<Scope>,
 | 
				
			||||||
            handler: &impl Handler<base::Error>,
 | 
					            handler: &impl Handler<base::Error>,
 | 
				
			||||||
        ) -> TranspileResult<()> {
 | 
					        ) -> TranspileResult<((), ())> {
 | 
				
			||||||
            let _ = scope;
 | 
					            let _ = scope;
 | 
				
			||||||
            handler.receive(TranspileError::LuaDisabled);
 | 
					            handler.receive(TranspileError::LuaDisabled);
 | 
				
			||||||
            tracing::error!("Lua code evaluation is disabled");
 | 
					            tracing::error!("Lua code evaluation is disabled");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,7 @@ use crate::{
 | 
				
			||||||
#[doc(hidden)]
 | 
					#[doc(hidden)]
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
pub mod conversions;
 | 
					pub mod conversions;
 | 
				
			||||||
mod error;
 | 
					pub mod error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod expression;
 | 
					pub mod expression;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,9 +38,12 @@ pub mod internal_functions;
 | 
				
			||||||
#[doc(hidden)]
 | 
					#[doc(hidden)]
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
pub mod function;
 | 
					pub mod function;
 | 
				
			||||||
 | 
					#[doc(inline)]
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
pub use function::TranspiledFunctionArguments;
 | 
					pub use function::TranspiledFunctionArguments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod variables;
 | 
					mod variables;
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
pub use variables::{Scope, VariableData};
 | 
					pub use variables::{Scope, VariableData};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub mod util;
 | 
					pub mod util;
 | 
				
			||||||
| 
						 | 
					@ -58,7 +61,7 @@ pub struct FunctionData {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Possible values for an annotation.
 | 
					/// Possible values for an annotation.
 | 
				
			||||||
#[expect(clippy::module_name_repetitions)]
 | 
					#[expect(clippy::module_name_repetitions)]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, EnumIs)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, EnumIs)]
 | 
				
			||||||
pub enum TranspileAnnotationValue {
 | 
					pub enum TranspileAnnotationValue {
 | 
				
			||||||
    /// No value.
 | 
					    /// No value.
 | 
				
			||||||
    None,
 | 
					    None,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ use crate::{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    error::{MismatchedTypes, TranspileError, TranspileResult},
 | 
					    error::{MismatchedTypes, MissingValue, TranspileError, TranspileResult, UnknownIdentifier},
 | 
				
			||||||
    expression::{ComptimeValue, ExpectedType, ExtendedCondition},
 | 
					    expression::{ComptimeValue, ExpectedType, ExtendedCondition},
 | 
				
			||||||
    variables::{Scope, TranspileAssignmentTarget, VariableData},
 | 
					    variables::{Scope, TranspileAssignmentTarget, VariableData},
 | 
				
			||||||
    FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
 | 
					    FunctionData, TranspileAnnotationValue, TranspiledFunctionArguments,
 | 
				
			||||||
| 
						 | 
					@ -342,11 +342,46 @@ impl Transpiler {
 | 
				
			||||||
            Expression::Primary(Primary::FunctionCall(func)) => {
 | 
					            Expression::Primary(Primary::FunctionCall(func)) => {
 | 
				
			||||||
                self.transpile_function_call(func, scope, handler)
 | 
					                self.transpile_function_call(func, scope, handler)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Expression::Primary(Primary::Identifier(ident)) => {
 | 
				
			||||||
 | 
					                match scope.get_variable(ident.span.str()).as_deref() {
 | 
				
			||||||
 | 
					                    Some(VariableData::ComptimeValue { value }) => {
 | 
				
			||||||
 | 
					                        value.read().unwrap().as_ref().map_or_else(
 | 
				
			||||||
 | 
					                            || {
 | 
				
			||||||
 | 
					                                let error = TranspileError::MissingValue(MissingValue {
 | 
				
			||||||
 | 
					                                    expression: ident.span.clone(),
 | 
				
			||||||
 | 
					                                });
 | 
				
			||||||
 | 
					                                handler.receive(error.clone());
 | 
				
			||||||
 | 
					                                Err(error)
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            |val| {
 | 
				
			||||||
 | 
					                                let cmd = val.to_string_no_macro().map_or_else(
 | 
				
			||||||
 | 
					                                    || Command::UsesMacro(val.to_macro_string().into()),
 | 
				
			||||||
 | 
					                                    Command::Raw,
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					                                Ok(vec![cmd])
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Some(_) => {
 | 
				
			||||||
 | 
					                        let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
 | 
				
			||||||
 | 
					                            expression.clone(),
 | 
				
			||||||
 | 
					                        ));
 | 
				
			||||||
 | 
					                        handler.receive(error.clone());
 | 
				
			||||||
 | 
					                        Err(error)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    None => {
 | 
				
			||||||
 | 
					                        let error = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
				
			||||||
 | 
					                            identifier: ident.span.clone(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        handler.receive(error.clone());
 | 
				
			||||||
 | 
					                        Err(error)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            expression @ Expression::Primary(
 | 
					            expression @ Expression::Primary(
 | 
				
			||||||
                Primary::Integer(_)
 | 
					                Primary::Integer(_)
 | 
				
			||||||
                | Primary::Boolean(_)
 | 
					                | Primary::Boolean(_)
 | 
				
			||||||
                | Primary::Prefix(_)
 | 
					                | Primary::Prefix(_)
 | 
				
			||||||
                | Primary::Identifier(_)
 | 
					 | 
				
			||||||
                | Primary::Indexed(_),
 | 
					                | Primary::Indexed(_),
 | 
				
			||||||
            ) => {
 | 
					            ) => {
 | 
				
			||||||
                let error =
 | 
					                let error =
 | 
				
			||||||
| 
						 | 
					@ -361,9 +396,9 @@ impl Transpiler {
 | 
				
			||||||
                Ok(vec![Command::UsesMacro(string.into())])
 | 
					                Ok(vec![Command::UsesMacro(string.into())])
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
 | 
					            Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
 | 
				
			||||||
                Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
					                Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
				
			||||||
                Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
 | 
					                Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
 | 
				
			||||||
                Some(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
 | 
					                Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
 | 
				
			||||||
                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
                        expected_type: ExpectedType::String,
 | 
					                        expected_type: ExpectedType::String,
 | 
				
			||||||
                        expression: code.span(),
 | 
					                        expression: code.span(),
 | 
				
			||||||
| 
						 | 
					@ -371,7 +406,13 @@ impl Transpiler {
 | 
				
			||||||
                    handler.receive(err.clone());
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
                    Err(err)
 | 
					                    Err(err)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                None => Ok(Vec::new()),
 | 
					                Err(_) => {
 | 
				
			||||||
 | 
					                    let err = TranspileError::MissingValue(MissingValue {
 | 
				
			||||||
 | 
					                        expression: code.span(),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
 | 
					                    Err(err)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Expression::Primary(Primary::Parenthesized(parenthesized)) => self
 | 
					            Expression::Primary(Primary::Parenthesized(parenthesized)) => self
 | 
				
			||||||
| 
						 | 
					@ -382,8 +423,8 @@ impl Transpiler {
 | 
				
			||||||
                    handler,
 | 
					                    handler,
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
            Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
 | 
					            Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
 | 
				
			||||||
                Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
					                Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
 | 
				
			||||||
                Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
 | 
					                Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
 | 
				
			||||||
                _ => {
 | 
					                _ => {
 | 
				
			||||||
                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
                        expression: bin.span(),
 | 
					                        expression: bin.span(),
 | 
				
			||||||
| 
						 | 
					@ -621,7 +662,7 @@ impl Transpiler {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
 | 
					        if let Ok(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
 | 
				
			||||||
            if value {
 | 
					            if value {
 | 
				
			||||||
                Ok(Some((Vec::new(), then)))
 | 
					                Ok(Some((Vec::new(), then)))
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,17 +30,17 @@ use crate::{
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    error::{
 | 
					    error::{
 | 
				
			||||||
        AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
 | 
					        AssignmentError, IllegalAnnotationContent, IllegalIndexing, IllegalIndexingReason,
 | 
				
			||||||
        MismatchedTypes,
 | 
					        MismatchedTypes, NotComptime,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
 | 
					    expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
 | 
				
			||||||
    internal_functions::InternalFunction,
 | 
					 | 
				
			||||||
    FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
 | 
					    FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
use super::Transpiler;
 | 
					use super::{internal_functions::InternalFunction, Transpiler};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Stores the data required to access a variable.
 | 
					/// Stores the data required to access a variable.
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
#[derive(Debug, Clone, EnumAsInner)]
 | 
					#[derive(Debug, Clone, EnumAsInner)]
 | 
				
			||||||
pub enum VariableData {
 | 
					pub enum VariableData {
 | 
				
			||||||
    /// A function.
 | 
					    /// A function.
 | 
				
			||||||
| 
						 | 
					@ -100,6 +100,11 @@ pub enum VariableData {
 | 
				
			||||||
        /// The implementation
 | 
					        /// The implementation
 | 
				
			||||||
        implementation: InternalFunction,
 | 
					        implementation: InternalFunction,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    /// Compiler internal variable.
 | 
				
			||||||
 | 
					    ComptimeValue {
 | 
				
			||||||
 | 
					        /// The value.
 | 
				
			||||||
 | 
					        value: Arc<RwLock<Option<ComptimeValue>>>,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, Copy, EnumAsInner)]
 | 
					#[derive(Debug, Clone, Copy, EnumAsInner)]
 | 
				
			||||||
| 
						 | 
					@ -122,6 +127,7 @@ impl<'a> From<&'a AssignmentDestination> for TranspileAssignmentTarget<'a> {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A scope that stores variables.
 | 
					/// A scope that stores variables.
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
pub struct Scope<'a> {
 | 
					pub struct Scope<'a> {
 | 
				
			||||||
    /// Parent scope where variables are inherited from.
 | 
					    /// Parent scope where variables are inherited from.
 | 
				
			||||||
| 
						 | 
					@ -132,6 +138,7 @@ pub struct Scope<'a> {
 | 
				
			||||||
    shadowed: RwLock<HashMap<String, usize>>,
 | 
					    shadowed: RwLock<HashMap<String, usize>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl<'a> Scope<'a> {
 | 
					impl<'a> Scope<'a> {
 | 
				
			||||||
    /// Creates a new scope.
 | 
					    /// Creates a new scope.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
| 
						 | 
					@ -226,6 +233,7 @@ impl<'a> Scope<'a> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl Debug for Scope<'_> {
 | 
					impl Debug for Scope<'_> {
 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
        struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
 | 
					        struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
 | 
				
			||||||
| 
						 | 
					@ -279,6 +287,31 @@ impl Transpiler {
 | 
				
			||||||
                scope,
 | 
					                scope,
 | 
				
			||||||
                handler,
 | 
					                handler,
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
 | 
					            VariableDeclaration::ComptimeValue(declaration) => {
 | 
				
			||||||
 | 
					                let value = if let Some(assignment) = declaration.assignment() {
 | 
				
			||||||
 | 
					                    Some(
 | 
				
			||||||
 | 
					                        assignment
 | 
				
			||||||
 | 
					                            .expression()
 | 
				
			||||||
 | 
					                            .comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                            .map_err(|err| {
 | 
				
			||||||
 | 
					                                let err = TranspileError::NotComptime(err);
 | 
				
			||||||
 | 
					                                handler.receive(err.clone());
 | 
				
			||||||
 | 
					                                err
 | 
				
			||||||
 | 
					                            })?,
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    None
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                scope.set_variable(
 | 
				
			||||||
 | 
					                    declaration.identifier().span.str(),
 | 
				
			||||||
 | 
					                    VariableData::ComptimeValue {
 | 
				
			||||||
 | 
					                        value: Arc::new(RwLock::new(value)),
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Ok(Vec::new())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -463,7 +496,7 @@ impl Transpiler {
 | 
				
			||||||
        let (identifier, indexing_value) = match destination {
 | 
					        let (identifier, indexing_value) = match destination {
 | 
				
			||||||
            TranspileAssignmentTarget::Identifier(ident) => (ident, None),
 | 
					            TranspileAssignmentTarget::Identifier(ident) => (ident, None),
 | 
				
			||||||
            TranspileAssignmentTarget::Indexed(ident, expression) => {
 | 
					            TranspileAssignmentTarget::Indexed(ident, expression) => {
 | 
				
			||||||
                (ident, expression.comptime_eval(scope, handler))
 | 
					                (ident, expression.comptime_eval(scope, handler).ok())
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        if let Some(target) = scope.get_variable(identifier.span.str()) {
 | 
					        if let Some(target) = scope.get_variable(identifier.span.str()) {
 | 
				
			||||||
| 
						 | 
					@ -489,7 +522,15 @@ impl Transpiler {
 | 
				
			||||||
                        target: s,
 | 
					                        target: s,
 | 
				
			||||||
                    }),
 | 
					                    }),
 | 
				
			||||||
                    Some(ComptimeValue::MacroString(s)) => {
 | 
					                    Some(ComptimeValue::MacroString(s)) => {
 | 
				
			||||||
                        todo!("indexing scoreboard with macro string: {s:?}")
 | 
					                        // TODO: allow indexing with macro string
 | 
				
			||||||
 | 
					                        let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
 | 
					                            reason: IllegalIndexingReason::InvalidComptimeType {
 | 
				
			||||||
 | 
					                                expected: ExpectedType::String,
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            expression: expression.span(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        handler.receive(err.clone());
 | 
				
			||||||
 | 
					                        return Err(err);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Some(_) => {
 | 
					                    Some(_) => {
 | 
				
			||||||
                        let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
					                        let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
| 
						 | 
					@ -621,7 +662,15 @@ impl Transpiler {
 | 
				
			||||||
                        entity: s,
 | 
					                        entity: s,
 | 
				
			||||||
                    }),
 | 
					                    }),
 | 
				
			||||||
                    Some(ComptimeValue::MacroString(s)) => {
 | 
					                    Some(ComptimeValue::MacroString(s)) => {
 | 
				
			||||||
                        todo!("indexing tag with macro string: {s:?}")
 | 
					                        // TODO: allow indexing tag with macro string
 | 
				
			||||||
 | 
					                        let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
 | 
					                            expression: expression.span(),
 | 
				
			||||||
 | 
					                            reason: IllegalIndexingReason::InvalidComptimeType {
 | 
				
			||||||
 | 
					                                expected: ExpectedType::String,
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        handler.receive(err.clone());
 | 
				
			||||||
 | 
					                        return Err(err);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    Some(_) => {
 | 
					                    Some(_) => {
 | 
				
			||||||
                        let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
					                        let err = TranspileError::IllegalIndexing(IllegalIndexing {
 | 
				
			||||||
| 
						 | 
					@ -643,6 +692,16 @@ impl Transpiler {
 | 
				
			||||||
                        return Err(err);
 | 
					                        return Err(err);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					                VariableData::ComptimeValue { value } => {
 | 
				
			||||||
 | 
					                    let comptime_value =
 | 
				
			||||||
 | 
					                        expression.comptime_eval(scope, handler).map_err(|err| {
 | 
				
			||||||
 | 
					                            let err = TranspileError::NotComptime(err);
 | 
				
			||||||
 | 
					                            handler.receive(err.clone());
 | 
				
			||||||
 | 
					                            err
 | 
				
			||||||
 | 
					                        })?;
 | 
				
			||||||
 | 
					                    *value.write().unwrap() = Some(comptime_value);
 | 
				
			||||||
 | 
					                    return Ok(Vec::new());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                VariableData::Function { .. }
 | 
					                VariableData::Function { .. }
 | 
				
			||||||
                | VariableData::MacroParameter { .. }
 | 
					                | VariableData::MacroParameter { .. }
 | 
				
			||||||
                | VariableData::InternalFunction { .. } => {
 | 
					                | VariableData::InternalFunction { .. } => {
 | 
				
			||||||
| 
						 | 
					@ -704,6 +763,7 @@ impl Transpiler {
 | 
				
			||||||
                TranspileAnnotationValue::Expression(expr) => {
 | 
					                TranspileAnnotationValue::Expression(expr) => {
 | 
				
			||||||
                    if let Some(name_eval) = expr
 | 
					                    if let Some(name_eval) = expr
 | 
				
			||||||
                        .comptime_eval(scope, handler)
 | 
					                        .comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                        .ok()
 | 
				
			||||||
                        .and_then(|val| val.to_string_no_macro())
 | 
					                        .and_then(|val| val.to_string_no_macro())
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        // TODO: change invalid criteria if boolean
 | 
					                        // TODO: change invalid criteria if boolean
 | 
				
			||||||
| 
						 | 
					@ -802,9 +862,11 @@ impl Transpiler {
 | 
				
			||||||
                        if let (Some(name_eval), Some(target_eval)) = (
 | 
					                        if let (Some(name_eval), Some(target_eval)) = (
 | 
				
			||||||
                            objective
 | 
					                            objective
 | 
				
			||||||
                                .comptime_eval(scope, handler)
 | 
					                                .comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                                .ok()
 | 
				
			||||||
                                .and_then(|val| val.to_string_no_macro()),
 | 
					                                .and_then(|val| val.to_string_no_macro()),
 | 
				
			||||||
                            target
 | 
					                            target
 | 
				
			||||||
                                .comptime_eval(scope, handler)
 | 
					                                .comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                                .ok()
 | 
				
			||||||
                                .and_then(|val| val.to_string_no_macro()),
 | 
					                                .and_then(|val| val.to_string_no_macro()),
 | 
				
			||||||
                        ) {
 | 
					                        ) {
 | 
				
			||||||
                            // TODO: change invalid criteria if boolean
 | 
					                            // TODO: change invalid criteria if boolean
 | 
				
			||||||
| 
						 | 
					@ -931,7 +993,15 @@ impl Transpiler {
 | 
				
			||||||
                    Ok((name, targets))
 | 
					                    Ok((name, targets))
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                TranspileAnnotationValue::Map(map) => {
 | 
					                TranspileAnnotationValue::Map(map) => {
 | 
				
			||||||
                    todo!("allow map deobfuscate annotation for array variables")
 | 
					                    // TODO: implement when map deobfuscate annotation is implemented
 | 
				
			||||||
 | 
					                    let error =
 | 
				
			||||||
 | 
					                        TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
 | 
					                            annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
 | 
					                            message: "Deobfuscate annotation value must be a string or none."
 | 
				
			||||||
 | 
					                                .to_string(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    handler.receive(error.clone());
 | 
				
			||||||
 | 
					                    Err(error)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                TranspileAnnotationValue::Expression(_) => {
 | 
					                TranspileAnnotationValue::Expression(_) => {
 | 
				
			||||||
                    let error =
 | 
					                    let error =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,7 +107,7 @@ pub fn identifier_to_scoreboard_target(ident: &str) -> std::borrow::Cow<str> {
 | 
				
			||||||
        let new_ident = ident
 | 
					        let new_ident = ident
 | 
				
			||||||
            .chars()
 | 
					            .chars()
 | 
				
			||||||
            .map(|c| {
 | 
					            .map(|c| {
 | 
				
			||||||
                if *c != '_' && !c.is_ascii_alphanumeric() {
 | 
					                if c != '_' && !c.is_ascii_alphanumeric() {
 | 
				
			||||||
                    '_'
 | 
					                    '_'
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    c
 | 
					                    c
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue