Compare commits
	
		
			No commits in common. "1e82d2321f036dfa71c8df4a1741cc6cb5fbf0b1" and "469b8d3875744d1ab7b2d152fbe809ede15a17ea" have entirely different histories.
		
	
	
		
			1e82d2321f
			...
			469b8d3875
		
	
		
							
								
								
									
										13
									
								
								src/lib.rs
								
								
								
								
							
							
						
						
									
										13
									
								
								src/lib.rs
								
								
								
								
							| 
						 | 
					@ -115,6 +115,9 @@ pub fn parse(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Transpiles the given source code into a shulkerbox [`Datapack`].
 | 
					/// Transpiles the given source code into a shulkerbox [`Datapack`].
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
 | 
					/// # Parameters:
 | 
				
			||||||
 | 
					/// - `script_paths`: A list of tuples containing the identifier and the path of each script file.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
/// # Errors
 | 
					/// # Errors
 | 
				
			||||||
/// - If an error occurs during [`parse()`]
 | 
					/// - If an error occurs during [`parse()`]
 | 
				
			||||||
/// - If an error occurs while transpiling the source code.
 | 
					/// - If an error occurs while transpiling the source code.
 | 
				
			||||||
| 
						 | 
					@ -161,7 +164,15 @@ where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Ok(program)
 | 
					            Ok(program)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .collect::<Result<Vec<_>>>()?;
 | 
					        .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if programs.iter().any(Result::is_err) {
 | 
				
			||||||
 | 
					        return Err(programs.into_iter().find_map(Result::err).unwrap());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let programs = programs
 | 
				
			||||||
 | 
					        .into_iter()
 | 
				
			||||||
 | 
					        .filter_map(Result::ok)
 | 
				
			||||||
 | 
					        .collect::<Vec<_>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tracing::info!("Transpiling the source code.");
 | 
					    tracing::info!("Transpiling the source code.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@ fn get_global_scope(declarations: &[Declaration]) -> SemanticScope<'static> {
 | 
				
			||||||
    let scope = SemanticScope::new();
 | 
					    let scope = SemanticScope::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for declaration in declarations {
 | 
					    for declaration in declarations {
 | 
				
			||||||
        declaration.add_to_semantic_scope(&scope);
 | 
					        declaration.add_to_scope(&scope);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    scope
 | 
					    scope
 | 
				
			||||||
| 
						 | 
					@ -83,32 +83,23 @@ impl Namespace {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Declaration {
 | 
					impl Declaration {
 | 
				
			||||||
    fn add_to_semantic_scope(&self, scope: &SemanticScope) {
 | 
					    fn add_to_scope(&self, scope: &SemanticScope) {
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::Function(func) => {
 | 
					            Self::Function(func) => {
 | 
				
			||||||
                let name = func.identifier();
 | 
					                let name = func.identifier();
 | 
				
			||||||
                scope.set_variable(name.span.str(), VariableType::Function);
 | 
					                scope.set_variable(name.span.str(), VariableType::Function);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::GlobalVariable((_, var, _)) => {
 | 
					            Self::Import(imp) => match imp.items() {
 | 
				
			||||||
                let name = var.identifier();
 | 
					                ImportItems::All(_) => {}
 | 
				
			||||||
                let var_type = match var {
 | 
					                ImportItems::Named(items) => {
 | 
				
			||||||
                    VariableDeclaration::Array(arr) => match arr.variable_type().keyword {
 | 
					                    for item in items.elements() {
 | 
				
			||||||
                        KeywordKind::Bool => VariableType::BooleanStorageArray,
 | 
					                        if scope.get_variable(item.span.str()) != Some(VariableType::Function) {
 | 
				
			||||||
                        KeywordKind::Int => VariableType::ScoreboardArray,
 | 
					                            scope.set_variable(item.span.str(), VariableType::Function);
 | 
				
			||||||
                        _ => unreachable!("variable type is not a valid type"),
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    VariableDeclaration::Score(_) => VariableType::Scoreboard,
 | 
					 | 
				
			||||||
                    VariableDeclaration::Tag(_) => VariableType::Tag,
 | 
					 | 
				
			||||||
                    VariableDeclaration::Single(single) => match single.variable_type().keyword {
 | 
					 | 
				
			||||||
                        KeywordKind::Bool => VariableType::BooleanStorage,
 | 
					 | 
				
			||||||
                        KeywordKind::Int => VariableType::ScoreboardValue,
 | 
					 | 
				
			||||||
                        _ => unreachable!("variable type is not a valid type"),
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    VariableDeclaration::ComptimeValue(_) => VariableType::ComptimeValue,
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                scope.set_variable(name.span.str(), var_type);
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
            Self::Import(_) | Self::Tag(_) => {}
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            Self::Tag(_) => {}
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,8 +137,6 @@ impl Declaration {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Self::GlobalVariable((_, var, _)) => var.analyze_semantics(scope, handler),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            Self::Tag(_) => Ok(()),
 | 
					            Self::Tag(_) => Ok(()),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -560,26 +549,16 @@ impl Primary {
 | 
				
			||||||
            Self::Boolean(_) | Self::Integer(_) | Self::StringLiteral(_) => Ok(()),
 | 
					            Self::Boolean(_) | Self::Integer(_) | Self::StringLiteral(_) => Ok(()),
 | 
				
			||||||
            Self::MacroStringLiteral(lit) => lit.analyze_semantics(scope, handler),
 | 
					            Self::MacroStringLiteral(lit) => lit.analyze_semantics(scope, handler),
 | 
				
			||||||
            Self::FunctionCall(call) => {
 | 
					            Self::FunctionCall(call) => {
 | 
				
			||||||
                let var = scope.get_variable(call.identifier().span.str());
 | 
					                if scope.get_variable(call.identifier().span.str()) == Some(VariableType::Function)
 | 
				
			||||||
                var.map_or_else(
 | 
					                {
 | 
				
			||||||
                    || {
 | 
					                    Ok(())
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
                    let err = error::Error::UnknownIdentifier(UnknownIdentifier {
 | 
					                    let err = error::Error::UnknownIdentifier(UnknownIdentifier {
 | 
				
			||||||
                        identifier: call.identifier().span.clone(),
 | 
					                        identifier: call.identifier().span.clone(),
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    handler.receive(err.clone());
 | 
					                    handler.receive(err.clone());
 | 
				
			||||||
                    Err(err)
 | 
					                    Err(err)
 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    |var| match var {
 | 
					 | 
				
			||||||
                        VariableType::Function | VariableType::InternalFunction => Ok(()),
 | 
					 | 
				
			||||||
                        _ => {
 | 
					 | 
				
			||||||
                            let err = error::Error::UnexpectedExpression(UnexpectedExpression(
 | 
					 | 
				
			||||||
                                Expression::Primary(self.clone()),
 | 
					 | 
				
			||||||
                            ));
 | 
					 | 
				
			||||||
                            handler.receive(err.clone());
 | 
					 | 
				
			||||||
                            Err(err)
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::Indexed(indexed) => {
 | 
					            Self::Indexed(indexed) => {
 | 
				
			||||||
                if let Self::Identifier(ident) = indexed.object().as_ref() {
 | 
					                if let Self::Identifier(ident) = indexed.object().as_ref() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,10 +23,7 @@ use crate::{
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{
 | 
					use super::{statement::Block, Annotation, ConnectedList, DelimitedList};
 | 
				
			||||||
    statement::{Block, VariableDeclaration},
 | 
					 | 
				
			||||||
    Annotation, ConnectedList, DelimitedList,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Represents a declaration in the syntax tree.
 | 
					/// Represents a declaration in the syntax tree.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
| 
						 | 
					@ -36,8 +33,6 @@ use super::{
 | 
				
			||||||
/// Declaration:
 | 
					/// Declaration:
 | 
				
			||||||
///    Function
 | 
					///    Function
 | 
				
			||||||
///    | Import
 | 
					///    | Import
 | 
				
			||||||
///     | Tag
 | 
					 | 
				
			||||||
///     | ('pub'? VariableDeclaration ';')
 | 
					 | 
				
			||||||
///   ;
 | 
					///   ;
 | 
				
			||||||
/// ```
 | 
					/// ```
 | 
				
			||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
					#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
 | 
				
			||||||
| 
						 | 
					@ -46,7 +41,6 @@ pub enum Declaration {
 | 
				
			||||||
    Function(Function),
 | 
					    Function(Function),
 | 
				
			||||||
    Import(Import),
 | 
					    Import(Import),
 | 
				
			||||||
    Tag(Tag),
 | 
					    Tag(Tag),
 | 
				
			||||||
    GlobalVariable((Option<Keyword>, VariableDeclaration, Punctuation)),
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl SourceElement for Declaration {
 | 
					impl SourceElement for Declaration {
 | 
				
			||||||
| 
						 | 
					@ -55,11 +49,6 @@ impl SourceElement for Declaration {
 | 
				
			||||||
            Self::Function(function) => function.span(),
 | 
					            Self::Function(function) => function.span(),
 | 
				
			||||||
            Self::Import(import) => import.span(),
 | 
					            Self::Import(import) => import.span(),
 | 
				
			||||||
            Self::Tag(tag) => tag.span(),
 | 
					            Self::Tag(tag) => tag.span(),
 | 
				
			||||||
            Self::GlobalVariable((pub_kw, variable, semicolon)) => pub_kw
 | 
					 | 
				
			||||||
                .as_ref()
 | 
					 | 
				
			||||||
                .map_or_else(|| variable.span(), SourceElement::span)
 | 
					 | 
				
			||||||
                .join(&semicolon.span)
 | 
					 | 
				
			||||||
                .expect("invalid declaration span"),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -70,17 +59,13 @@ impl Declaration {
 | 
				
			||||||
    /// # Errors
 | 
					    /// # Errors
 | 
				
			||||||
    /// - if the annotation is invalid for the target declaration.
 | 
					    /// - if the annotation is invalid for the target declaration.
 | 
				
			||||||
    pub fn with_annotation(self, annotation: Annotation) -> ParseResult<Self> {
 | 
					    pub fn with_annotation(self, annotation: Annotation) -> ParseResult<Self> {
 | 
				
			||||||
 | 
					        #[expect(clippy::single_match_else)]
 | 
				
			||||||
        match self {
 | 
					        match self {
 | 
				
			||||||
            Self::Function(mut function) => {
 | 
					            Self::Function(mut function) => {
 | 
				
			||||||
                function.annotations.push_front(annotation);
 | 
					                function.annotations.push_front(annotation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Ok(Self::Function(function))
 | 
					                Ok(Self::Function(function))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Self::GlobalVariable((pub_kw, var, semi)) => {
 | 
					 | 
				
			||||||
                let var_with_annotation = var.with_annotation(annotation)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Ok(Self::GlobalVariable((pub_kw, var_with_annotation, semi)))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            _ => {
 | 
					            _ => {
 | 
				
			||||||
                let err = Error::InvalidAnnotation(InvalidAnnotation {
 | 
					                let err = Error::InvalidAnnotation(InvalidAnnotation {
 | 
				
			||||||
                    annotation: annotation.assignment.identifier.span,
 | 
					                    annotation: annotation.assignment.identifier.span,
 | 
				
			||||||
| 
						 | 
					@ -357,19 +342,11 @@ impl Parser<'_> {
 | 
				
			||||||
            Reading::Atomic(Token::Keyword(pub_keyword))
 | 
					            Reading::Atomic(Token::Keyword(pub_keyword))
 | 
				
			||||||
                if pub_keyword.keyword == KeywordKind::Pub =>
 | 
					                if pub_keyword.keyword == KeywordKind::Pub =>
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                if let Ok(function) = self.try_parse(|parser| parser.parse_function(&VoidHandler)) {
 | 
					                let function = self.parse_function(handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
 | 
					                tracing::trace!("Parsed function '{:?}'", function.identifier.span.str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Ok(Declaration::Function(function))
 | 
					                Ok(Declaration::Function(function))
 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    // eat the pub keyword
 | 
					 | 
				
			||||||
                    self.forward();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    let var = self.parse_variable_declaration(handler)?;
 | 
					 | 
				
			||||||
                    let semi = self.parse_punctuation(';', true, handler)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    Ok(Declaration::GlobalVariable((Some(pub_keyword), var, semi)))
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // parse annotations
 | 
					            // parse annotations
 | 
				
			||||||
| 
						 | 
					@ -480,18 +457,6 @@ impl Parser<'_> {
 | 
				
			||||||
                }))
 | 
					                }))
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Reading::Atomic(Token::Keyword(keyword))
 | 
					 | 
				
			||||||
                if matches!(
 | 
					 | 
				
			||||||
                    keyword.keyword,
 | 
					 | 
				
			||||||
                    KeywordKind::Int | KeywordKind::Bool | KeywordKind::Val
 | 
					 | 
				
			||||||
                ) =>
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                let var = self.parse_variable_declaration(handler)?;
 | 
					 | 
				
			||||||
                let semi = self.parse_punctuation(';', true, handler)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                Ok(Declaration::GlobalVariable((None, var, semi)))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            unexpected => {
 | 
					            unexpected => {
 | 
				
			||||||
                // make progress
 | 
					                // make progress
 | 
				
			||||||
                self.forward();
 | 
					                self.forward();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -358,12 +358,11 @@ impl Primary {
 | 
				
			||||||
                    })
 | 
					                    })
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                |var| match var.as_ref() {
 | 
					                |var| match var.as_ref() {
 | 
				
			||||||
                    VariableData::ComptimeValue {
 | 
					                    VariableData::ComptimeValue { value } => {
 | 
				
			||||||
                        value,
 | 
					                        value.read().unwrap().clone().ok_or_else(|| NotComptime {
 | 
				
			||||||
                        read_only: _,
 | 
					 | 
				
			||||||
                    } => value.read().unwrap().clone().ok_or_else(|| NotComptime {
 | 
					 | 
				
			||||||
                            expression: ident.span.clone(),
 | 
					                            expression: ident.span.clone(),
 | 
				
			||||||
                    }),
 | 
					                        })
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    _ => Err(NotComptime {
 | 
					                    _ => Err(NotComptime {
 | 
				
			||||||
                        expression: self.span(),
 | 
					                        expression: self.span(),
 | 
				
			||||||
                    }),
 | 
					                    }),
 | 
				
			||||||
| 
						 | 
					@ -1277,7 +1276,6 @@ impl Transpiler {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[expect(clippy::too_many_lines)]
 | 
					 | 
				
			||||||
    fn transpile_scoreboard_operation(
 | 
					    fn transpile_scoreboard_operation(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        binary: &Binary,
 | 
					        binary: &Binary,
 | 
				
			||||||
| 
						 | 
					@ -1305,15 +1303,6 @@ impl Transpiler {
 | 
				
			||||||
            scope,
 | 
					            scope,
 | 
				
			||||||
            handler,
 | 
					            handler,
 | 
				
			||||||
        )?;
 | 
					        )?;
 | 
				
			||||||
 | 
					 | 
				
			||||||
        let (right_cmds, rhs_score) =
 | 
					 | 
				
			||||||
            if let Ok(ComptimeValue::Integer(int)) = right.comptime_eval(scope, handler) {
 | 
					 | 
				
			||||||
                self.initialize_constant_score(int);
 | 
					 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    Vec::new(),
 | 
					 | 
				
			||||||
                    ("shu_constants", std::borrow::Cow::Owned(int.to_string())),
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
        let right_cmds = self.transpile_expression(
 | 
					        let right_cmds = self.transpile_expression(
 | 
				
			||||||
            right,
 | 
					            right,
 | 
				
			||||||
            &DataLocation::ScoreboardValue {
 | 
					            &DataLocation::ScoreboardValue {
 | 
				
			||||||
| 
						 | 
					@ -1324,19 +1313,10 @@ impl Transpiler {
 | 
				
			||||||
            handler,
 | 
					            handler,
 | 
				
			||||||
        )?;
 | 
					        )?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                (
 | 
					 | 
				
			||||||
                    right_cmds,
 | 
					 | 
				
			||||||
                    (
 | 
					 | 
				
			||||||
                        temp_objective.as_str(),
 | 
					 | 
				
			||||||
                        std::borrow::Cow::Borrowed(&temp_locations[1]),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let calc_cmds = {
 | 
					        let calc_cmds = {
 | 
				
			||||||
            let (target_objective, target) = score_target_location;
 | 
					            let (target_objective, target) = score_target_location;
 | 
				
			||||||
            let source = rhs_score.1.as_ref();
 | 
					            let source = &temp_locations[1];
 | 
				
			||||||
            let source_objective = rhs_score.0;
 | 
					            let source_objective = &temp_objective;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let operation = match operator {
 | 
					            let operation = match operator {
 | 
				
			||||||
                BinaryOperator::Add(_) => "+=",
 | 
					                BinaryOperator::Add(_) => "+=",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
use chksum_md5 as md5;
 | 
					use chksum_md5 as md5;
 | 
				
			||||||
use enum_as_inner::EnumAsInner;
 | 
					use enum_as_inner::EnumAsInner;
 | 
				
			||||||
use std::{borrow::ToOwned, collections::BTreeMap, sync::Arc};
 | 
					use std::{collections::BTreeMap, sync::Arc};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use shulkerbox::datapack::{Command, Execute};
 | 
					use shulkerbox::datapack::{Command, Execute};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,16 +48,31 @@ impl Transpiler {
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<(String, TranspiledFunctionArguments)> {
 | 
					    ) -> TranspileResult<(String, TranspiledFunctionArguments)> {
 | 
				
			||||||
        let program_identifier = identifier_span.source_file().identifier();
 | 
					        let program_identifier = identifier_span.source_file().identifier();
 | 
				
			||||||
        let function = scope.get_variable(identifier_span.str());
 | 
					        let program_query = (
 | 
				
			||||||
        let already_transpiled = function
 | 
					            program_identifier.to_string(),
 | 
				
			||||||
            .as_ref()
 | 
					            identifier_span.str().to_string(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        let alias_query = self.aliases.get(&program_query).cloned();
 | 
				
			||||||
 | 
					        let already_transpiled = scope
 | 
				
			||||||
 | 
					            .get_variable(identifier_span.str())
 | 
				
			||||||
            .expect("called function should be in scope")
 | 
					            .expect("called function should be in scope")
 | 
				
			||||||
            .as_ref()
 | 
					            .as_ref()
 | 
				
			||||||
            .as_function()
 | 
					            .as_function()
 | 
				
			||||||
            .map(|(_, path, _)| path.get().is_some())
 | 
					            .map(|(_, path)| path.get().is_some())
 | 
				
			||||||
            .expect("called variable should be of type function");
 | 
					            .expect("called variable should be of type function");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let function_data = function.ok_or_else(|| {
 | 
					        let function_data = scope
 | 
				
			||||||
 | 
					            .get_variable(identifier_span.str())
 | 
				
			||||||
 | 
					            .or_else(|| {
 | 
				
			||||||
 | 
					                alias_query
 | 
				
			||||||
 | 
					                    .clone()
 | 
				
			||||||
 | 
					                    .and_then(|(alias_program_identifier, alias_function_name)| {
 | 
				
			||||||
 | 
					                        self.scopes
 | 
				
			||||||
 | 
					                            .get(&alias_program_identifier)
 | 
				
			||||||
 | 
					                            .and_then(|s| s.get_variable(&alias_function_name))
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .ok_or_else(|| {
 | 
				
			||||||
                let error = TranspileError::MissingFunctionDeclaration(
 | 
					                let error = TranspileError::MissingFunctionDeclaration(
 | 
				
			||||||
                    MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
 | 
					                    MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
| 
						 | 
					@ -68,7 +83,6 @@ impl Transpiler {
 | 
				
			||||||
        let VariableData::Function {
 | 
					        let VariableData::Function {
 | 
				
			||||||
            function_data,
 | 
					            function_data,
 | 
				
			||||||
            path: function_path,
 | 
					            path: function_path,
 | 
				
			||||||
            function_scope,
 | 
					 | 
				
			||||||
        } = function_data.as_ref()
 | 
					        } = function_data.as_ref()
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
            unreachable!("must be of correct type, otherwise errored out before");
 | 
					            unreachable!("must be of correct type, otherwise errored out before");
 | 
				
			||||||
| 
						 | 
					@ -120,6 +134,8 @@ impl Transpiler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            function_path.set(function_location.clone()).unwrap();
 | 
					            function_path.set(function_location.clone()).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let function_scope = Scope::with_parent(scope);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (i, param) in function_data.parameters.iter().enumerate() {
 | 
					            for (i, param) in function_data.parameters.iter().enumerate() {
 | 
				
			||||||
                let param_str = param.identifier().span.str();
 | 
					                let param_str = param.identifier().span.str();
 | 
				
			||||||
                match param.variable_type() {
 | 
					                match param.variable_type() {
 | 
				
			||||||
| 
						 | 
					@ -165,7 +181,7 @@ impl Transpiler {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let commands =
 | 
					            let commands =
 | 
				
			||||||
                self.transpile_function(&statements, program_identifier, function_scope, handler)?;
 | 
					                self.transpile_function(&statements, program_identifier, &function_scope, handler)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let namespace = self.datapack.namespace_mut(&function_data.namespace);
 | 
					            let namespace = self.datapack.namespace_mut(&function_data.namespace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -266,10 +266,7 @@ mod enabled {
 | 
				
			||||||
                        .map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
 | 
					                        .map_err(|err| LuaRuntimeError::from_lua_err(&err, self.span()))?;
 | 
				
			||||||
                    Value::Table(table)
 | 
					                    Value::Table(table)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                Some(VariableData::ComptimeValue {
 | 
					                Some(VariableData::ComptimeValue { value }) => {
 | 
				
			||||||
                    value,
 | 
					 | 
				
			||||||
                    read_only: _,
 | 
					 | 
				
			||||||
                }) => {
 | 
					 | 
				
			||||||
                    let value = value.read().unwrap();
 | 
					                    let value = value.read().unwrap();
 | 
				
			||||||
                    match &*value {
 | 
					                    match &*value {
 | 
				
			||||||
                        Some(ComptimeValue::Boolean(b)) => Value::Boolean(*b),
 | 
					                        Some(ComptimeValue::Boolean(b)) => Value::Boolean(*b),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
//! Transpiler for `Shulkerscript`
 | 
					//! Transpiler for `Shulkerscript`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    collections::{BTreeMap, HashSet},
 | 
					    collections::{BTreeMap, HashMap, HashSet},
 | 
				
			||||||
    ops::Deref,
 | 
					    ops::Deref,
 | 
				
			||||||
    sync::{Arc, OnceLock},
 | 
					    sync::{Arc, OnceLock},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,11 @@ pub struct Transpiler {
 | 
				
			||||||
    pub(super) initialized_constant_scores: HashSet<i64>,
 | 
					    pub(super) initialized_constant_scores: HashSet<i64>,
 | 
				
			||||||
    pub(super) temp_counter: usize,
 | 
					    pub(super) temp_counter: usize,
 | 
				
			||||||
    /// Top-level [`Scope`] for each program identifier
 | 
					    /// Top-level [`Scope`] for each program identifier
 | 
				
			||||||
    pub(super) scopes: BTreeMap<String, Arc<Scope>>,
 | 
					    pub(super) scopes: BTreeMap<String, Arc<Scope<'static>>>,
 | 
				
			||||||
 | 
					    /// Key: (program identifier, function name)
 | 
				
			||||||
 | 
					    pub(super) functions: BTreeMap<(String, String), FunctionData>,
 | 
				
			||||||
 | 
					    /// Key: alias, Value: target
 | 
				
			||||||
 | 
					    pub(super) aliases: HashMap<(String, String), (String, String)>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Transpiler {
 | 
					impl Transpiler {
 | 
				
			||||||
| 
						 | 
					@ -55,6 +59,8 @@ impl Transpiler {
 | 
				
			||||||
            initialized_constant_scores: HashSet::new(),
 | 
					            initialized_constant_scores: HashSet::new(),
 | 
				
			||||||
            temp_counter: 0,
 | 
					            temp_counter: 0,
 | 
				
			||||||
            scopes: BTreeMap::new(),
 | 
					            scopes: BTreeMap::new(),
 | 
				
			||||||
 | 
					            functions: BTreeMap::new(),
 | 
				
			||||||
 | 
					            aliases: HashMap::new(),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,11 +75,7 @@ impl Transpiler {
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> Result<Datapack, TranspileError> {
 | 
					    ) -> Result<Datapack, TranspileError> {
 | 
				
			||||||
        tracing::trace!("Transpiling program declarations");
 | 
					        tracing::trace!("Transpiling program declarations");
 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut aliases = Vec::new();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for program in programs {
 | 
					        for program in programs {
 | 
				
			||||||
            let mut program_aliases = BTreeMap::new();
 | 
					 | 
				
			||||||
            let program_identifier = program
 | 
					            let program_identifier = program
 | 
				
			||||||
                .namespace()
 | 
					                .namespace()
 | 
				
			||||||
                .span()
 | 
					                .span()
 | 
				
			||||||
| 
						 | 
					@ -82,48 +84,17 @@ impl Transpiler {
 | 
				
			||||||
                .clone();
 | 
					                .clone();
 | 
				
			||||||
            let scope = self
 | 
					            let scope = self
 | 
				
			||||||
                .scopes
 | 
					                .scopes
 | 
				
			||||||
                .entry(program_identifier.clone())
 | 
					                .entry(program_identifier)
 | 
				
			||||||
                .or_insert_with(Scope::with_internal_functions)
 | 
					                .or_insert_with(Scope::with_internal_functions)
 | 
				
			||||||
                .to_owned();
 | 
					                .to_owned();
 | 
				
			||||||
            self.transpile_program_declarations(program, &mut program_aliases, &scope, handler)?;
 | 
					            self.transpile_program_declarations(program, &scope, handler);
 | 
				
			||||||
            aliases.push((scope.clone(), program_aliases));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (scope, program_aliases) in aliases {
 | 
					 | 
				
			||||||
            for (alias_name, (alias_program_identifier, actual_name)) in program_aliases {
 | 
					 | 
				
			||||||
                if let Some(alias_scope) = self.scopes.get(&alias_program_identifier) {
 | 
					 | 
				
			||||||
                    if let Some(var_data) = alias_scope.get_variable(&actual_name) {
 | 
					 | 
				
			||||||
                        scope.set_arc_variable(&alias_name, var_data);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        tracing::error!(
 | 
					 | 
				
			||||||
                            "Importing a non-existent variable: {} from {}",
 | 
					 | 
				
			||||||
                            actual_name,
 | 
					 | 
				
			||||||
                            alias_program_identifier
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    tracing::error!(
 | 
					 | 
				
			||||||
                        "Importing from a non-existent program: {}",
 | 
					 | 
				
			||||||
                        alias_program_identifier
 | 
					 | 
				
			||||||
                    );
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut always_transpile_functions = Vec::new();
 | 
					        let mut always_transpile_functions = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let functions = self.scopes.iter().flat_map(|(_, scope)| {
 | 
					            let functions = &mut self.functions;
 | 
				
			||||||
                scope
 | 
					            for (_, data) in functions.iter() {
 | 
				
			||||||
                    .get_local_variables()
 | 
					 | 
				
			||||||
                    .read()
 | 
					 | 
				
			||||||
                    .unwrap()
 | 
					 | 
				
			||||||
                    .values()
 | 
					 | 
				
			||||||
                    .filter_map(|data| data.as_function().map(|(data, _, _)| data))
 | 
					 | 
				
			||||||
                    .cloned()
 | 
					 | 
				
			||||||
                    .collect::<Vec<_>>()
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            for data in functions {
 | 
					 | 
				
			||||||
                let always_transpile_function = data.annotations.contains_key("tick")
 | 
					                let always_transpile_function = data.annotations.contains_key("tick")
 | 
				
			||||||
                    || data.annotations.contains_key("load")
 | 
					                    || data.annotations.contains_key("load")
 | 
				
			||||||
                    || data.annotations.contains_key("deobfuscate");
 | 
					                    || data.annotations.contains_key("deobfuscate");
 | 
				
			||||||
| 
						 | 
					@ -170,28 +141,25 @@ impl Transpiler {
 | 
				
			||||||
    fn transpile_program_declarations(
 | 
					    fn transpile_program_declarations(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        program: &ProgramFile,
 | 
					        program: &ProgramFile,
 | 
				
			||||||
        program_aliases: &mut BTreeMap<String, (String, String)>,
 | 
					 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<()> {
 | 
					    ) {
 | 
				
			||||||
        let namespace = program.namespace();
 | 
					        let namespace = program.namespace();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for declaration in program.declarations() {
 | 
					        for declaration in program.declarations() {
 | 
				
			||||||
            self.transpile_declaration(declaration, namespace, program_aliases, scope, handler)?;
 | 
					            self.transpile_declaration(declaration, namespace, scope, handler);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Transpiles the given declaration.
 | 
					    /// Transpiles the given declaration.
 | 
				
			||||||
 | 
					    #[allow(clippy::needless_pass_by_ref_mut)]
 | 
				
			||||||
    fn transpile_declaration(
 | 
					    fn transpile_declaration(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        declaration: &Declaration,
 | 
					        declaration: &Declaration,
 | 
				
			||||||
        namespace: &Namespace,
 | 
					        namespace: &Namespace,
 | 
				
			||||||
        program_aliases: &mut BTreeMap<String, (String, String)>,
 | 
					 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
    ) -> TranspileResult<()> {
 | 
					    ) {
 | 
				
			||||||
        let program_identifier = declaration.span().source_file().identifier().clone();
 | 
					        let program_identifier = declaration.span().source_file().identifier().clone();
 | 
				
			||||||
        match declaration {
 | 
					        match declaration {
 | 
				
			||||||
            Declaration::Function(function) => {
 | 
					            Declaration::Function(function) => {
 | 
				
			||||||
| 
						 | 
					@ -227,18 +195,19 @@ impl Transpiler {
 | 
				
			||||||
                scope.set_variable(
 | 
					                scope.set_variable(
 | 
				
			||||||
                    &name,
 | 
					                    &name,
 | 
				
			||||||
                    VariableData::Function {
 | 
					                    VariableData::Function {
 | 
				
			||||||
                        function_data,
 | 
					                        function_data: function_data.clone(),
 | 
				
			||||||
                        path: OnceLock::new(),
 | 
					                        path: OnceLock::new(),
 | 
				
			||||||
                        function_scope: scope.clone(),
 | 
					 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
 | 
					                self.functions
 | 
				
			||||||
 | 
					                    .insert((program_identifier, name), function_data);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Declaration::Import(import) => {
 | 
					            Declaration::Import(import) => {
 | 
				
			||||||
                let path = import.module().str_content();
 | 
					                let path = import.module().str_content();
 | 
				
			||||||
                let import_identifier =
 | 
					                let import_identifier =
 | 
				
			||||||
                    super::util::calculate_import_identifier(&program_identifier, path);
 | 
					                    super::util::calculate_import_identifier(&program_identifier, path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // let aliases = &mut self.aliases;
 | 
					                let aliases = &mut self.aliases;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                match import.items() {
 | 
					                match import.items() {
 | 
				
			||||||
                    ImportItems::All(_) => {
 | 
					                    ImportItems::All(_) => {
 | 
				
			||||||
| 
						 | 
					@ -249,8 +218,8 @@ impl Transpiler {
 | 
				
			||||||
                    ImportItems::Named(list) => {
 | 
					                    ImportItems::Named(list) => {
 | 
				
			||||||
                        for item in list.elements() {
 | 
					                        for item in list.elements() {
 | 
				
			||||||
                            let name = item.span.str();
 | 
					                            let name = item.span.str();
 | 
				
			||||||
                            program_aliases.insert(
 | 
					                            aliases.insert(
 | 
				
			||||||
                                name.to_string(),
 | 
					                                (program_identifier.clone(), name.to_string()),
 | 
				
			||||||
                                (import_identifier.clone(), name.to_string()),
 | 
					                                (import_identifier.clone(), name.to_string()),
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
| 
						 | 
					@ -272,21 +241,9 @@ impl Transpiler {
 | 
				
			||||||
                if tag.replace().is_some() {
 | 
					                if tag.replace().is_some() {
 | 
				
			||||||
                    sb_tag.set_replace(true);
 | 
					                    sb_tag.set_replace(true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					                // TODO: handle global variables
 | 
				
			||||||
            Declaration::GlobalVariable((_, declaration, _)) => {
 | 
					 | 
				
			||||||
                let setup_variable_cmds = self.transpile_variable_declaration(
 | 
					 | 
				
			||||||
                    declaration,
 | 
					 | 
				
			||||||
                    true,
 | 
					 | 
				
			||||||
                    &program_identifier,
 | 
					 | 
				
			||||||
                    scope,
 | 
					 | 
				
			||||||
                    handler,
 | 
					 | 
				
			||||||
                )?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.setup_cmds.extend(setup_variable_cmds);
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(super) fn transpile_statement(
 | 
					    pub(super) fn transpile_statement(
 | 
				
			||||||
| 
						 | 
					@ -307,7 +264,7 @@ impl Transpiler {
 | 
				
			||||||
                unreachable!("Only literal commands are allowed in functions at this time.")
 | 
					                unreachable!("Only literal commands are allowed in functions at this time.")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Statement::ExecuteBlock(execute) => {
 | 
					            Statement::ExecuteBlock(execute) => {
 | 
				
			||||||
                let child_scope = Scope::with_parent(scope.clone());
 | 
					                let child_scope = Scope::with_parent(scope);
 | 
				
			||||||
                Ok(self.transpile_execute_block(
 | 
					                Ok(self.transpile_execute_block(
 | 
				
			||||||
                    execute,
 | 
					                    execute,
 | 
				
			||||||
                    program_identifier,
 | 
					                    program_identifier,
 | 
				
			||||||
| 
						 | 
					@ -320,7 +277,7 @@ impl Transpiler {
 | 
				
			||||||
                Ok(vec![Command::Comment(content.to_string())])
 | 
					                Ok(vec![Command::Comment(content.to_string())])
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Statement::Grouping(group) => {
 | 
					            Statement::Grouping(group) => {
 | 
				
			||||||
                let child_scope = Scope::with_parent(scope.clone());
 | 
					                let child_scope = Scope::with_parent(scope);
 | 
				
			||||||
                let statements = group.block().statements();
 | 
					                let statements = group.block().statements();
 | 
				
			||||||
                let mut errors = Vec::new();
 | 
					                let mut errors = Vec::new();
 | 
				
			||||||
                let commands = statements
 | 
					                let commands = statements
 | 
				
			||||||
| 
						 | 
					@ -360,14 +317,9 @@ impl Transpiler {
 | 
				
			||||||
                        Err(error)
 | 
					                        Err(error)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                SemicolonStatement::VariableDeclaration(decl) => self
 | 
					                SemicolonStatement::VariableDeclaration(decl) => {
 | 
				
			||||||
                    .transpile_variable_declaration(
 | 
					                    self.transpile_variable_declaration(decl, program_identifier, scope, handler)
 | 
				
			||||||
                        decl,
 | 
					                }
 | 
				
			||||||
                        false,
 | 
					 | 
				
			||||||
                        program_identifier,
 | 
					 | 
				
			||||||
                        scope,
 | 
					 | 
				
			||||||
                        handler,
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                SemicolonStatement::Assignment(assignment) => self.transpile_assignment(
 | 
					                SemicolonStatement::Assignment(assignment) => self.transpile_assignment(
 | 
				
			||||||
                    TranspileAssignmentTarget::from(assignment.destination()),
 | 
					                    TranspileAssignmentTarget::from(assignment.destination()),
 | 
				
			||||||
                    assignment.expression(),
 | 
					                    assignment.expression(),
 | 
				
			||||||
| 
						 | 
					@ -392,10 +344,8 @@ impl Transpiler {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            Expression::Primary(Primary::Identifier(ident)) => {
 | 
					            Expression::Primary(Primary::Identifier(ident)) => {
 | 
				
			||||||
                match scope.get_variable(ident.span.str()).as_deref() {
 | 
					                match scope.get_variable(ident.span.str()).as_deref() {
 | 
				
			||||||
                    Some(VariableData::ComptimeValue {
 | 
					                    Some(VariableData::ComptimeValue { value }) => {
 | 
				
			||||||
                        value,
 | 
					                        value.read().unwrap().as_ref().map_or_else(
 | 
				
			||||||
                        read_only: _,
 | 
					 | 
				
			||||||
                    }) => value.read().unwrap().as_ref().map_or_else(
 | 
					 | 
				
			||||||
                            || {
 | 
					                            || {
 | 
				
			||||||
                                let error = TranspileError::MissingValue(MissingValue {
 | 
					                                let error = TranspileError::MissingValue(MissingValue {
 | 
				
			||||||
                                    expression: ident.span.clone(),
 | 
					                                    expression: ident.span.clone(),
 | 
				
			||||||
| 
						 | 
					@ -410,7 +360,8 @@ impl Transpiler {
 | 
				
			||||||
                                );
 | 
					                                );
 | 
				
			||||||
                                Ok(vec![cmd])
 | 
					                                Ok(vec![cmd])
 | 
				
			||||||
                            },
 | 
					                            },
 | 
				
			||||||
                    ),
 | 
					                        )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    Some(_) => {
 | 
					                    Some(_) => {
 | 
				
			||||||
                        let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
 | 
					                        let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
 | 
				
			||||||
                            expression.clone(),
 | 
					                            expression.clone(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,8 +49,6 @@ pub enum VariableData {
 | 
				
			||||||
        function_data: FunctionData,
 | 
					        function_data: FunctionData,
 | 
				
			||||||
        /// The path to the function once it is generated.
 | 
					        /// The path to the function once it is generated.
 | 
				
			||||||
        path: OnceLock<String>,
 | 
					        path: OnceLock<String>,
 | 
				
			||||||
        /// The scope of the function.
 | 
					 | 
				
			||||||
        function_scope: Arc<Scope>,
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    /// A macro function parameter.
 | 
					    /// A macro function parameter.
 | 
				
			||||||
    MacroParameter {
 | 
					    MacroParameter {
 | 
				
			||||||
| 
						 | 
					@ -106,8 +104,6 @@ pub enum VariableData {
 | 
				
			||||||
    ComptimeValue {
 | 
					    ComptimeValue {
 | 
				
			||||||
        /// The value.
 | 
					        /// The value.
 | 
				
			||||||
        value: Arc<RwLock<Option<ComptimeValue>>>,
 | 
					        value: Arc<RwLock<Option<ComptimeValue>>>,
 | 
				
			||||||
        /// Whether the value is read-only.
 | 
					 | 
				
			||||||
        read_only: bool,
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -133,9 +129,9 @@ impl<'a> From<&'a AssignmentDestination> for TranspileAssignmentTarget<'a> {
 | 
				
			||||||
/// A scope that stores variables.
 | 
					/// A scope that stores variables.
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
#[derive(Default)]
 | 
					#[derive(Default)]
 | 
				
			||||||
pub struct Scope {
 | 
					pub struct Scope<'a> {
 | 
				
			||||||
    /// Parent scope where variables are inherited from.
 | 
					    /// Parent scope where variables are inherited from.
 | 
				
			||||||
    parent: Option<Arc<Self>>,
 | 
					    parent: Option<&'a Arc<Self>>,
 | 
				
			||||||
    /// Variables stored in the scope.
 | 
					    /// Variables stored in the scope.
 | 
				
			||||||
    variables: RwLock<HashMap<String, Arc<VariableData>>>,
 | 
					    variables: RwLock<HashMap<String, Arc<VariableData>>>,
 | 
				
			||||||
    /// How many times the variable has been shadowed in the current scope.
 | 
					    /// How many times the variable has been shadowed in the current scope.
 | 
				
			||||||
| 
						 | 
					@ -143,7 +139,7 @@ pub struct Scope {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl Scope {
 | 
					impl<'a> Scope<'a> {
 | 
				
			||||||
    /// Creates a new scope.
 | 
					    /// Creates a new scope.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn new() -> Arc<Self> {
 | 
					    pub fn new() -> Arc<Self> {
 | 
				
			||||||
| 
						 | 
					@ -165,7 +161,7 @@ impl Scope {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Creates a new scope with a parent.
 | 
					    /// Creates a new scope with a parent.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn with_parent(parent: Arc<Self>) -> Arc<Self> {
 | 
					    pub fn with_parent(parent: &'a Arc<Self>) -> Arc<Self> {
 | 
				
			||||||
        Arc::new(Self {
 | 
					        Arc::new(Self {
 | 
				
			||||||
            parent: Some(parent),
 | 
					            parent: Some(parent),
 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
| 
						 | 
					@ -202,17 +198,11 @@ impl Scope {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Sets a variable in the scope.
 | 
					    /// Sets a variable in the scope.
 | 
				
			||||||
    pub fn set_variable(&self, name: &str, var: VariableData) {
 | 
					    pub fn set_variable(&self, name: &str, var: VariableData) {
 | 
				
			||||||
        self.set_arc_variable(name, Arc::new(var));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Sets a variable in the scope.
 | 
					 | 
				
			||||||
    /// This function is used to set a variable that is already wrapped in an `Arc`.
 | 
					 | 
				
			||||||
    pub fn set_arc_variable(&self, name: &str, var: Arc<VariableData>) {
 | 
					 | 
				
			||||||
        let prev = self
 | 
					        let prev = self
 | 
				
			||||||
            .variables
 | 
					            .variables
 | 
				
			||||||
            .write()
 | 
					            .write()
 | 
				
			||||||
            .unwrap()
 | 
					            .unwrap()
 | 
				
			||||||
            .insert(name.to_string(), var);
 | 
					            .insert(name.to_string(), Arc::new(var));
 | 
				
			||||||
        *self
 | 
					        *self
 | 
				
			||||||
            .shadowed
 | 
					            .shadowed
 | 
				
			||||||
            .write()
 | 
					            .write()
 | 
				
			||||||
| 
						 | 
					@ -239,12 +229,12 @@ impl Scope {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Gets the parent scope.
 | 
					    /// Gets the parent scope.
 | 
				
			||||||
    pub fn get_parent(&self) -> Option<Arc<Self>> {
 | 
					    pub fn get_parent(&self) -> Option<Arc<Self>> {
 | 
				
			||||||
        self.parent.clone()
 | 
					        self.parent.cloned()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "shulkerbox")]
 | 
					#[cfg(feature = "shulkerbox")]
 | 
				
			||||||
impl Debug for Scope {
 | 
					impl Debug for Scope<'_> {
 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
        struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
 | 
					        struct VariableWrapper<'a>(&'a RwLock<HashMap<String, Arc<VariableData>>>);
 | 
				
			||||||
        impl Debug for VariableWrapper<'_> {
 | 
					        impl Debug for VariableWrapper<'_> {
 | 
				
			||||||
| 
						 | 
					@ -268,7 +258,6 @@ impl Transpiler {
 | 
				
			||||||
    pub(super) fn transpile_variable_declaration(
 | 
					    pub(super) fn transpile_variable_declaration(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        declaration: &VariableDeclaration,
 | 
					        declaration: &VariableDeclaration,
 | 
				
			||||||
        is_global: bool,
 | 
					 | 
				
			||||||
        program_identifier: &str,
 | 
					        program_identifier: &str,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
| 
						 | 
					@ -276,7 +265,6 @@ impl Transpiler {
 | 
				
			||||||
        match declaration {
 | 
					        match declaration {
 | 
				
			||||||
            VariableDeclaration::Single(declaration) => self.transpile_single_variable_declaration(
 | 
					            VariableDeclaration::Single(declaration) => self.transpile_single_variable_declaration(
 | 
				
			||||||
                declaration,
 | 
					                declaration,
 | 
				
			||||||
                is_global,
 | 
					 | 
				
			||||||
                program_identifier,
 | 
					                program_identifier,
 | 
				
			||||||
                scope,
 | 
					                scope,
 | 
				
			||||||
                handler,
 | 
					                handler,
 | 
				
			||||||
| 
						 | 
					@ -289,7 +277,6 @@ impl Transpiler {
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            VariableDeclaration::Array(declaration) => self.transpile_array_variable_declaration(
 | 
					            VariableDeclaration::Array(declaration) => self.transpile_array_variable_declaration(
 | 
				
			||||||
                declaration,
 | 
					                declaration,
 | 
				
			||||||
                is_global,
 | 
					 | 
				
			||||||
                program_identifier,
 | 
					                program_identifier,
 | 
				
			||||||
                scope,
 | 
					                scope,
 | 
				
			||||||
                handler,
 | 
					                handler,
 | 
				
			||||||
| 
						 | 
					@ -320,7 +307,6 @@ impl Transpiler {
 | 
				
			||||||
                    declaration.identifier().span.str(),
 | 
					                    declaration.identifier().span.str(),
 | 
				
			||||||
                    VariableData::ComptimeValue {
 | 
					                    VariableData::ComptimeValue {
 | 
				
			||||||
                        value: Arc::new(RwLock::new(value)),
 | 
					                        value: Arc::new(RwLock::new(value)),
 | 
				
			||||||
                        read_only: is_global,
 | 
					 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -332,7 +318,6 @@ impl Transpiler {
 | 
				
			||||||
    fn transpile_single_variable_declaration(
 | 
					    fn transpile_single_variable_declaration(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        declaration: &SingleVariableDeclaration,
 | 
					        declaration: &SingleVariableDeclaration,
 | 
				
			||||||
        is_global: bool,
 | 
					 | 
				
			||||||
        program_identifier: &str,
 | 
					        program_identifier: &str,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
| 
						 | 
					@ -357,7 +342,7 @@ impl Transpiler {
 | 
				
			||||||
                    declaration.identifier().span.str(),
 | 
					                    declaration.identifier().span.str(),
 | 
				
			||||||
                    VariableData::ScoreboardValue {
 | 
					                    VariableData::ScoreboardValue {
 | 
				
			||||||
                        objective: name.clone(),
 | 
					                        objective: name.clone(),
 | 
				
			||||||
                        target: target.clone(),
 | 
					                        target,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -365,49 +350,25 @@ impl Transpiler {
 | 
				
			||||||
                scope.set_variable(
 | 
					                scope.set_variable(
 | 
				
			||||||
                    declaration.identifier().span.str(),
 | 
					                    declaration.identifier().span.str(),
 | 
				
			||||||
                    VariableData::BooleanStorage {
 | 
					                    VariableData::BooleanStorage {
 | 
				
			||||||
                        storage_name: name.clone(),
 | 
					                        storage_name: name,
 | 
				
			||||||
                        path: target.clone(),
 | 
					                        path: target,
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            _ => unreachable!("no other variable types"),
 | 
					            _ => unreachable!("no other variable types"),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if let Some(assignment) = declaration.assignment().as_ref() {
 | 
					        declaration.assignment().as_ref().map_or_else(
 | 
				
			||||||
            let cmds = self.transpile_assignment(
 | 
					            || Ok(Vec::new()),
 | 
				
			||||||
 | 
					            |assignment| {
 | 
				
			||||||
 | 
					                self.transpile_assignment(
 | 
				
			||||||
                    TranspileAssignmentTarget::Identifier(declaration.identifier()),
 | 
					                    TranspileAssignmentTarget::Identifier(declaration.identifier()),
 | 
				
			||||||
                    assignment.expression(),
 | 
					                    assignment.expression(),
 | 
				
			||||||
                    scope,
 | 
					                    scope,
 | 
				
			||||||
                    handler,
 | 
					                    handler,
 | 
				
			||||||
            )?;
 | 
					                )
 | 
				
			||||||
            if is_global {
 | 
					            },
 | 
				
			||||||
                let (temp_objective, temp_targets) = self.get_temp_scoreboard_locations(1);
 | 
					        )
 | 
				
			||||||
                let temp_target = &temp_targets[0];
 | 
					 | 
				
			||||||
                let test_cmd = match declaration.variable_type().keyword {
 | 
					 | 
				
			||||||
                    KeywordKind::Int => {
 | 
					 | 
				
			||||||
                        Command::Raw(format!("scoreboard players get {name} {target}"))
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    KeywordKind::Bool => Command::Raw(format!("data get storage {name} {target}")),
 | 
					 | 
				
			||||||
                    _ => unreachable!("no other variable types"),
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                let test_exists_cmd = Command::Execute(Execute::Store(
 | 
					 | 
				
			||||||
                    format!("success score {temp_target} {temp_objective}").into(),
 | 
					 | 
				
			||||||
                    Box::new(Execute::Run(Box::new(test_cmd))),
 | 
					 | 
				
			||||||
                ));
 | 
					 | 
				
			||||||
                let cond_cmd = Command::Execute(Execute::If(
 | 
					 | 
				
			||||||
                    Condition::Atom(
 | 
					 | 
				
			||||||
                        format!("score {temp_target} {temp_objective} matches 0").into(),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                    Box::new(Execute::Run(Box::new(Command::Group(cmds)))),
 | 
					 | 
				
			||||||
                    None,
 | 
					 | 
				
			||||||
                ));
 | 
					 | 
				
			||||||
                Ok(vec![test_exists_cmd, cond_cmd])
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                Ok(cmds)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            Ok(Vec::new())
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn transpile_score_variable_declaration(
 | 
					    fn transpile_score_variable_declaration(
 | 
				
			||||||
| 
						 | 
					@ -450,7 +411,6 @@ impl Transpiler {
 | 
				
			||||||
    fn transpile_array_variable_declaration(
 | 
					    fn transpile_array_variable_declaration(
 | 
				
			||||||
        &mut self,
 | 
					        &mut self,
 | 
				
			||||||
        declaration: &ArrayVariableDeclaration,
 | 
					        declaration: &ArrayVariableDeclaration,
 | 
				
			||||||
        _is_global: bool,
 | 
					 | 
				
			||||||
        program_identifier: &str,
 | 
					        program_identifier: &str,
 | 
				
			||||||
        scope: &Arc<Scope>,
 | 
					        scope: &Arc<Scope>,
 | 
				
			||||||
        handler: &impl Handler<base::Error>,
 | 
					        handler: &impl Handler<base::Error>,
 | 
				
			||||||
| 
						 | 
					@ -490,7 +450,6 @@ impl Transpiler {
 | 
				
			||||||
        declaration.assignment().as_ref().map_or_else(
 | 
					        declaration.assignment().as_ref().map_or_else(
 | 
				
			||||||
            || Ok(Vec::new()),
 | 
					            || Ok(Vec::new()),
 | 
				
			||||||
            |assignment| {
 | 
					            |assignment| {
 | 
				
			||||||
                // TODO: implement global already exists check when array assignments are implemented
 | 
					 | 
				
			||||||
                self.transpile_assignment(
 | 
					                self.transpile_assignment(
 | 
				
			||||||
                    TranspileAssignmentTarget::Identifier(declaration.identifier()),
 | 
					                    TranspileAssignmentTarget::Identifier(declaration.identifier()),
 | 
				
			||||||
                    assignment.expression(),
 | 
					                    assignment.expression(),
 | 
				
			||||||
| 
						 | 
					@ -733,15 +692,7 @@ impl Transpiler {
 | 
				
			||||||
                        return Err(err);
 | 
					                        return Err(err);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                VariableData::ComptimeValue { value, read_only } => {
 | 
					                VariableData::ComptimeValue { value } => {
 | 
				
			||||||
                    if *read_only {
 | 
					 | 
				
			||||||
                        let err = TranspileError::AssignmentError(AssignmentError {
 | 
					 | 
				
			||||||
                            identifier: identifier.span(),
 | 
					 | 
				
			||||||
                            message: "Cannot assign to a read-only value.".to_string(),
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                        handler.receive(err.clone());
 | 
					 | 
				
			||||||
                        return Err(err);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    let comptime_value =
 | 
					                    let comptime_value =
 | 
				
			||||||
                        expression.comptime_eval(scope, handler).map_err(|err| {
 | 
					                        expression.comptime_eval(scope, handler).map_err(|err| {
 | 
				
			||||||
                            let err = TranspileError::NotComptime(err);
 | 
					                            let err = TranspileError::NotComptime(err);
 | 
				
			||||||
| 
						 | 
					@ -1146,14 +1097,9 @@ impl Transpiler {
 | 
				
			||||||
                    DataLocation::Storage {
 | 
					                    DataLocation::Storage {
 | 
				
			||||||
                        storage_name: target_storage_name,
 | 
					                        storage_name: target_storage_name,
 | 
				
			||||||
                        path: target_path,
 | 
					                        path: target_path,
 | 
				
			||||||
                        r#type: target_type,
 | 
					                        r#type,
 | 
				
			||||||
                    } => {
 | 
					                    } => {
 | 
				
			||||||
                        if storage_name == target_storage_name
 | 
					                        if matches!(r#type, StorageType::Boolean) {
 | 
				
			||||||
                            && path == target_path
 | 
					 | 
				
			||||||
                            && r#type == target_type
 | 
					 | 
				
			||||||
                        {
 | 
					 | 
				
			||||||
                            Ok(Vec::new())
 | 
					 | 
				
			||||||
                        } else if matches!(target_type, StorageType::Boolean) {
 | 
					 | 
				
			||||||
                            let cmd = Command::Raw(format!(
 | 
					                            let cmd = Command::Raw(format!(
 | 
				
			||||||
                                        "data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"
 | 
					                                        "data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"
 | 
				
			||||||
                                    ));
 | 
					                                    ));
 | 
				
			||||||
| 
						 | 
					@ -1177,15 +1123,11 @@ impl Transpiler {
 | 
				
			||||||
                    objective: target_objective,
 | 
					                    objective: target_objective,
 | 
				
			||||||
                    target: target_target,
 | 
					                    target: target_target,
 | 
				
			||||||
                } => {
 | 
					                } => {
 | 
				
			||||||
                    if objective == target_objective && score_target == target_target {
 | 
					 | 
				
			||||||
                        Ok(Vec::new())
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                    let cmd = Command::Raw(format!(
 | 
					                    let cmd = Command::Raw(format!(
 | 
				
			||||||
                        "scoreboard players operation {target_target} {target_objective} = {score_target} {objective}"
 | 
					                        "scoreboard players operation {target_target} {target_objective} = {score_target} {objective}"
 | 
				
			||||||
                    ));
 | 
					                    ));
 | 
				
			||||||
                    Ok(vec![cmd])
 | 
					                    Ok(vec![cmd])
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                DataLocation::Storage {
 | 
					                DataLocation::Storage {
 | 
				
			||||||
                    storage_name,
 | 
					                    storage_name,
 | 
				
			||||||
                    path,
 | 
					                    path,
 | 
				
			||||||
| 
						 | 
					@ -1271,7 +1213,7 @@ mod tests {
 | 
				
			||||||
                objective: "test".to_string(),
 | 
					                objective: "test".to_string(),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        let child = Scope::with_parent(scope);
 | 
					        let child = Scope::with_parent(&scope);
 | 
				
			||||||
        if let Some(var) = child.get_variable("test") {
 | 
					        if let Some(var) = child.get_variable("test") {
 | 
				
			||||||
            match var.as_ref() {
 | 
					            match var.as_ref() {
 | 
				
			||||||
                VariableData::Scoreboard { objective } => assert_eq!(objective, "test"),
 | 
					                VariableData::Scoreboard { objective } => assert_eq!(objective, "test"),
 | 
				
			||||||
| 
						 | 
					@ -1291,7 +1233,7 @@ mod tests {
 | 
				
			||||||
                objective: "test1".to_string(),
 | 
					                objective: "test1".to_string(),
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        let child = Scope::with_parent(scope);
 | 
					        let child = Scope::with_parent(&scope);
 | 
				
			||||||
        child.set_variable(
 | 
					        child.set_variable(
 | 
				
			||||||
            "test",
 | 
					            "test",
 | 
				
			||||||
            VariableData::Scoreboard {
 | 
					            VariableData::Scoreboard {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue