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