Compare commits
	
		
			3 Commits
		
	
	
		
			713977390d
			...
			0885665baf
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						0885665baf | |
| 
							
							
								 | 
						055de5c4ea | |
| 
							
							
								 | 
						d988a10d9d | 
| 
						 | 
					@ -444,7 +444,7 @@ impl Primary {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::Lua(_) | Self::StringLiteral(_) | Self::Integer(_) | Self::Boolean(_) => Ok(()),
 | 
					            Self::Lua(_) | Self::StringLiteral(_) | Self::Integer(_) | Self::Boolean(_) => Ok(()),
 | 
				
			||||||
            Self::MacroStringLiteral(literal) => literal.analyze_semantics(macro_names, handler),
 | 
					            Self::MacroStringLiteral(literal) => literal.analyze_semantics(macro_names, handler),
 | 
				
			||||||
            Self::Identifier(_) | Self::Parenthesized(_) | Self::Prefix(_) => {
 | 
					            Self::Identifier(_) | Self::Parenthesized(_) | Self::Prefix(_) | Self::Indexed(_) => {
 | 
				
			||||||
                // TODO: correctly analyze the semantics of the primary expression
 | 
					                // TODO: correctly analyze the semantics of the primary expression
 | 
				
			||||||
                Ok(())
 | 
					                Ok(())
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -185,6 +185,7 @@ pub enum Primary {
 | 
				
			||||||
    Identifier(Identifier),
 | 
					    Identifier(Identifier),
 | 
				
			||||||
    Prefix(Prefix),
 | 
					    Prefix(Prefix),
 | 
				
			||||||
    Parenthesized(Parenthesized),
 | 
					    Parenthesized(Parenthesized),
 | 
				
			||||||
 | 
					    Indexed(Indexed),
 | 
				
			||||||
    Integer(Integer),
 | 
					    Integer(Integer),
 | 
				
			||||||
    Boolean(Boolean),
 | 
					    Boolean(Boolean),
 | 
				
			||||||
    StringLiteral(StringLiteral),
 | 
					    StringLiteral(StringLiteral),
 | 
				
			||||||
| 
						 | 
					@ -199,6 +200,7 @@ impl SourceElement for Primary {
 | 
				
			||||||
            Self::Identifier(identifier) => identifier.span(),
 | 
					            Self::Identifier(identifier) => identifier.span(),
 | 
				
			||||||
            Self::Prefix(prefix) => prefix.span(),
 | 
					            Self::Prefix(prefix) => prefix.span(),
 | 
				
			||||||
            Self::Parenthesized(parenthesized) => parenthesized.span(),
 | 
					            Self::Parenthesized(parenthesized) => parenthesized.span(),
 | 
				
			||||||
 | 
					            Self::Indexed(indexed) => indexed.span(),
 | 
				
			||||||
            Self::Integer(int) => int.span(),
 | 
					            Self::Integer(int) => int.span(),
 | 
				
			||||||
            Self::Boolean(bool) => bool.span(),
 | 
					            Self::Boolean(bool) => bool.span(),
 | 
				
			||||||
            Self::StringLiteral(string_literal) => string_literal.span(),
 | 
					            Self::StringLiteral(string_literal) => string_literal.span(),
 | 
				
			||||||
| 
						 | 
					@ -245,6 +247,50 @@ impl SourceElement for Parenthesized {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Represents a indexed expression in the syntax tree.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Syntax Synopsis:    
 | 
				
			||||||
 | 
					/// ```ebnf
 | 
				
			||||||
 | 
					/// Indexed:
 | 
				
			||||||
 | 
					///     PrimaryExpression '[' Expression ']'
 | 
				
			||||||
 | 
					///     ;
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
				
			||||||
 | 
					pub struct Indexed {
 | 
				
			||||||
 | 
					    /// The object that is indexed.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    object: Box<Primary>,
 | 
				
			||||||
 | 
					    /// The left bracket.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    left_bracket: Punctuation,
 | 
				
			||||||
 | 
					    /// The index expression.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    index: Box<Expression>,
 | 
				
			||||||
 | 
					    /// The right bracket.
 | 
				
			||||||
 | 
					    #[get = "pub"]
 | 
				
			||||||
 | 
					    right_bracket: Punctuation,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Indexed {
 | 
				
			||||||
 | 
					    /// Dissolves the indexed expression into its components
 | 
				
			||||||
 | 
					    #[must_use]
 | 
				
			||||||
 | 
					    pub fn dissolve(self) -> (Primary, Punctuation, Expression, Punctuation) {
 | 
				
			||||||
 | 
					        (
 | 
				
			||||||
 | 
					            *self.object,
 | 
				
			||||||
 | 
					            self.left_bracket,
 | 
				
			||||||
 | 
					            *self.index,
 | 
				
			||||||
 | 
					            self.right_bracket,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SourceElement for Indexed {
 | 
				
			||||||
 | 
					    fn span(&self) -> Span {
 | 
				
			||||||
 | 
					        self.object.span().join(&self.right_bracket.span).unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents a prefix operator in the syntax tree.
 | 
					/// Represents a prefix operator in the syntax tree.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// Syntax Synopsis:
 | 
					/// Syntax Synopsis:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ use crate::{
 | 
				
			||||||
    lexical::{
 | 
					    lexical::{
 | 
				
			||||||
        token::{
 | 
					        token::{
 | 
				
			||||||
            CommandLiteral, DocComment, Identifier, Integer, Keyword, KeywordKind, Punctuation,
 | 
					            CommandLiteral, DocComment, Identifier, Integer, Keyword, KeywordKind, Punctuation,
 | 
				
			||||||
            Token,
 | 
					            StringLiteral, Token,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        token_stream::Delimiter,
 | 
					        token_stream::Delimiter,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -542,7 +542,7 @@ impl ArrayVariableDeclaration {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type CriteriaSelection = (Punctuation, AnyStringLiteral, Punctuation);
 | 
					type CriteriaSelection = (Punctuation, StringLiteral, Punctuation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents a scoreboard variable declaration in the syntax tree.
 | 
					/// Represents a scoreboard variable declaration in the syntax tree.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
| 
						 | 
					@ -550,7 +550,7 @@ type CriteriaSelection = (Punctuation, AnyStringLiteral, Punctuation);
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// ```ebnf
 | 
					/// ```ebnf
 | 
				
			||||||
/// ScoreVariableDeclaration:
 | 
					/// ScoreVariableDeclaration:
 | 
				
			||||||
///     'int' ('<' AnyStringLiteral '>')? identifier '[' AnyStringLiteral? ']' VariableDeclarationAssignment?
 | 
					///     'int' ('<' StringLiteral '>')? identifier '[' AnyStringLiteral? ']' VariableDeclarationAssignment?
 | 
				
			||||||
/// ```
 | 
					/// ```
 | 
				
			||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
				
			||||||
| 
						 | 
					@ -685,14 +685,14 @@ impl TagVariableDeclaration {
 | 
				
			||||||
/// Syntax Synopsis:
 | 
					/// Syntax Synopsis:
 | 
				
			||||||
/// ```ebnf
 | 
					/// ```ebnf
 | 
				
			||||||
/// Assignment:
 | 
					/// Assignment:
 | 
				
			||||||
///    Identifier '=' Expression
 | 
					///    AssignmentDestination '=' Expression
 | 
				
			||||||
/// ```
 | 
					/// ```
 | 
				
			||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
 | 
				
			||||||
pub struct Assignment {
 | 
					pub struct Assignment {
 | 
				
			||||||
    /// The identifier of the assignment.
 | 
					    /// The identifier of the assignment.
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    identifier: Identifier,
 | 
					    destination: AssignmentDestination,
 | 
				
			||||||
    /// The equals sign of the assignment.
 | 
					    /// The equals sign of the assignment.
 | 
				
			||||||
    #[get = "pub"]
 | 
					    #[get = "pub"]
 | 
				
			||||||
    equals: Punctuation,
 | 
					    equals: Punctuation,
 | 
				
			||||||
| 
						 | 
					@ -703,7 +703,7 @@ pub struct Assignment {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SourceElement for Assignment {
 | 
					impl SourceElement for Assignment {
 | 
				
			||||||
    fn span(&self) -> Span {
 | 
					    fn span(&self) -> Span {
 | 
				
			||||||
        self.identifier
 | 
					        self.destination
 | 
				
			||||||
            .span()
 | 
					            .span()
 | 
				
			||||||
            .join(&self.expression.span())
 | 
					            .join(&self.expression.span())
 | 
				
			||||||
            .expect("The span of the assignment is invalid.")
 | 
					            .expect("The span of the assignment is invalid.")
 | 
				
			||||||
| 
						 | 
					@ -713,8 +713,38 @@ impl SourceElement for Assignment {
 | 
				
			||||||
impl Assignment {
 | 
					impl Assignment {
 | 
				
			||||||
    /// Dissolves the [`Assignment`] into its components.
 | 
					    /// Dissolves the [`Assignment`] into its components.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn dissolve(self) -> (Identifier, Punctuation, Expression) {
 | 
					    pub fn dissolve(self) -> (AssignmentDestination, Punctuation, Expression) {
 | 
				
			||||||
        (self.identifier, self.equals, self.expression)
 | 
					        (self.destination, self.equals, self.expression)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Represents an assignment destination in the syntax tree.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Syntax Synopsis:
 | 
				
			||||||
 | 
					/// ```ebnf
 | 
				
			||||||
 | 
					/// AssignmentDestination:
 | 
				
			||||||
 | 
					///     Identifier
 | 
				
			||||||
 | 
					///     | Identifier '[' Expression ']'
 | 
				
			||||||
 | 
					///     ;
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 | 
				
			||||||
 | 
					pub enum AssignmentDestination {
 | 
				
			||||||
 | 
					    /// Assignment to an identifier.
 | 
				
			||||||
 | 
					    Identifier(Identifier),
 | 
				
			||||||
 | 
					    /// Assignment to an indexed identifier.
 | 
				
			||||||
 | 
					    Indexed(Identifier, Punctuation, Expression, Punctuation),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SourceElement for AssignmentDestination {
 | 
				
			||||||
 | 
					    fn span(&self) -> Span {
 | 
				
			||||||
 | 
					        match self {
 | 
				
			||||||
 | 
					            Self::Identifier(identifier) => identifier.span(),
 | 
				
			||||||
 | 
					            Self::Indexed(identifier, _, _, close) => identifier
 | 
				
			||||||
 | 
					                .span()
 | 
				
			||||||
 | 
					                .join(&close.span())
 | 
				
			||||||
 | 
					                .expect("The span of the indexed assignment destination is invalid."),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -859,15 +889,30 @@ impl<'a> Parser<'a> {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            _ => {
 | 
					            _ => {
 | 
				
			||||||
                // try to parse assignment
 | 
					                // try to parse assignment
 | 
				
			||||||
                // TODO: improve
 | 
					 | 
				
			||||||
                #[expect(clippy::option_if_let_else)]
 | 
					                #[expect(clippy::option_if_let_else)]
 | 
				
			||||||
                if let Ok(assignment) = self.try_parse(|p| {
 | 
					                if let Ok(assignment) = self.try_parse(|p| {
 | 
				
			||||||
 | 
					                    let destination = {
 | 
				
			||||||
                        let identifier = p.parse_identifier(&VoidHandler)?;
 | 
					                        let identifier = p.parse_identifier(&VoidHandler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        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)
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            AssignmentDestination::Identifier(identifier)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
                    let equals = p.parse_punctuation('=', true, &VoidHandler)?;
 | 
					                    let equals = p.parse_punctuation('=', true, &VoidHandler)?;
 | 
				
			||||||
                    let expression = p.parse_expression(&VoidHandler)?;
 | 
					                    let expression = p.parse_expression(&VoidHandler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Ok(SemicolonStatement::Assignment(Assignment {
 | 
					                    Ok(SemicolonStatement::Assignment(Assignment {
 | 
				
			||||||
                        identifier,
 | 
					                        destination,
 | 
				
			||||||
                        equals,
 | 
					                        equals,
 | 
				
			||||||
                        expression,
 | 
					                        expression,
 | 
				
			||||||
                    }))
 | 
					                    }))
 | 
				
			||||||
| 
						 | 
					@ -923,7 +968,7 @@ impl<'a> Parser<'a> {
 | 
				
			||||||
        let criteria_selection = if variable_type.keyword == KeywordKind::Int {
 | 
					        let criteria_selection = if variable_type.keyword == KeywordKind::Int {
 | 
				
			||||||
            self.try_parse(|p| {
 | 
					            self.try_parse(|p| {
 | 
				
			||||||
                let open = p.parse_punctuation('<', true, &VoidHandler)?;
 | 
					                let open = p.parse_punctuation('<', true, &VoidHandler)?;
 | 
				
			||||||
                let criteria = p.parse_any_string_literal(&VoidHandler)?;
 | 
					                let criteria = p.parse_string_literal(&VoidHandler)?;
 | 
				
			||||||
                let close = p.parse_punctuation('>', true, &VoidHandler)?;
 | 
					                let close = p.parse_punctuation('>', true, &VoidHandler)?;
 | 
				
			||||||
                Ok((open, criteria, close))
 | 
					                Ok((open, criteria, close))
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
//! The expression transpiler.
 | 
					//! The expression transpiler.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{fmt::Display, sync::Arc};
 | 
					use std::{fmt::Display, string::ToString, sync::Arc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{util::MacroString, Scope, VariableData};
 | 
					use super::{util::MacroString, Scope, VariableData};
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
| 
						 | 
					@ -265,10 +265,7 @@ impl Primary {
 | 
				
			||||||
                    .get_variable(ident.span.str())
 | 
					                    .get_variable(ident.span.str())
 | 
				
			||||||
                    .map_or(false, |variable| match r#type {
 | 
					                    .map_or(false, |variable| match r#type {
 | 
				
			||||||
                        ValueType::Boolean => {
 | 
					                        ValueType::Boolean => {
 | 
				
			||||||
                            matches!(
 | 
					                            matches!(variable.as_ref(), VariableData::BooleanStorage { .. })
 | 
				
			||||||
                                variable.as_ref(),
 | 
					 | 
				
			||||||
                                VariableData::Tag { .. } | VariableData::BooleanStorage { .. }
 | 
					 | 
				
			||||||
                            )
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        ValueType::Integer => {
 | 
					                        ValueType::Integer => {
 | 
				
			||||||
                            matches!(variable.as_ref(), VariableData::ScoreboardValue { .. })
 | 
					                            matches!(variable.as_ref(), VariableData::ScoreboardValue { .. })
 | 
				
			||||||
| 
						 | 
					@ -289,6 +286,29 @@ impl Primary {
 | 
				
			||||||
                        && prefix.operand().can_yield_type(r#type, scope)
 | 
					                        && prefix.operand().can_yield_type(r#type, scope)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            Self::Indexed(indexed) => {
 | 
				
			||||||
 | 
					                let Self::Identifier(ident) = indexed.object().as_ref() else {
 | 
				
			||||||
 | 
					                    todo!("throw error: cannot index anything except identifiers")
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                scope
 | 
				
			||||||
 | 
					                    .get_variable(ident.span.str())
 | 
				
			||||||
 | 
					                    .map_or(false, |variable| match r#type {
 | 
				
			||||||
 | 
					                        ValueType::Boolean => {
 | 
				
			||||||
 | 
					                            matches!(
 | 
				
			||||||
 | 
					                                variable.as_ref(),
 | 
				
			||||||
 | 
					                                VariableData::Tag { .. } | VariableData::BooleanStorageArray { .. }
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        ValueType::Integer => {
 | 
				
			||||||
 | 
					                            matches!(
 | 
				
			||||||
 | 
					                                variable.as_ref(),
 | 
				
			||||||
 | 
					                                VariableData::Scoreboard { .. }
 | 
				
			||||||
 | 
					                                    | VariableData::ScoreboardArray { .. }
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        ValueType::String => false,
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            #[cfg_attr(not(feature = "lua"), expect(unused_variables))]
 | 
					            #[cfg_attr(not(feature = "lua"), expect(unused_variables))]
 | 
				
			||||||
            Self::Lua(lua) => {
 | 
					            Self::Lua(lua) => {
 | 
				
			||||||
                cfg_if::cfg_if! {
 | 
					                cfg_if::cfg_if! {
 | 
				
			||||||
| 
						 | 
					@ -323,7 +343,7 @@ impl Primary {
 | 
				
			||||||
            Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
 | 
					            Self::StringLiteral(string_literal) => Some(ComptimeValue::String(
 | 
				
			||||||
                string_literal.str_content().to_string(),
 | 
					                string_literal.str_content().to_string(),
 | 
				
			||||||
            )),
 | 
					            )),
 | 
				
			||||||
            Self::Identifier(_) | Self::FunctionCall(_) => None,
 | 
					            Self::Identifier(_) | Self::FunctionCall(_) | Self::Indexed(_) => None,
 | 
				
			||||||
            Self::Parenthesized(parenthesized) => {
 | 
					            Self::Parenthesized(parenthesized) => {
 | 
				
			||||||
                parenthesized.expression().comptime_eval(scope, handler)
 | 
					                parenthesized.expression().comptime_eval(scope, handler)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -730,109 +750,21 @@ impl Transpiler {
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            Primary::Identifier(ident) => {
 | 
					            Primary::Identifier(ident) => {
 | 
				
			||||||
                let variable = scope.get_variable(ident.span.str());
 | 
					                let variable = scope.get_variable(ident.span.str());
 | 
				
			||||||
                #[expect(clippy::option_if_let_else)]
 | 
					 | 
				
			||||||
                if let Some(variable) = variable.as_deref() {
 | 
					                if let Some(variable) = variable.as_deref() {
 | 
				
			||||||
                    match variable {
 | 
					                    let from = match variable {
 | 
				
			||||||
                        VariableData::BooleanStorage { storage_name, path } => match target {
 | 
					                        VariableData::BooleanStorage { storage_name, path } => {
 | 
				
			||||||
                            DataLocation::ScoreboardValue { objective, target } => {
 | 
					                            Ok(DataLocation::Storage {
 | 
				
			||||||
                                let cmd = Command::Execute(Execute::Store(
 | 
					                                storage_name: storage_name.to_string(),
 | 
				
			||||||
                                    format!("store result score {target} {objective}").into(),
 | 
					                                path: path.to_string(),
 | 
				
			||||||
                                    Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
					                                r#type: StorageType::Boolean,
 | 
				
			||||||
                                        "data get storage {storage_name} {path}"
 | 
					                            })
 | 
				
			||||||
                                    ))))),
 | 
					 | 
				
			||||||
                                ));
 | 
					 | 
				
			||||||
                                Ok(vec![cmd])
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                            DataLocation::Tag { tag_name, entity } => {
 | 
					                        VariableData::ScoreboardValue { objective, target } => {
 | 
				
			||||||
                                let cmd = Command::Execute(Execute::If(
 | 
					                            Ok(DataLocation::ScoreboardValue {
 | 
				
			||||||
                                    Condition::Atom(
 | 
					                                objective: objective.to_string(),
 | 
				
			||||||
                                        format!("data storage {storage_name} {{{path}: 1b}}")
 | 
					                                target: target.to_string(),
 | 
				
			||||||
                                            .into(),
 | 
					                            })
 | 
				
			||||||
                                    ),
 | 
					 | 
				
			||||||
                                    Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
					 | 
				
			||||||
                                        "tag {entity} add {tag_name}"
 | 
					 | 
				
			||||||
                                    ))))),
 | 
					 | 
				
			||||||
                                    Some(Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
					 | 
				
			||||||
                                        "tag {entity} remove {tag_name}"
 | 
					 | 
				
			||||||
                                    )))))),
 | 
					 | 
				
			||||||
                                ));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                Ok(vec![cmd])
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                            DataLocation::Storage {
 | 
					 | 
				
			||||||
                                storage_name: target_storage_name,
 | 
					 | 
				
			||||||
                                path: target_path,
 | 
					 | 
				
			||||||
                                r#type,
 | 
					 | 
				
			||||||
                            } => {
 | 
					 | 
				
			||||||
                                if matches!(r#type, StorageType::Boolean) {
 | 
					 | 
				
			||||||
                                    let cmd = Command::Raw(format!(
 | 
					 | 
				
			||||||
                                        "data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"
 | 
					 | 
				
			||||||
                                    ));
 | 
					 | 
				
			||||||
                                    Ok(vec![cmd])
 | 
					 | 
				
			||||||
                                } else {
 | 
					 | 
				
			||||||
                                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					 | 
				
			||||||
                                        expression: primary.span(),
 | 
					 | 
				
			||||||
                                        expected_type: target.value_type().into(),
 | 
					 | 
				
			||||||
                                    });
 | 
					 | 
				
			||||||
                                    handler.receive(err.clone());
 | 
					 | 
				
			||||||
                                    Err(err)
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        VariableData::ScoreboardValue {
 | 
					 | 
				
			||||||
                            objective,
 | 
					 | 
				
			||||||
                            target: score_target,
 | 
					 | 
				
			||||||
                        } => match target {
 | 
					 | 
				
			||||||
                            DataLocation::ScoreboardValue {
 | 
					 | 
				
			||||||
                                objective: target_objective,
 | 
					 | 
				
			||||||
                                target: target_target,
 | 
					 | 
				
			||||||
                            } => {
 | 
					 | 
				
			||||||
                                let cmd = Command::Raw(format!(
 | 
					 | 
				
			||||||
                                    "scoreboard players operation {target_target} {target_objective} = {score_target} {objective}"
 | 
					 | 
				
			||||||
                                ));
 | 
					 | 
				
			||||||
                                Ok(vec![cmd])
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            DataLocation::Storage {
 | 
					 | 
				
			||||||
                                storage_name,
 | 
					 | 
				
			||||||
                                path,
 | 
					 | 
				
			||||||
                                r#type,
 | 
					 | 
				
			||||||
                            } => {
 | 
					 | 
				
			||||||
                                if matches!(
 | 
					 | 
				
			||||||
                                    r#type,
 | 
					 | 
				
			||||||
                                    StorageType::Byte
 | 
					 | 
				
			||||||
                                        | StorageType::Double
 | 
					 | 
				
			||||||
                                        | StorageType::Int
 | 
					 | 
				
			||||||
                                        | StorageType::Long
 | 
					 | 
				
			||||||
                                ) {
 | 
					 | 
				
			||||||
                                    let cmd = Command::Execute(Execute::Store(
 | 
					 | 
				
			||||||
                                        format!(
 | 
					 | 
				
			||||||
                                            "result storage {storage_name} {path} {t} 1.0",
 | 
					 | 
				
			||||||
                                            t = r#type.as_str()
 | 
					 | 
				
			||||||
                                        )
 | 
					 | 
				
			||||||
                                        .into(),
 | 
					 | 
				
			||||||
                                        Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
					 | 
				
			||||||
                                            "scoreboard players get {score_target} {objective}"
 | 
					 | 
				
			||||||
                                        ))))),
 | 
					 | 
				
			||||||
                                    ));
 | 
					 | 
				
			||||||
                                    Ok(vec![cmd])
 | 
					 | 
				
			||||||
                                } else {
 | 
					 | 
				
			||||||
                                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					 | 
				
			||||||
                                        expression: primary.span(),
 | 
					 | 
				
			||||||
                                        expected_type: target.value_type().into(),
 | 
					 | 
				
			||||||
                                    });
 | 
					 | 
				
			||||||
                                    handler.receive(err.clone());
 | 
					 | 
				
			||||||
                                    Err(err)
 | 
					 | 
				
			||||||
                                }
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                            DataLocation::Tag { .. } => {
 | 
					 | 
				
			||||||
                                let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					 | 
				
			||||||
                                    expected_type: ExpectedType::Boolean,
 | 
					 | 
				
			||||||
                                    expression: primary.span(),
 | 
					 | 
				
			||||||
                                });
 | 
					 | 
				
			||||||
                                handler.receive(err.clone());
 | 
					 | 
				
			||||||
                                Err(err)
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        _ => {
 | 
					                        _ => {
 | 
				
			||||||
                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
					                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
                                expected_type: target.value_type().into(),
 | 
					                                expected_type: target.value_type().into(),
 | 
				
			||||||
| 
						 | 
					@ -841,7 +773,92 @@ impl Transpiler {
 | 
				
			||||||
                            handler.receive(err.clone());
 | 
					                            handler.receive(err.clone());
 | 
				
			||||||
                            Err(err)
 | 
					                            Err(err)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                    }?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    self.move_data(&from, target, primary, handler)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
				
			||||||
 | 
					                        identifier: ident.span.clone(),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
 | 
					                    Err(err)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Primary::Indexed(indexed) => {
 | 
				
			||||||
 | 
					                let Primary::Identifier(ident) = indexed.object().as_ref() else {
 | 
				
			||||||
 | 
					                    todo!("can only index identifier")
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                let variable = scope.get_variable(ident.span.str());
 | 
				
			||||||
 | 
					                #[expect(clippy::option_if_let_else)]
 | 
				
			||||||
 | 
					                if let Some(variable) = variable.as_deref() {
 | 
				
			||||||
 | 
					                    let from = match variable {
 | 
				
			||||||
 | 
					                        VariableData::Scoreboard { objective } => {
 | 
				
			||||||
 | 
					                            if let Some(ComptimeValue::String(target)) =
 | 
				
			||||||
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                Ok(DataLocation::ScoreboardValue {
 | 
				
			||||||
 | 
					                                    objective: objective.to_string(),
 | 
				
			||||||
 | 
					                                    target,
 | 
				
			||||||
 | 
					                                })
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                todo!("can only index scoreboard with comptime string")
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        VariableData::ScoreboardArray { objective, targets } => {
 | 
				
			||||||
 | 
					                            if let Some(ComptimeValue::Integer(index)) =
 | 
				
			||||||
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                if let Some(target) = usize::try_from(index)
 | 
				
			||||||
 | 
					                                    .ok()
 | 
				
			||||||
 | 
					                                    .and_then(|index| targets.get(index))
 | 
				
			||||||
 | 
					                                    .map(ToString::to_string)
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    Ok(DataLocation::ScoreboardValue {
 | 
				
			||||||
 | 
					                                        objective: objective.to_string(),
 | 
				
			||||||
 | 
					                                        target,
 | 
				
			||||||
 | 
					                                    })
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    todo!("index out of bounds")
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                todo!("can only index array with comptime integer")
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        VariableData::BooleanStorageArray {
 | 
				
			||||||
 | 
					                            storage_name,
 | 
				
			||||||
 | 
					                            paths,
 | 
				
			||||||
 | 
					                        } => {
 | 
				
			||||||
 | 
					                            if let Some(ComptimeValue::Integer(index)) =
 | 
				
			||||||
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                if let Some(path) = usize::try_from(index)
 | 
				
			||||||
 | 
					                                    .ok()
 | 
				
			||||||
 | 
					                                    .and_then(|index| paths.get(index))
 | 
				
			||||||
 | 
					                                    .map(ToString::to_string)
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    Ok(DataLocation::Storage {
 | 
				
			||||||
 | 
					                                        storage_name: storage_name.to_string(),
 | 
				
			||||||
 | 
					                                        path,
 | 
				
			||||||
 | 
					                                        r#type: StorageType::Boolean,
 | 
				
			||||||
 | 
					                                    })
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    todo!("index out of bounds")
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                todo!("can only index array with comptime integer")
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        _ => {
 | 
				
			||||||
 | 
					                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                                expected_type: target.value_type().into(),
 | 
				
			||||||
 | 
					                                expression: primary.span(),
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            handler.receive(err.clone());
 | 
				
			||||||
 | 
					                            Err(err)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    self.move_data(&from, target, primary, handler)
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
					                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
				
			||||||
                        identifier: ident.span.clone(),
 | 
					                        identifier: ident.span.clone(),
 | 
				
			||||||
| 
						 | 
					@ -1004,6 +1021,57 @@ impl Transpiler {
 | 
				
			||||||
            Primary::Parenthesized(parenthesized) => {
 | 
					            Primary::Parenthesized(parenthesized) => {
 | 
				
			||||||
                self.transpile_expression_as_condition(parenthesized.expression(), scope, handler)
 | 
					                self.transpile_expression_as_condition(parenthesized.expression(), scope, handler)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            Primary::Indexed(indexed) => {
 | 
				
			||||||
 | 
					                let Primary::Identifier(ident) = indexed.object().as_ref() else {
 | 
				
			||||||
 | 
					                    todo!("can only index identifier")
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                #[expect(clippy::option_if_let_else)]
 | 
				
			||||||
 | 
					                if let Some(variable) = scope.get_variable(ident.span.str()).as_deref() {
 | 
				
			||||||
 | 
					                    #[expect(clippy::single_match_else)]
 | 
				
			||||||
 | 
					                    match variable {
 | 
				
			||||||
 | 
					                        VariableData::BooleanStorageArray {
 | 
				
			||||||
 | 
					                            storage_name,
 | 
				
			||||||
 | 
					                            paths,
 | 
				
			||||||
 | 
					                        } => {
 | 
				
			||||||
 | 
					                            if let Some(ComptimeValue::Integer(index)) =
 | 
				
			||||||
 | 
					                                indexed.index().comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                if let Some(path) = usize::try_from(index)
 | 
				
			||||||
 | 
					                                    .ok()
 | 
				
			||||||
 | 
					                                    .and_then(|index| paths.get(index))
 | 
				
			||||||
 | 
					                                    .map(ToString::to_string)
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    Ok((
 | 
				
			||||||
 | 
					                                        Vec::new(),
 | 
				
			||||||
 | 
					                                        ExtendedCondition::Runtime(Condition::Atom(
 | 
				
			||||||
 | 
					                                            format!("data storage {storage_name} {{{path}: 1b}}")
 | 
				
			||||||
 | 
					                                                .into(),
 | 
				
			||||||
 | 
					                                        )),
 | 
				
			||||||
 | 
					                                    ))
 | 
				
			||||||
 | 
					                                } else {
 | 
				
			||||||
 | 
					                                    todo!("index out of bounds")
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                todo!("can only index array with comptime integer")
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        _ => {
 | 
				
			||||||
 | 
					                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                                expected_type: ExpectedType::Boolean,
 | 
				
			||||||
 | 
					                                expression: primary.span(),
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            handler.receive(err.clone());
 | 
				
			||||||
 | 
					                            Err(err)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
 | 
				
			||||||
 | 
					                        identifier: ident.span.clone(),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
 | 
					                    Err(err)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            Primary::Prefix(prefix) => match prefix.operator() {
 | 
					            Primary::Prefix(prefix) => match prefix.operator() {
 | 
				
			||||||
                PrefixOperator::LogicalNot(_) => {
 | 
					                PrefixOperator::LogicalNot(_) => {
 | 
				
			||||||
                    let (cmds, cond) = self.transpile_primary_expression_as_condition(
 | 
					                    let (cmds, cond) = self.transpile_primary_expression_as_condition(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,12 +98,37 @@ mod enabled {
 | 
				
			||||||
        fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> mlua::Result<()> {
 | 
					        fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> mlua::Result<()> {
 | 
				
			||||||
            let globals = lua.globals();
 | 
					            let globals = lua.globals();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let location = {
 | 
					            let shulkerscript_globals = {
 | 
				
			||||||
 | 
					                let table = lua.create_table()?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let (location_path, location_start, location_end) = {
 | 
				
			||||||
                    let span = self.span();
 | 
					                    let span = self.span();
 | 
				
			||||||
                    let file = span.source_file();
 | 
					                    let file = span.source_file();
 | 
				
			||||||
                file.path_relative().unwrap_or_else(|| file.path().clone())
 | 
					                    let path = file.path().to_owned();
 | 
				
			||||||
 | 
					                    let start_location = span.start_location();
 | 
				
			||||||
 | 
					                    let end_location = span.end_location().unwrap_or_else(|| {
 | 
				
			||||||
 | 
					                        let line_amount = file.line_amount();
 | 
				
			||||||
 | 
					                        let column = file.get_line(line_amount).expect("line amount used").len();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        crate::base::source_file::Location {
 | 
				
			||||||
 | 
					                            line: line_amount,
 | 
				
			||||||
 | 
					                            column,
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    (path, start_location, end_location)
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                table.set("file_path", location_path.to_string_lossy())?;
 | 
				
			||||||
 | 
					                table.set("start_line", location_start.line)?;
 | 
				
			||||||
 | 
					                table.set("start_column", location_start.column)?;
 | 
				
			||||||
 | 
					                table.set("end_line", location_end.line)?;
 | 
				
			||||||
 | 
					                table.set("end_column", location_end.column)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                table.set("version", crate::VERSION)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                table
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            globals.set("shu_location", location.to_string_lossy())?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if let Some(inputs) = self.inputs() {
 | 
					            if let Some(inputs) = self.inputs() {
 | 
				
			||||||
                for x in inputs.elements() {
 | 
					                for x in inputs.elements() {
 | 
				
			||||||
| 
						 | 
					@ -112,25 +137,66 @@ mod enabled {
 | 
				
			||||||
                        Some(VariableData::MacroParameter { macro_name, .. }) => {
 | 
					                        Some(VariableData::MacroParameter { macro_name, .. }) => {
 | 
				
			||||||
                            Value::String(lua.create_string(format!("$({macro_name})"))?)
 | 
					                            Value::String(lua.create_string(format!("$({macro_name})"))?)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                        Some(VariableData::Scoreboard { objective }) => {
 | 
				
			||||||
 | 
					                            let table = lua.create_table()?;
 | 
				
			||||||
 | 
					                            table.set("objective", objective.as_str())?;
 | 
				
			||||||
 | 
					                            Value::Table(table)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        Some(VariableData::ScoreboardValue { objective, target }) => {
 | 
					                        Some(VariableData::ScoreboardValue { objective, target }) => {
 | 
				
			||||||
                            let table = lua.create_table()?;
 | 
					                            let table = lua.create_table()?;
 | 
				
			||||||
                            table.set("objective", lua.create_string(objective)?)?;
 | 
					                            table.set("objective", lua.create_string(objective)?)?;
 | 
				
			||||||
                            table.set("target", lua.create_string(target)?)?;
 | 
					                            table.set("target", lua.create_string(target)?)?;
 | 
				
			||||||
                            Value::Table(table)
 | 
					                            Value::Table(table)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                        Some(VariableData::ScoreboardArray { objective, targets }) => {
 | 
				
			||||||
 | 
					                            let table = lua.create_table()?;
 | 
				
			||||||
 | 
					                            table.set("objective", objective.as_str())?;
 | 
				
			||||||
 | 
					                            let values = lua.create_table_from(
 | 
				
			||||||
 | 
					                                targets
 | 
				
			||||||
 | 
					                                    .iter()
 | 
				
			||||||
 | 
					                                    .enumerate()
 | 
				
			||||||
 | 
					                                    .map(|(i, target)| (i + 1, target.as_str())),
 | 
				
			||||||
 | 
					                            )?;
 | 
				
			||||||
 | 
					                            table.set("targets", values)?;
 | 
				
			||||||
 | 
					                            Value::Table(table)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        Some(VariableData::BooleanStorage { storage_name, path }) => {
 | 
					                        Some(VariableData::BooleanStorage { storage_name, path }) => {
 | 
				
			||||||
                            let table = lua.create_table()?;
 | 
					                            let table = lua.create_table()?;
 | 
				
			||||||
                            table.set("storage", lua.create_string(storage_name)?)?;
 | 
					                            table.set("storage", lua.create_string(storage_name)?)?;
 | 
				
			||||||
                            table.set("path", lua.create_string(path)?)?;
 | 
					                            table.set("path", lua.create_string(path)?)?;
 | 
				
			||||||
                            Value::Table(table)
 | 
					                            Value::Table(table)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        Some(_) => todo!("allow other variable types"),
 | 
					                        Some(VariableData::BooleanStorageArray {
 | 
				
			||||||
 | 
					                            storage_name,
 | 
				
			||||||
 | 
					                            paths,
 | 
				
			||||||
 | 
					                        }) => {
 | 
				
			||||||
 | 
					                            let table = lua.create_table()?;
 | 
				
			||||||
 | 
					                            table.set("storage", storage_name.as_str())?;
 | 
				
			||||||
 | 
					                            let values = lua.create_table_from(
 | 
				
			||||||
 | 
					                                paths
 | 
				
			||||||
 | 
					                                    .iter()
 | 
				
			||||||
 | 
					                                    .enumerate()
 | 
				
			||||||
 | 
					                                    .map(|(i, path)| (i + 1, path.as_str())),
 | 
				
			||||||
 | 
					                            )?;
 | 
				
			||||||
 | 
					                            table.set("paths", values)?;
 | 
				
			||||||
 | 
					                            Value::Table(table)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        Some(VariableData::Tag { tag_name }) => {
 | 
				
			||||||
 | 
					                            let table = lua.create_table()?;
 | 
				
			||||||
 | 
					                            table.set("name", tag_name.as_str())?;
 | 
				
			||||||
 | 
					                            Value::Table(table)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        Some(VariableData::Function { .. }) => {
 | 
				
			||||||
 | 
					                            todo!("allow function variable type")
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        None => todo!("throw correct error"),
 | 
					                        None => todo!("throw correct error"),
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
                    globals.set(name, value)?;
 | 
					                    globals.set(name, value)?;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            globals.set("shulkerscript", shulkerscript_globals)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ok(())
 | 
					            Ok(())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ use crate::{
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    error::{MismatchedTypes, TranspileError, TranspileResult, UnknownIdentifier},
 | 
					    error::{MismatchedTypes, TranspileError, TranspileResult, UnknownIdentifier},
 | 
				
			||||||
    expression::{ComptimeValue, ExpectedType, ExtendedCondition, StorageType},
 | 
					    expression::{ComptimeValue, ExpectedType, ExtendedCondition, StorageType},
 | 
				
			||||||
    variables::{Scope, VariableData},
 | 
					    variables::{Scope, TranspileAssignmentTarget, VariableData},
 | 
				
			||||||
    FunctionData, TranspileAnnotationValue,
 | 
					    FunctionData, TranspileAnnotationValue,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -514,6 +514,7 @@ impl Transpiler {
 | 
				
			||||||
                        Expression::Primary(
 | 
					                        Expression::Primary(
 | 
				
			||||||
                            Primary::Parenthesized(_)
 | 
					                            Primary::Parenthesized(_)
 | 
				
			||||||
                            | Primary::Prefix(_)
 | 
					                            | Primary::Prefix(_)
 | 
				
			||||||
 | 
					                            | Primary::Indexed(_)
 | 
				
			||||||
                            | Primary::FunctionCall(_),
 | 
					                            | Primary::FunctionCall(_),
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                        | Expression::Binary(_) => {
 | 
					                        | Expression::Binary(_) => {
 | 
				
			||||||
| 
						 | 
					@ -745,7 +746,7 @@ impl Transpiler {
 | 
				
			||||||
                    self.transpile_variable_declaration(decl, program_identifier, scope, handler)
 | 
					                    self.transpile_variable_declaration(decl, program_identifier, scope, handler)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                SemicolonStatement::Assignment(assignment) => self.transpile_assignment(
 | 
					                SemicolonStatement::Assignment(assignment) => self.transpile_assignment(
 | 
				
			||||||
                    assignment.identifier(),
 | 
					                    TranspileAssignmentTarget::from(assignment.destination()),
 | 
				
			||||||
                    assignment.expression(),
 | 
					                    assignment.expression(),
 | 
				
			||||||
                    scope,
 | 
					                    scope,
 | 
				
			||||||
                    handler,
 | 
					                    handler,
 | 
				
			||||||
| 
						 | 
					@ -770,7 +771,8 @@ impl Transpiler {
 | 
				
			||||||
                Primary::Integer(_)
 | 
					                Primary::Integer(_)
 | 
				
			||||||
                | Primary::Boolean(_)
 | 
					                | Primary::Boolean(_)
 | 
				
			||||||
                | Primary::Prefix(_)
 | 
					                | Primary::Prefix(_)
 | 
				
			||||||
                | Primary::Identifier(_),
 | 
					                | Primary::Identifier(_)
 | 
				
			||||||
 | 
					                | Primary::Indexed(_),
 | 
				
			||||||
            ) => {
 | 
					            ) => {
 | 
				
			||||||
                let error =
 | 
					                let error =
 | 
				
			||||||
                    TranspileError::UnexpectedExpression(UnexpectedExpression(expression.clone()));
 | 
					                    TranspileError::UnexpectedExpression(UnexpectedExpression(expression.clone()));
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,17 +15,20 @@ use shulkerbox::prelude::{Command, Condition, Execute};
 | 
				
			||||||
use enum_as_inner::EnumAsInner;
 | 
					use enum_as_inner::EnumAsInner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    base::{self, source_file::SourceElement as _, Handler},
 | 
					    base::{self, source_file::SourceElement, Handler},
 | 
				
			||||||
    lexical::token::{Identifier, KeywordKind},
 | 
					    lexical::token::{Identifier, KeywordKind},
 | 
				
			||||||
    syntax::syntax_tree::{
 | 
					    syntax::syntax_tree::{
 | 
				
			||||||
        expression::{Expression, Primary},
 | 
					        expression::{Expression, Primary},
 | 
				
			||||||
        statement::{SingleVariableDeclaration, VariableDeclaration},
 | 
					        statement::{
 | 
				
			||||||
 | 
					            AssignmentDestination, ScoreVariableDeclaration, SingleVariableDeclaration,
 | 
				
			||||||
 | 
					            VariableDeclaration,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{
 | 
				
			||||||
    error::{AssignmentError, IllegalAnnotationContent},
 | 
					    error::{AssignmentError, IllegalAnnotationContent, MismatchedTypes},
 | 
				
			||||||
    expression::{ComptimeValue, DataLocation},
 | 
					    expression::{ComptimeValue, DataLocation, ExpectedType, StorageType},
 | 
				
			||||||
    FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
 | 
					    FunctionData, TranspileAnnotationValue, TranspileError, TranspileResult,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -89,6 +92,25 @@ pub enum VariableData {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy, EnumAsInner)]
 | 
				
			||||||
 | 
					pub enum TranspileAssignmentTarget<'a> {
 | 
				
			||||||
 | 
					    Identifier(&'a Identifier),
 | 
				
			||||||
 | 
					    Indexed(&'a Identifier, &'a Expression),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'a> From<&'a AssignmentDestination> for TranspileAssignmentTarget<'a> {
 | 
				
			||||||
 | 
					    fn from(destination: &'a AssignmentDestination) -> Self {
 | 
				
			||||||
 | 
					        match destination {
 | 
				
			||||||
 | 
					            AssignmentDestination::Identifier(ident) => {
 | 
				
			||||||
 | 
					                TranspileAssignmentTarget::Identifier(ident)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            AssignmentDestination::Indexed(ident, _, expr, _) => {
 | 
				
			||||||
 | 
					                TranspileAssignmentTarget::Indexed(ident, expr)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// A scope that stores variables.
 | 
					/// A scope that stores variables.
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
pub struct Scope<'a> {
 | 
					pub struct Scope<'a> {
 | 
				
			||||||
| 
						 | 
					@ -210,8 +232,14 @@ impl Transpiler {
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<Vec<Command>> {
 | 
					    ) -> TranspileResult<Vec<Command>> {
 | 
				
			||||||
        match declaration {
 | 
					        match declaration {
 | 
				
			||||||
            VariableDeclaration::Single(single) => self.transpile_single_variable_declaration(
 | 
					            VariableDeclaration::Single(declaration) => self.transpile_single_variable_declaration(
 | 
				
			||||||
                single,
 | 
					                declaration,
 | 
				
			||||||
 | 
					                program_identifier,
 | 
				
			||||||
 | 
					                scope,
 | 
				
			||||||
 | 
					                handler,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            VariableDeclaration::Score(declaration) => self.transpile_score_variable_declaration(
 | 
				
			||||||
 | 
					                declaration,
 | 
				
			||||||
                program_identifier,
 | 
					                program_identifier,
 | 
				
			||||||
                scope,
 | 
					                scope,
 | 
				
			||||||
                handler,
 | 
					                handler,
 | 
				
			||||||
| 
						 | 
					@ -222,18 +250,72 @@ impl Transpiler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn transpile_single_variable_declaration(
 | 
					    fn transpile_single_variable_declaration(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        single: &SingleVariableDeclaration,
 | 
					        declaration: &SingleVariableDeclaration,
 | 
				
			||||||
        program_identifier: &str,
 | 
					        program_identifier: &str,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<Vec<Command>> {
 | 
					    ) -> TranspileResult<Vec<Command>> {
 | 
				
			||||||
        let mut deobfuscate_annotations = single
 | 
					        let variable_type = declaration.variable_type().keyword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let (name, target) = self.get_data_location_identifier_pair(
 | 
				
			||||||
 | 
					            declaration,
 | 
				
			||||||
 | 
					            program_identifier,
 | 
				
			||||||
 | 
					            scope,
 | 
				
			||||||
 | 
					            handler,
 | 
				
			||||||
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        match variable_type {
 | 
				
			||||||
 | 
					            KeywordKind::Int => {
 | 
				
			||||||
 | 
					                if !self.datapack.scoreboards().contains_key(&name) {
 | 
				
			||||||
 | 
					                    self.datapack
 | 
				
			||||||
 | 
					                        .register_scoreboard(&name, None::<&str>, None::<&str>);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                scope.set_variable(
 | 
				
			||||||
 | 
					                    declaration.identifier().span.str(),
 | 
				
			||||||
 | 
					                    VariableData::ScoreboardValue {
 | 
				
			||||||
 | 
					                        objective: name.clone(),
 | 
				
			||||||
 | 
					                        target,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            KeywordKind::Bool => {
 | 
				
			||||||
 | 
					                scope.set_variable(
 | 
				
			||||||
 | 
					                    declaration.identifier().span.str(),
 | 
				
			||||||
 | 
					                    VariableData::BooleanStorage {
 | 
				
			||||||
 | 
					                        storage_name: name,
 | 
				
			||||||
 | 
					                        path: target,
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            _ => unreachable!("no other variable types"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        declaration.assignment().as_ref().map_or_else(
 | 
				
			||||||
 | 
					            || Ok(Vec::new()),
 | 
				
			||||||
 | 
					            |assignment| {
 | 
				
			||||||
 | 
					                self.transpile_assignment(
 | 
				
			||||||
 | 
					                    TranspileAssignmentTarget::Identifier(declaration.identifier()),
 | 
				
			||||||
 | 
					                    assignment.expression(),
 | 
				
			||||||
 | 
					                    scope,
 | 
				
			||||||
 | 
					                    handler,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn transpile_score_variable_declaration(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        declaration: &ScoreVariableDeclaration,
 | 
				
			||||||
 | 
					        program_identifier: &str,
 | 
				
			||||||
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
 | 
					    ) -> TranspileResult<Vec<Command>> {
 | 
				
			||||||
 | 
					        let mut deobfuscate_annotations = declaration
 | 
				
			||||||
            .annotations()
 | 
					            .annotations()
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
            .filter(|a| a.has_identifier("deobfuscate"));
 | 
					            .filter(|a| a.has_identifier("deobfuscate"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let variable_type = single.variable_type().keyword;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let deobfuscate_annotation = deobfuscate_annotations.next();
 | 
					        let deobfuscate_annotation = deobfuscate_annotations.next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(duplicate) = deobfuscate_annotations.next() {
 | 
					        if let Some(duplicate) = deobfuscate_annotations.next() {
 | 
				
			||||||
| 
						 | 
					@ -244,69 +326,73 @@ impl Transpiler {
 | 
				
			||||||
            handler.receive(error.clone());
 | 
					            handler.receive(error.clone());
 | 
				
			||||||
            return Err(error);
 | 
					            return Err(error);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let (name, target) =
 | 
					        let name =
 | 
				
			||||||
            self.get_single_data_location_identifiers(single, program_identifier, scope, handler)?;
 | 
					            self.get_data_location_identifier(declaration, program_identifier, scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let criteria = declaration
 | 
				
			||||||
 | 
					            .criteria()
 | 
				
			||||||
 | 
					            .as_ref()
 | 
				
			||||||
 | 
					            .map(|(_, c, _)| c.str_content());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match variable_type {
 | 
					 | 
				
			||||||
            KeywordKind::Int => {
 | 
					 | 
				
			||||||
        if !self.datapack.scoreboards().contains_key(&name) {
 | 
					        if !self.datapack.scoreboards().contains_key(&name) {
 | 
				
			||||||
            self.datapack
 | 
					            self.datapack
 | 
				
			||||||
                        .register_scoreboard(&name, None::<&str>, None::<&str>);
 | 
					                .register_scoreboard(&name, criteria, None::<&str>);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        scope.set_variable(
 | 
					        scope.set_variable(
 | 
				
			||||||
                    single.identifier().span.str(),
 | 
					            declaration.identifier().span.str(),
 | 
				
			||||||
                    VariableData::ScoreboardValue {
 | 
					            VariableData::Scoreboard {
 | 
				
			||||||
                objective: name.clone(),
 | 
					                objective: name.clone(),
 | 
				
			||||||
                        target,
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            KeywordKind::Bool => {
 | 
					 | 
				
			||||||
                scope.set_variable(
 | 
					 | 
				
			||||||
                    single.identifier().span.str(),
 | 
					 | 
				
			||||||
                    VariableData::BooleanStorage {
 | 
					 | 
				
			||||||
                        storage_name: name,
 | 
					 | 
				
			||||||
                        path: target,
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                );
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            _ => unreachable!("no other variable types"),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        single.assignment().as_ref().map_or_else(
 | 
					        // TODO: implement assignment when map literal is implemented
 | 
				
			||||||
            || Ok(Vec::new()),
 | 
					        Ok(Vec::new())
 | 
				
			||||||
            |assignment| {
 | 
					 | 
				
			||||||
                self.transpile_assignment(
 | 
					 | 
				
			||||||
                    single.identifier(),
 | 
					 | 
				
			||||||
                    assignment.expression(),
 | 
					 | 
				
			||||||
                    scope,
 | 
					 | 
				
			||||||
                    handler,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(super) fn transpile_assignment(
 | 
					    pub(super) fn transpile_assignment(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        identifier: &Identifier,
 | 
					        destination: TranspileAssignmentTarget,
 | 
				
			||||||
        expression: &crate::syntax::syntax_tree::expression::Expression,
 | 
					        expression: &crate::syntax::syntax_tree::expression::Expression,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<Vec<Command>> {
 | 
					    ) -> TranspileResult<Vec<Command>> {
 | 
				
			||||||
 | 
					        let (identifier, indexing_value) = match destination {
 | 
				
			||||||
 | 
					            TranspileAssignmentTarget::Identifier(ident) => (ident, None),
 | 
				
			||||||
 | 
					            TranspileAssignmentTarget::Indexed(ident, expression) => {
 | 
				
			||||||
 | 
					                (ident, expression.comptime_eval(scope, handler))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
        if let Some(target) = scope.get_variable(identifier.span.str()) {
 | 
					        if let Some(target) = scope.get_variable(identifier.span.str()) {
 | 
				
			||||||
            let data_location = match target.as_ref() {
 | 
					            let data_location = match target.as_ref() {
 | 
				
			||||||
                VariableData::BooleanStorage { storage_name, path } => Ok(DataLocation::Storage {
 | 
					                VariableData::BooleanStorage { storage_name, path } => {
 | 
				
			||||||
 | 
					                    // TODO: make sure that no indexing is done
 | 
				
			||||||
 | 
					                    Ok(DataLocation::Storage {
 | 
				
			||||||
                        storage_name: storage_name.to_owned(),
 | 
					                        storage_name: storage_name.to_owned(),
 | 
				
			||||||
                        path: path.to_owned(),
 | 
					                        path: path.to_owned(),
 | 
				
			||||||
                        r#type: super::expression::StorageType::Boolean,
 | 
					                        r#type: super::expression::StorageType::Boolean,
 | 
				
			||||||
                }),
 | 
					                    })
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                VariableData::ScoreboardValue { objective, target } => {
 | 
					                VariableData::ScoreboardValue { objective, target } => {
 | 
				
			||||||
 | 
					                    // TODO: make sure that no indexing is done
 | 
				
			||||||
                    Ok(DataLocation::ScoreboardValue {
 | 
					                    Ok(DataLocation::ScoreboardValue {
 | 
				
			||||||
                        objective: objective.to_owned(),
 | 
					                        objective: objective.to_owned(),
 | 
				
			||||||
                        target: target.to_owned(),
 | 
					                        target: target.to_owned(),
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                VariableData::Scoreboard { objective } => match indexing_value {
 | 
				
			||||||
 | 
					                    Some(ComptimeValue::String(s)) => Ok(DataLocation::ScoreboardValue {
 | 
				
			||||||
 | 
					                        objective: objective.clone(),
 | 
				
			||||||
 | 
					                        target: s,
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					                    Some(ComptimeValue::MacroString(s)) => {
 | 
				
			||||||
 | 
					                        todo!("indexing scoreboard with macro string: {s}")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Some(_) => todo!("invalid indexing value"),
 | 
				
			||||||
 | 
					                    None => {
 | 
				
			||||||
 | 
					                        todo!("cannot assign to scoreboard without indexing")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
                VariableData::Function { .. } | VariableData::MacroParameter { .. } => {
 | 
					                VariableData::Function { .. } | VariableData::MacroParameter { .. } => {
 | 
				
			||||||
                    let err = TranspileError::AssignmentError(AssignmentError {
 | 
					                    let err = TranspileError::AssignmentError(AssignmentError {
 | 
				
			||||||
                        identifier: identifier.span(),
 | 
					                        identifier: identifier.span(),
 | 
				
			||||||
| 
						 | 
					@ -335,9 +421,88 @@ impl Transpiler {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn get_data_location_identifier(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        declaration: &ScoreVariableDeclaration,
 | 
				
			||||||
 | 
					        program_identifier: &str,
 | 
				
			||||||
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
 | 
					    ) -> TranspileResult<String> {
 | 
				
			||||||
 | 
					        let mut deobfuscate_annotations = declaration
 | 
				
			||||||
 | 
					            .annotations()
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .filter(|a| a.has_identifier("deobfuscate"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let deobfuscate_annotation = deobfuscate_annotations.next();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if let Some(duplicate) = deobfuscate_annotations.next() {
 | 
				
			||||||
 | 
					            let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
 | 
					                annotation: duplicate.span(),
 | 
				
			||||||
 | 
					                message: "Multiple deobfuscate annotations are not allowed.".to_string(),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            handler.receive(error.clone());
 | 
				
			||||||
 | 
					            return Err(error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if let Some(deobfuscate_annotation) = deobfuscate_annotation {
 | 
				
			||||||
 | 
					            let deobfuscate_annotation_value =
 | 
				
			||||||
 | 
					                TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            match deobfuscate_annotation_value {
 | 
				
			||||||
 | 
					                TranspileAnnotationValue::Expression(expr) => {
 | 
				
			||||||
 | 
					                    if let Some(name_eval) = expr
 | 
				
			||||||
 | 
					                        .comptime_eval(scope, handler)
 | 
				
			||||||
 | 
					                        .and_then(|val| val.to_string_no_macro())
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        if !crate::util::is_valid_scoreboard_objective_name(&name_eval) {
 | 
				
			||||||
 | 
					                            let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
 | 
					                                                    annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
 | 
					                                                    message: "Deobfuscate annotation must be a valid scoreboard objective name.".to_string()
 | 
				
			||||||
 | 
					                                                });
 | 
				
			||||||
 | 
					                            handler.receive(error.clone());
 | 
				
			||||||
 | 
					                            return Err(error);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        Ok(name_eval)
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        let error = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
 | 
					                                                annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
 | 
					                                                message: "Deobfuscate annotation could not have been evaluated at compile time.".to_string()
 | 
				
			||||||
 | 
					                                            });
 | 
				
			||||||
 | 
					                        handler.receive(error.clone());
 | 
				
			||||||
 | 
					                        Err(error)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                TranspileAnnotationValue::None => {
 | 
				
			||||||
 | 
					                    Ok(declaration.identifier().span.str().to_string())
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                TranspileAnnotationValue::Map(_) => {
 | 
				
			||||||
 | 
					                    let error =
 | 
				
			||||||
 | 
					                        TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
 | 
				
			||||||
 | 
					                            annotation: deobfuscate_annotation.span(),
 | 
				
			||||||
 | 
					                            message: "Deobfuscate annotation must have no value or must be string."
 | 
				
			||||||
 | 
					                                .to_string(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    handler.receive(error.clone());
 | 
				
			||||||
 | 
					                    Err(error)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            let hashed = md5::hash(program_identifier).to_hex_lowercase();
 | 
				
			||||||
 | 
					            let name = "shu_values_".to_string() + &hashed;
 | 
				
			||||||
 | 
					            let identifier_name = declaration.identifier().span.str();
 | 
				
			||||||
 | 
					            let scope_ident = self.temp_counter;
 | 
				
			||||||
 | 
					            self.temp_counter = self.temp_counter.wrapping_add(1);
 | 
				
			||||||
 | 
					            let mut target = md5::hash(format!(
 | 
				
			||||||
 | 
					                "{scope_ident}\0{identifier_name}\0{shadowed}",
 | 
				
			||||||
 | 
					                shadowed = scope.get_variable_shadow_count(identifier_name)
 | 
				
			||||||
 | 
					            ))
 | 
				
			||||||
 | 
					            .to_hex_lowercase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Ok(name)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[expect(clippy::too_many_lines)]
 | 
					    #[expect(clippy::too_many_lines)]
 | 
				
			||||||
    #[cfg(feature = "shulkerbox")]
 | 
					    fn get_data_location_identifier_pair(
 | 
				
			||||||
    fn get_single_data_location_identifiers(
 | 
					 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        single: &SingleVariableDeclaration,
 | 
					        single: &SingleVariableDeclaration,
 | 
				
			||||||
        program_identifier: &str,
 | 
					        program_identifier: &str,
 | 
				
			||||||
| 
						 | 
					@ -385,10 +550,10 @@ impl Transpiler {
 | 
				
			||||||
                        if let (Some(name_eval), Some(target_eval)) = (
 | 
					                        if let (Some(name_eval), Some(target_eval)) = (
 | 
				
			||||||
                            objective
 | 
					                            objective
 | 
				
			||||||
                                .comptime_eval(scope, handler)
 | 
					                                .comptime_eval(scope, handler)
 | 
				
			||||||
                                .map(|val| val.to_string()),
 | 
					                                .and_then(|val| val.to_string_no_macro()),
 | 
				
			||||||
                            target
 | 
					                            target
 | 
				
			||||||
                                .comptime_eval(scope, handler)
 | 
					                                .comptime_eval(scope, handler)
 | 
				
			||||||
                                .map(|val| val.to_string()),
 | 
					                                .and_then(|val| val.to_string_no_macro()),
 | 
				
			||||||
                        ) {
 | 
					                        ) {
 | 
				
			||||||
                            // TODO: change invalid criteria if boolean
 | 
					                            // TODO: change invalid criteria if boolean
 | 
				
			||||||
                            if !crate::util::is_valid_scoreboard_objective_name(&name_eval) {
 | 
					                            if !crate::util::is_valid_scoreboard_objective_name(&name_eval) {
 | 
				
			||||||
| 
						 | 
					@ -468,6 +633,139 @@ impl Transpiler {
 | 
				
			||||||
            Ok((name, target))
 | 
					            Ok((name, target))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Move data from location `from` to location `to`.
 | 
				
			||||||
 | 
					    ///
 | 
				
			||||||
 | 
					    /// # Errors
 | 
				
			||||||
 | 
					    /// - if the data type does not match
 | 
				
			||||||
 | 
					    #[expect(clippy::too_many_lines)]
 | 
				
			||||||
 | 
					    pub fn move_data(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        from: &DataLocation,
 | 
				
			||||||
 | 
					        to: &DataLocation,
 | 
				
			||||||
 | 
					        expression: &impl SourceElement,
 | 
				
			||||||
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
 | 
					    ) -> TranspileResult<Vec<Command>> {
 | 
				
			||||||
 | 
					        match from {
 | 
				
			||||||
 | 
					            DataLocation::Storage {
 | 
				
			||||||
 | 
					                storage_name,
 | 
				
			||||||
 | 
					                path,
 | 
				
			||||||
 | 
					                r#type,
 | 
				
			||||||
 | 
					            } => match r#type {
 | 
				
			||||||
 | 
					                StorageType::Boolean
 | 
				
			||||||
 | 
					                | StorageType::Byte
 | 
				
			||||||
 | 
					                | StorageType::Int
 | 
				
			||||||
 | 
					                | StorageType::Long
 | 
				
			||||||
 | 
					                | StorageType::Double => match to {
 | 
				
			||||||
 | 
					                    DataLocation::ScoreboardValue { objective, target } => {
 | 
				
			||||||
 | 
					                        let cmd = Command::Execute(Execute::Store(
 | 
				
			||||||
 | 
					                            format!("store result score {target} {objective}").into(),
 | 
				
			||||||
 | 
					                            Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
				
			||||||
 | 
					                                "data get storage {storage_name} {path}"
 | 
				
			||||||
 | 
					                            ))))),
 | 
				
			||||||
 | 
					                        ));
 | 
				
			||||||
 | 
					                        Ok(vec![cmd])
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    DataLocation::Tag { tag_name, entity } => {
 | 
				
			||||||
 | 
					                        let cmd = Command::Execute(Execute::If(
 | 
				
			||||||
 | 
					                            Condition::Atom(
 | 
				
			||||||
 | 
					                                format!("data storage {storage_name} {{{path}: 1b}}").into(),
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
 | 
					                            Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
				
			||||||
 | 
					                                "tag {entity} add {tag_name}"
 | 
				
			||||||
 | 
					                            ))))),
 | 
				
			||||||
 | 
					                            Some(Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
				
			||||||
 | 
					                                "tag {entity} remove {tag_name}"
 | 
				
			||||||
 | 
					                            )))))),
 | 
				
			||||||
 | 
					                        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        Ok(vec![cmd])
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    DataLocation::Storage {
 | 
				
			||||||
 | 
					                        storage_name: target_storage_name,
 | 
				
			||||||
 | 
					                        path: target_path,
 | 
				
			||||||
 | 
					                        r#type,
 | 
				
			||||||
 | 
					                    } => {
 | 
				
			||||||
 | 
					                        if matches!(r#type, StorageType::Boolean) {
 | 
				
			||||||
 | 
					                            let cmd = Command::Raw(format!(
 | 
				
			||||||
 | 
					                                        "data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"
 | 
				
			||||||
 | 
					                                    ));
 | 
				
			||||||
 | 
					                            Ok(vec![cmd])
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                                expression: expression.span(),
 | 
				
			||||||
 | 
					                                expected_type: to.value_type().into(),
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            handler.receive(err.clone());
 | 
				
			||||||
 | 
					                            Err(err)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            DataLocation::ScoreboardValue {
 | 
				
			||||||
 | 
					                objective,
 | 
				
			||||||
 | 
					                target: score_target,
 | 
				
			||||||
 | 
					            } => match to {
 | 
				
			||||||
 | 
					                DataLocation::ScoreboardValue {
 | 
				
			||||||
 | 
					                    objective: target_objective,
 | 
				
			||||||
 | 
					                    target: target_target,
 | 
				
			||||||
 | 
					                } => {
 | 
				
			||||||
 | 
					                    let cmd = Command::Raw(format!(
 | 
				
			||||||
 | 
					                        "scoreboard players operation {target_target} {target_objective} = {score_target} {objective}"
 | 
				
			||||||
 | 
					                    ));
 | 
				
			||||||
 | 
					                    Ok(vec![cmd])
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                DataLocation::Storage {
 | 
				
			||||||
 | 
					                    storage_name,
 | 
				
			||||||
 | 
					                    path,
 | 
				
			||||||
 | 
					                    r#type,
 | 
				
			||||||
 | 
					                } => {
 | 
				
			||||||
 | 
					                    if matches!(
 | 
				
			||||||
 | 
					                        r#type,
 | 
				
			||||||
 | 
					                        StorageType::Byte
 | 
				
			||||||
 | 
					                            | StorageType::Double
 | 
				
			||||||
 | 
					                            | StorageType::Int
 | 
				
			||||||
 | 
					                            | StorageType::Long
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        let cmd = Command::Execute(Execute::Store(
 | 
				
			||||||
 | 
					                            format!(
 | 
				
			||||||
 | 
					                                "result storage {storage_name} {path} {t} 1.0",
 | 
				
			||||||
 | 
					                                t = r#type.as_str()
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                            .into(),
 | 
				
			||||||
 | 
					                            Box::new(Execute::Run(Box::new(Command::Raw(format!(
 | 
				
			||||||
 | 
					                                "scoreboard players get {score_target} {objective}"
 | 
				
			||||||
 | 
					                            ))))),
 | 
				
			||||||
 | 
					                        ));
 | 
				
			||||||
 | 
					                        Ok(vec![cmd])
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                            expression: expression.span(),
 | 
				
			||||||
 | 
					                            expected_type: to.value_type().into(),
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        handler.receive(err.clone());
 | 
				
			||||||
 | 
					                        Err(err)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                DataLocation::Tag { .. } => {
 | 
				
			||||||
 | 
					                    let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                        expected_type: ExpectedType::Boolean,
 | 
				
			||||||
 | 
					                        expression: expression.span(),
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
 | 
					                    Err(err)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            DataLocation::Tag { .. } => {
 | 
				
			||||||
 | 
					                let err = TranspileError::MismatchedTypes(MismatchedTypes {
 | 
				
			||||||
 | 
					                    expected_type: to.value_type().into(),
 | 
				
			||||||
 | 
					                    expression: expression.span(),
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                handler.receive(err.clone());
 | 
				
			||||||
 | 
					                Err(err)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue