run as expression, implement return statement
This commit is contained in:
		
							parent
							
								
									68149d9ddf
								
							
						
					
					
						commit
						dd97937feb
					
				|  | @ -35,7 +35,7 @@ pathdiff = "0.2.3" | |||
| serde = { version = "1.0.217", features = ["derive"], optional = true } | ||||
| serde_json = { version = "1.0.138", optional = true } | ||||
| # shulkerbox = { version = "0.1.0", default-features = false, optional = true } | ||||
| shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "7392887c127b1aae0d78d573a02332c2fa2591c8", default-features = false, optional = true } | ||||
| shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "5c4d340162fc16065add448ed387a1ce481c27d6", default-features = false, optional = true } | ||||
| strsim = "0.11.1" | ||||
| strum = { version = "0.27.0", features = ["derive"] } | ||||
| thiserror = "2.0.11" | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ pub enum KeywordKind { | |||
|     Bool, | ||||
|     Macro, | ||||
|     Val, | ||||
|     Return, | ||||
| } | ||||
| 
 | ||||
| impl Display for KeywordKind { | ||||
|  | @ -119,6 +120,7 @@ impl KeywordKind { | |||
|             Self::Bool => "bool", | ||||
|             Self::Macro => "macro", | ||||
|             Self::Val => "val", | ||||
|             Self::Return => "return", | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ use crate::{ | |||
|                 Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockHeadItem as _, | ||||
|                 ExecuteBlockTail, | ||||
|             }, | ||||
|             Assignment, AssignmentDestination, Block, Grouping, Run, Semicolon, SemicolonStatement, | ||||
|             Assignment, AssignmentDestination, Block, Grouping, Semicolon, SemicolonStatement, | ||||
|             Statement, VariableDeclaration, | ||||
|         }, | ||||
|         AnyStringLiteral, | ||||
|  | @ -228,7 +228,6 @@ impl Statement { | |||
|                 let child_scope = SemanticScope::with_parent(scope); | ||||
|                 group.analyze_semantics(&child_scope, handler) | ||||
|             } | ||||
|             Self::Run(run) => run.analyze_semantics(scope, handler), | ||||
|             Self::Semicolon(sem) => sem.analyze_semantics(scope, handler), | ||||
|         } | ||||
|     } | ||||
|  | @ -342,16 +341,6 @@ impl Grouping { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Run { | ||||
|     fn analyze_semantics( | ||||
|         &self, | ||||
|         scope: &SemanticScope, | ||||
|         handler: &impl Handler<base::Error>, | ||||
|     ) -> Result<(), error::Error> { | ||||
|         self.expression().analyze_semantics(scope, handler) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Semicolon { | ||||
|     fn analyze_semantics( | ||||
|         &self, | ||||
|  | @ -364,6 +353,7 @@ impl Semicolon { | |||
|             } | ||||
|             SemicolonStatement::Expression(expr) => expr.analyze_semantics(scope, handler), | ||||
|             SemicolonStatement::VariableDeclaration(decl) => decl.analyze_semantics(scope, handler), | ||||
|             SemicolonStatement::Return(ret) => ret.expression().analyze_semantics(scope, handler), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -665,6 +655,21 @@ impl Primary { | |||
|                         Err(err) | ||||
|                     } | ||||
|                 } | ||||
|                 PrefixOperator::Run(_) => { | ||||
|                     if prefixed | ||||
|                         .operand() | ||||
|                         .can_yield_type_semantics(ValueType::String, scope) | ||||
|                     { | ||||
|                         prefixed.operand().analyze_semantics(scope, handler) | ||||
|                     } else { | ||||
|                         let err = error::Error::MismatchedTypes(MismatchedTypes { | ||||
|                             expected_type: ExpectedType::String, | ||||
|                             expression: prefixed.operand().span(), | ||||
|                         }); | ||||
|                         handler.receive(err.clone()); | ||||
|                         Err(err) | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             Self::Lua(lua) => lua.analyze_semantics(scope, handler), | ||||
|         } | ||||
|  | @ -701,12 +706,24 @@ impl Primary { | |||
|                 _ => false, | ||||
|             }, | ||||
|             Self::Prefix(prefixed) => match prefixed.operator() { | ||||
|                 PrefixOperator::LogicalNot(_) => prefixed | ||||
|                 PrefixOperator::LogicalNot(_) => { | ||||
|                     expected == ValueType::Boolean | ||||
|                         && prefixed | ||||
|                             .operand() | ||||
|                     .can_yield_type_semantics(ValueType::Boolean, scope), | ||||
|                 PrefixOperator::Negate(_) => prefixed | ||||
|                             .can_yield_type_semantics(ValueType::Boolean, scope) | ||||
|                 } | ||||
|                 PrefixOperator::Negate(_) => { | ||||
|                     expected == ValueType::Integer | ||||
|                         && prefixed | ||||
|                             .operand() | ||||
|                     .can_yield_type_semantics(ValueType::Integer, scope), | ||||
|                             .can_yield_type_semantics(ValueType::Integer, scope) | ||||
|                 } | ||||
|                 PrefixOperator::Run(_) => { | ||||
|                     expected == ValueType::String | ||||
|                         && prefixed | ||||
|                             .operand() | ||||
|                             .can_yield_type_semantics(ValueType::String, scope) | ||||
|                 } | ||||
|             }, | ||||
|             Self::Parenthesized(parenthesized) => { | ||||
|                 parenthesized.can_yield_type_semantics(expected, scope) | ||||
|  | @ -807,8 +824,18 @@ impl Binary { | |||
|     #[must_use] | ||||
|     fn can_yield_type_semantics(&self, expected: ValueType, scope: &SemanticScope) -> bool { | ||||
|         match self.operator() { | ||||
|             BinaryOperator::Add(_) | ||||
|             | BinaryOperator::Subtract(_) | ||||
|             BinaryOperator::Add(_) => { | ||||
|                 if expected == ValueType::Integer { | ||||
|                     self.left_operand() | ||||
|                         .can_yield_type_semantics(ValueType::Integer, scope) | ||||
|                         && self | ||||
|                             .right_operand() | ||||
|                             .can_yield_type_semantics(ValueType::Integer, scope) | ||||
|                 } else { | ||||
|                     expected == ValueType::String | ||||
|                 } | ||||
|             } | ||||
|             BinaryOperator::Subtract(_) | ||||
|             | BinaryOperator::Multiply(_) | ||||
|             | BinaryOperator::Divide(_) | ||||
|             | BinaryOperator::Modulo(_) => { | ||||
|  |  | |||
|  | @ -56,7 +56,7 @@ impl SyntaxKind { | |||
|                 } else if variants.len() == 1 { | ||||
|                     variants[0].expected_binding_str() | ||||
|                 } else { | ||||
|                     let comma_range = ..variants.len() - 2; | ||||
|                     let comma_range = ..variants.len() - 1; | ||||
|                     let comma_elements = variants[comma_range] | ||||
|                         .iter() | ||||
|                         .map(Self::expected_binding_str) | ||||
|  |  | |||
|  | @ -357,11 +357,16 @@ impl Parser<'_> { | |||
|             Reading::Atomic(Token::Keyword(pub_keyword)) | ||||
|                 if pub_keyword.keyword == KeywordKind::Pub => | ||||
|             { | ||||
|                 if let Ok(function) = self.try_parse(|parser| parser.parse_function(&VoidHandler)) { | ||||
|                 match self.peek_offset(2) { | ||||
|                     Some(Reading::Atomic(Token::Keyword(function_keyword))) | ||||
|                         if function_keyword.keyword == KeywordKind::Function => | ||||
|                     { | ||||
|                         let function = self.parse_function(handler)?; | ||||
|                         tracing::trace!("Parsed function '{:?}'", function.identifier.span.str()); | ||||
| 
 | ||||
|                         Ok(Declaration::Function(function)) | ||||
|                 } else { | ||||
|                     } | ||||
|                     _ => { | ||||
|                         // eat the pub keyword
 | ||||
|                         self.forward(); | ||||
| 
 | ||||
|  | @ -371,6 +376,7 @@ impl Parser<'_> { | |||
|                         Ok(Declaration::GlobalVariable((Some(pub_keyword), var, semi))) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // parse annotations
 | ||||
|             Reading::Atomic(Token::Punctuation(punctuation)) if punctuation.punctuation == '#' => { | ||||
|  |  | |||
|  | @ -314,12 +314,15 @@ pub enum PrefixOperator { | |||
|     LogicalNot(Punctuation), | ||||
|     /// The negate operator '-'.
 | ||||
|     Negate(Punctuation), | ||||
|     /// The run keyword 'run'.
 | ||||
|     Run(Keyword), | ||||
| } | ||||
| 
 | ||||
| impl SourceElement for PrefixOperator { | ||||
|     fn span(&self) -> Span { | ||||
|         match self { | ||||
|             Self::LogicalNot(token) | Self::Negate(token) => token.span.clone(), | ||||
|             Self::Run(token) => token.span.clone(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -507,7 +510,7 @@ impl Parser<'_> { | |||
|     #[expect(clippy::too_many_lines)] | ||||
|     pub fn parse_primary(&mut self, handler: &impl Handler<base::Error>) -> ParseResult<Primary> { | ||||
|         match self.stop_at_significant() { | ||||
|             // prefixed expression
 | ||||
|             // prefixed expression with '!' or '-'
 | ||||
|             Reading::Atomic(Token::Punctuation(punc)) if matches!(punc.punctuation, '!' | '-') => { | ||||
|                 // eat the prefix
 | ||||
|                 self.forward(); | ||||
|  | @ -526,6 +529,23 @@ impl Parser<'_> { | |||
|                 })) | ||||
|             } | ||||
| 
 | ||||
|             // prefixed expression with 'run'
 | ||||
|             Reading::Atomic(Token::Keyword(run_keyword)) | ||||
|                 if run_keyword.keyword == KeywordKind::Run => | ||||
|             { | ||||
|                 // eat the run keyword
 | ||||
|                 self.forward(); | ||||
| 
 | ||||
|                 let expression = self.parse_primary(handler)?; | ||||
| 
 | ||||
|                 tracing::trace!("Parsed run expression: {:?}", expression); | ||||
| 
 | ||||
|                 Ok(Primary::Prefix(Prefix { | ||||
|                     operator: PrefixOperator::Run(run_keyword), | ||||
|                     operand: Box::new(expression), | ||||
|                 })) | ||||
|             } | ||||
| 
 | ||||
|             // parenthesized expression
 | ||||
|             Reading::IntoDelimited(left_parenthesis) if left_parenthesis.punctuation == '(' => self | ||||
|                 .parse_parenthesized(handler) | ||||
|  |  | |||
|  | @ -43,7 +43,6 @@ use super::{expression::Expression, Annotation, AnyStringLiteral}; | |||
| ///     | Grouping
 | ||||
| ///     | DocComment
 | ||||
| ///     | Semicolon
 | ||||
| ///     | Run
 | ||||
| ///     ;
 | ||||
| /// ```
 | ||||
| #[allow(missing_docs)] | ||||
|  | @ -56,7 +55,6 @@ pub enum Statement { | |||
|     Grouping(Grouping), | ||||
|     DocComment(DocComment), | ||||
|     Semicolon(Semicolon), | ||||
|     Run(Run), | ||||
| } | ||||
| 
 | ||||
| impl SourceElement for Statement { | ||||
|  | @ -68,7 +66,6 @@ impl SourceElement for Statement { | |||
|             Self::Grouping(grouping) => grouping.span(), | ||||
|             Self::DocComment(doc_comment) => doc_comment.span(), | ||||
|             Self::Semicolon(semi) => semi.span(), | ||||
|             Self::Run(run) => run.span(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -107,6 +104,14 @@ impl Statement { | |||
|                         target: "expressions".to_string(), | ||||
|                     }); | ||||
| 
 | ||||
|                     Err(err) | ||||
|                 } | ||||
|                 SemicolonStatement::Return(_) => { | ||||
|                     let err = Error::InvalidAnnotation(InvalidAnnotation { | ||||
|                         annotation: annotation.assignment.identifier.span, | ||||
|                         target: "return statements".to_string(), | ||||
|                     }); | ||||
| 
 | ||||
|                     Err(err) | ||||
|                 } | ||||
|             }, | ||||
|  | @ -162,46 +167,6 @@ impl SourceElement for Block { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Represents a run statement in the syntax tree.
 | ||||
| ///
 | ||||
| /// Syntax Synopsis:
 | ||||
| ///
 | ||||
| /// ``` ebnf
 | ||||
| /// Run:
 | ||||
| /// 'run' Expression ';'
 | ||||
| /// ;
 | ||||
| /// ```
 | ||||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||||
| #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] | ||||
| pub struct Run { | ||||
|     /// The `run` keyword.
 | ||||
|     #[get = "pub"] | ||||
|     run_keyword: Keyword, | ||||
|     /// The expression of the run statement.
 | ||||
|     #[get = "pub"] | ||||
|     expression: Expression, | ||||
|     /// The semicolon of the run statement.
 | ||||
|     #[get = "pub"] | ||||
|     semicolon: Punctuation, | ||||
| } | ||||
| 
 | ||||
| impl SourceElement for Run { | ||||
|     fn span(&self) -> Span { | ||||
|         self.run_keyword | ||||
|             .span() | ||||
|             .join(&self.semicolon.span()) | ||||
|             .expect("The span of the run statement is invalid.") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Run { | ||||
|     /// Dissolves the [`Run`] into its components.
 | ||||
|     #[must_use] | ||||
|     pub fn dissolve(self) -> (Keyword, Expression, Punctuation) { | ||||
|         (self.run_keyword, self.expression, self.semicolon) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Represents a grouping statement in the syntax tree.
 | ||||
| ///
 | ||||
| /// Syntax Synopsis:
 | ||||
|  | @ -293,6 +258,8 @@ pub enum SemicolonStatement { | |||
|     VariableDeclaration(VariableDeclaration), | ||||
|     /// An assignment.
 | ||||
|     Assignment(Assignment), | ||||
|     /// A return statement.
 | ||||
|     Return(ReturnStatement), | ||||
| } | ||||
| 
 | ||||
| impl SourceElement for SemicolonStatement { | ||||
|  | @ -301,10 +268,46 @@ impl SourceElement for SemicolonStatement { | |||
|             Self::Expression(expression) => expression.span(), | ||||
|             Self::VariableDeclaration(declaration) => declaration.span(), | ||||
|             Self::Assignment(assignment) => assignment.span(), | ||||
|             Self::Return(ret) => ret.span(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Represents a return statement in the syntax tree.
 | ||||
| ///
 | ||||
| /// Syntax Synopsis:
 | ||||
| /// ```ebnf
 | ||||
| /// ReturnStatement:
 | ||||
| ///     `return` Expression ;
 | ||||
| /// ```
 | ||||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||||
| #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] | ||||
| pub struct ReturnStatement { | ||||
|     /// The `return` keyword.
 | ||||
|     #[get = "pub"] | ||||
|     return_keyword: Keyword, | ||||
|     /// The expression of the return statement.
 | ||||
|     #[get = "pub"] | ||||
|     expression: Expression, | ||||
| } | ||||
| 
 | ||||
| impl SourceElement for ReturnStatement { | ||||
|     fn span(&self) -> Span { | ||||
|         self.return_keyword | ||||
|             .span() | ||||
|             .join(&self.expression.span()) | ||||
|             .expect("The span of the return statement is invalid.") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ReturnStatement { | ||||
|     /// Dissolves the [`ReturnStatement`] into its components.
 | ||||
|     #[must_use] | ||||
|     pub fn dissolve(self) -> (Keyword, Expression) { | ||||
|         (self.return_keyword, self.expression) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Represents a variable declaration in the syntax tree.
 | ||||
| ///
 | ||||
| /// Syntax Synopsis:
 | ||||
|  | @ -910,25 +913,6 @@ impl Parser<'_> { | |||
|                 })) | ||||
|             } | ||||
| 
 | ||||
|             // run statement
 | ||||
|             Reading::Atomic(Token::Keyword(run_keyword)) | ||||
|                 if run_keyword.keyword == KeywordKind::Run => | ||||
|             { | ||||
|                 // eat the run keyword
 | ||||
|                 self.forward(); | ||||
| 
 | ||||
|                 let expression = self.parse_expression(handler)?; | ||||
|                 let semicolon = self.parse_punctuation(';', true, handler)?; | ||||
| 
 | ||||
|                 tracing::trace!("Parsed run statement: {:?}", expression); | ||||
| 
 | ||||
|                 Ok(Statement::Run(Run { | ||||
|                     run_keyword, | ||||
|                     expression, | ||||
|                     semicolon, | ||||
|                 })) | ||||
|             } | ||||
| 
 | ||||
|             // semicolon statement
 | ||||
|             _ => self.parse_semicolon(handler).map(Statement::Semicolon), | ||||
|         } | ||||
|  | @ -941,6 +925,17 @@ impl Parser<'_> { | |||
|         handler: &impl Handler<base::Error>, | ||||
|     ) -> ParseResult<Semicolon> { | ||||
|         let statement = match self.stop_at_significant() { | ||||
|             Reading::Atomic(Token::Keyword(keyword)) if keyword.keyword == KeywordKind::Return => { | ||||
|                 // eat the return keyword
 | ||||
|                 self.forward(); | ||||
| 
 | ||||
|                 let expression = self.parse_expression(handler)?; | ||||
| 
 | ||||
|                 Ok(SemicolonStatement::Return(ReturnStatement { | ||||
|                     return_keyword: keyword, | ||||
|                     expression, | ||||
|                 })) | ||||
|             } | ||||
|             Reading::Atomic(Token::Keyword(keyword)) | ||||
|                 if matches!( | ||||
|                     keyword.keyword, | ||||
|  | @ -1025,6 +1020,7 @@ impl Parser<'_> { | |||
|                     expected: SyntaxKind::Either(&[ | ||||
|                         SyntaxKind::Keyword(KeywordKind::Int), | ||||
|                         SyntaxKind::Keyword(KeywordKind::Bool), | ||||
|                         SyntaxKind::Keyword(KeywordKind::Val), | ||||
|                     ]), | ||||
|                     found: unexpected.into_token(), | ||||
|                 }); | ||||
|  |  | |||
|  | @ -289,6 +289,10 @@ impl Primary { | |||
|                     matches!(r#type, ValueType::Integer) | ||||
|                         && prefix.operand().can_yield_type(r#type, scope) | ||||
|                 } | ||||
|                 PrefixOperator::Run(_) => { | ||||
|                     matches!(r#type, ValueType::Integer | ValueType::Boolean) | ||||
|                         && prefix.operand().can_yield_type(ValueType::String, scope) | ||||
|                 } | ||||
|             }, | ||||
|             Self::Indexed(indexed) => { | ||||
|                 if let Self::Identifier(ident) = indexed.object().as_ref() { | ||||
|  | @ -601,7 +605,7 @@ impl Transpiler { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[expect(clippy::too_many_lines)] | ||||
|     #[expect(clippy::too_many_lines, clippy::cognitive_complexity)] | ||||
|     pub(super) fn transpile_primary_expression( | ||||
|         &mut self, | ||||
|         primary: &Primary, | ||||
|  | @ -760,8 +764,8 @@ impl Transpiler { | |||
|                         StorageType::Byte | StorageType::Int | StorageType::Long | ||||
|                     ) => | ||||
|                     { | ||||
|                         let (target_objective, mut targets) = self.get_temp_scoreboard_locations(1); | ||||
|                         let target_ident = targets.pop().expect("at least size 1"); | ||||
|                         let (target_objective, [target_ident]) = | ||||
|                             self.get_temp_scoreboard_locations_array(); | ||||
| 
 | ||||
|                         let score_to_storage_cmd = Command::Execute(Execute::Store( | ||||
|                             format!( | ||||
|  | @ -807,6 +811,65 @@ impl Transpiler { | |||
| 
 | ||||
|                     Ok(cmds) | ||||
|                 } | ||||
|                 PrefixOperator::Run(_) => { | ||||
|                     let run_cmds = | ||||
|                         self.transpile_run_expression(prefix.operand(), scope, handler)?; | ||||
|                     let run_cmd = if run_cmds.len() == 1 { | ||||
|                         run_cmds.into_iter().next().expect("length is 1") | ||||
|                     } else { | ||||
|                         Command::Group(run_cmds) | ||||
|                     }; | ||||
|                     match target { | ||||
|                         DataLocation::ScoreboardValue { objective, target } => { | ||||
|                             let store = format!("result score {target} {objective}"); | ||||
|                             let exec = Command::Execute(Execute::Store( | ||||
|                                 store.into(), | ||||
|                                 Box::new(Execute::Run(Box::new(run_cmd))), | ||||
|                             )); | ||||
|                             Ok(vec![exec]) | ||||
|                         } | ||||
|                         DataLocation::Storage { | ||||
|                             storage_name, | ||||
|                             path, | ||||
|                             r#type, | ||||
|                         } => { | ||||
|                             let store = format!( | ||||
|                                 "{result_success} storage {storage_name} {path} {t} 1.0", | ||||
|                                 t = r#type.as_str(), | ||||
|                                 result_success = if matches!(r#type, StorageType::Boolean) { | ||||
|                                     "success" | ||||
|                                 } else { | ||||
|                                     "result" | ||||
|                                 } | ||||
|                             ); | ||||
|                             let exec = Command::Execute(Execute::Store( | ||||
|                                 store.into(), | ||||
|                                 Box::new(Execute::Run(Box::new(run_cmd))), | ||||
|                             )); | ||||
|                             Ok(vec![exec]) | ||||
|                         } | ||||
|                         DataLocation::Tag { tag_name, entity } => { | ||||
|                             let prepare_cmd = | ||||
|                                 Command::Raw(format!("tag {entity} remove {tag_name}")); | ||||
|                             let success_cmd = Command::Raw(format!("tag {entity} add {tag_name}")); | ||||
|                             let (temp_storage_name, [temp_storage_path]) = | ||||
|                                 self.get_temp_storage_locations_array(); | ||||
| 
 | ||||
|                             let store_cmd = Command::Execute(Execute::Store( | ||||
|                                 format!("success storage {temp_storage_name} {temp_storage_path} boolean 1.0").into(), 
 | ||||
|                                 Box::new(Execute::Run(Box::new(run_cmd))) | ||||
|                             )); | ||||
| 
 | ||||
|                             let if_cmd = Command::Execute(Execute::If( | ||||
|                                 Condition::Atom(format!("data storage {temp_storage_name} {{{temp_storage_name}:1b}}").into()), | ||||
|                                 Box::new(Execute::Run(Box::new(success_cmd))), | ||||
|                                 None, | ||||
|                             )); | ||||
| 
 | ||||
|                             Ok(vec![store_cmd, prepare_cmd, if_cmd]) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             Primary::Identifier(ident) => { | ||||
|                 let variable = scope.get_variable(ident.span.str()); | ||||
|  | @ -1215,6 +1278,25 @@ impl Transpiler { | |||
|                         }, | ||||
|                     )) | ||||
|                 } | ||||
|                 PrefixOperator::Run(_) => { | ||||
|                     let (temp_storage_name, [temp_storage_path]) = | ||||
|                         self.get_temp_storage_locations_array(); | ||||
|                     let cond = ExtendedCondition::Runtime(Condition::Atom( | ||||
|                         format!("data storage {temp_storage_name} {{{temp_storage_path}:1b}}") | ||||
|                             .into(), | ||||
|                     )); | ||||
|                     let store_cmds = self.transpile_primary_expression( | ||||
|                         primary, | ||||
|                         &DataLocation::Storage { | ||||
|                             storage_name: temp_storage_name, | ||||
|                             path: temp_storage_path, | ||||
|                             r#type: StorageType::Boolean, | ||||
|                         }, | ||||
|                         scope, | ||||
|                         handler, | ||||
|                     )?; | ||||
|                     Ok((store_cmds, cond)) | ||||
|                 } | ||||
|                 PrefixOperator::Negate(_) => { | ||||
|                     let err = TranspileError::MismatchedTypes(MismatchedTypes { | ||||
|                         expected_type: ExpectedType::Boolean, | ||||
|  | @ -1289,11 +1371,12 @@ impl Transpiler { | |||
|         let right = binary.right_operand(); | ||||
|         let operator = binary.operator(); | ||||
| 
 | ||||
|         let (temp_objective, temp_locations) = self.get_temp_scoreboard_locations(2); | ||||
|         let (temp_objective, [temp_location_a, temp_location_b]) = | ||||
|             self.get_temp_scoreboard_locations_array(); | ||||
| 
 | ||||
|         let score_target_location = match target { | ||||
|             DataLocation::ScoreboardValue { objective, target } => (objective, target), | ||||
|             _ => (&temp_objective, &temp_locations[0]), | ||||
|             _ => (&temp_objective, &temp_location_a), | ||||
|         }; | ||||
| 
 | ||||
|         let left_cmds = self.transpile_expression( | ||||
|  | @ -1318,7 +1401,7 @@ impl Transpiler { | |||
|                     right, | ||||
|                     &DataLocation::ScoreboardValue { | ||||
|                         objective: temp_objective.clone(), | ||||
|                         target: temp_locations[1].clone(), | ||||
|                         target: temp_location_b.clone(), | ||||
|                     }, | ||||
|                     scope, | ||||
|                     handler, | ||||
|  | @ -1328,7 +1411,7 @@ impl Transpiler { | |||
|                     right_cmds, | ||||
|                     ( | ||||
|                         temp_objective.as_str(), | ||||
|                         std::borrow::Cow::Borrowed(&temp_locations[1]), | ||||
|                         std::borrow::Cow::Borrowed(&temp_location_b), | ||||
|                     ), | ||||
|                 ) | ||||
|             }; | ||||
|  | @ -1419,13 +1502,12 @@ impl Transpiler { | |||
|             _ => unreachable!("This function should only be called for comparison operators."), | ||||
|         }; | ||||
| 
 | ||||
|         let (temp_objective, mut temp_locations) = self.get_temp_scoreboard_locations(2); | ||||
|         let (temp_objective, [temp_location_a, temp_location_b]) = | ||||
|             self.get_temp_scoreboard_locations_array(); | ||||
| 
 | ||||
|         let condition = Condition::Atom( | ||||
|             format!( | ||||
|                 "score {target} {temp_objective} {operator} {source} {temp_objective}", | ||||
|                 target = temp_locations[0], | ||||
|                 source = temp_locations[1] | ||||
|                 "score {temp_location_a} {temp_objective} {operator} {temp_location_b} {temp_objective}" | ||||
|             ) | ||||
|             .into(), | ||||
|         ); | ||||
|  | @ -1434,7 +1516,7 @@ impl Transpiler { | |||
|             binary.left_operand(), | ||||
|             &DataLocation::ScoreboardValue { | ||||
|                 objective: temp_objective.clone(), | ||||
|                 target: std::mem::take(&mut temp_locations[0]), | ||||
|                 target: temp_location_a, | ||||
|             }, | ||||
|             scope, | ||||
|             handler, | ||||
|  | @ -1443,7 +1525,7 @@ impl Transpiler { | |||
|             binary.right_operand(), | ||||
|             &DataLocation::ScoreboardValue { | ||||
|                 objective: temp_objective, | ||||
|                 target: std::mem::take(&mut temp_locations[1]), | ||||
|                 target: temp_location_b, | ||||
|             }, | ||||
|             scope, | ||||
|             handler, | ||||
|  | @ -1707,6 +1789,17 @@ impl Transpiler { | |||
|         (objective, targets) | ||||
|     } | ||||
| 
 | ||||
|     /// Get temporary scoreboard locations.
 | ||||
|     pub(super) fn get_temp_scoreboard_locations_array<const N: usize>( | ||||
|         &mut self, | ||||
|     ) -> (String, [String; N]) { | ||||
|         let (objective, targets) = self.get_temp_scoreboard_locations(N); | ||||
| 
 | ||||
|         let targets = targets.try_into().expect("build from range of type"); | ||||
| 
 | ||||
|         (objective, targets) | ||||
|     } | ||||
| 
 | ||||
|     /// Get temporary storage locations.
 | ||||
|     pub(super) fn get_temp_storage_locations(&mut self, amount: usize) -> (String, Vec<String>) { | ||||
|         let storage_name = "shulkerscript:temp_".to_string() | ||||
|  | @ -1734,4 +1827,15 @@ impl Transpiler { | |||
| 
 | ||||
|         (storage_name, paths) | ||||
|     } | ||||
| 
 | ||||
|     /// Get temporary storage locations.
 | ||||
|     pub(super) fn get_temp_storage_locations_array<const N: usize>( | ||||
|         &mut self, | ||||
|     ) -> (String, [String; N]) { | ||||
|         let (storage_name, paths) = self.get_temp_storage_locations(N); | ||||
| 
 | ||||
|         let paths = paths.try_into().expect("build from range of type"); | ||||
| 
 | ||||
|         (storage_name, paths) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -316,13 +316,13 @@ impl Transpiler { | |||
| 
 | ||||
|                                 VariableData::BooleanStorage { .. } | ||||
|                                 | VariableData::ScoreboardValue { .. } => { | ||||
|                                     let (temp_storage, mut temp_path) = | ||||
|                                         self.get_temp_storage_locations(1); | ||||
|                                     let (temp_storage, [temp_path]) = | ||||
|                                         self.get_temp_storage_locations_array(); | ||||
|                                     let prepare_cmds = self.transpile_primary_expression( | ||||
|                                         primary, | ||||
|                                         &super::expression::DataLocation::Storage { | ||||
|                                             storage_name: temp_storage.clone(), | ||||
|                                             path: temp_path[0].clone(), | ||||
|                                             path: temp_path.clone(), | ||||
|                                             r#type: match var.as_ref() { | ||||
|                                                 VariableData::BooleanStorage { .. } => { | ||||
|                                                     StorageType::Boolean | ||||
|  | @ -340,7 +340,7 @@ impl Transpiler { | |||
|                                     Ok(Parameter::Storage { | ||||
|                                         prepare_cmds, | ||||
|                                         storage_name: temp_storage, | ||||
|                                         path: std::mem::take(&mut temp_path[0]), | ||||
|                                         path: temp_path, | ||||
|                                     }) | ||||
|                                 } | ||||
|                                 _ => { | ||||
|  | @ -364,12 +364,13 @@ impl Transpiler { | |||
|                             | Primary::FunctionCall(_), | ||||
|                         ) | ||||
|                         | Expression::Binary(_) => { | ||||
|                             let (temp_storage, mut temp_path) = self.get_temp_storage_locations(1); | ||||
|                             let (temp_storage, [temp_path]) = | ||||
|                                 self.get_temp_storage_locations_array(); | ||||
|                             let prepare_cmds = self.transpile_expression( | ||||
|                                 expression, | ||||
|                                 &super::expression::DataLocation::Storage { | ||||
|                                     storage_name: temp_storage.clone(), | ||||
|                                     path: temp_path[0].clone(), | ||||
|                                     path: temp_path.clone(), | ||||
|                                     r#type: StorageType::Int, | ||||
|                                 }, | ||||
|                                 scope, | ||||
|  | @ -379,7 +380,7 @@ impl Transpiler { | |||
|                             Ok(Parameter::Storage { | ||||
|                                 prepare_cmds, | ||||
|                                 storage_name: temp_storage, | ||||
|                                 path: std::mem::take(&mut temp_path[0]), | ||||
|                                 path: temp_path, | ||||
|                             }) | ||||
|                         } | ||||
|                     }; | ||||
|  |  | |||
|  | @ -158,15 +158,12 @@ fn print_function( | |||
|                 json!({"nbt": path, "storage": storage_name, "color": PARAM_COLOR}), | ||||
|             ), | ||||
|             DataLocation::Tag { tag_name, entity } => { | ||||
|                 let (temp_storage_name, temp_storage_paths) = | ||||
|                     transpiler.get_temp_storage_locations(1); | ||||
|                 let (temp_storage_name, [temp_storage_path]) = | ||||
|                     transpiler.get_temp_storage_locations_array(); | ||||
|                 let selector = | ||||
|                     super::util::add_to_entity_selector(entity, &format!("tag={tag_name}")); | ||||
|                 let cmd = Command::Execute(Execute::Store( | ||||
|                     format!( | ||||
|                         "success storage {temp_storage_name} {path} byte 1.0", | ||||
|                         path = temp_storage_paths[0] | ||||
|                     ) | ||||
|                     format!("success storage {temp_storage_name} {temp_storage_path} byte 1.0") | ||||
|                         .into(), | ||||
|                     Box::new(Execute::Run(Box::new(Command::Raw(format!( | ||||
|                         "execute if entity {selector}" | ||||
|  | @ -175,7 +172,7 @@ fn print_function( | |||
| 
 | ||||
|                 ( | ||||
|                     Some(cmd), | ||||
|                     json!({"nbt": temp_storage_paths[0], "storage": temp_storage_name, "color": PARAM_COLOR}), | ||||
|                     json!({"nbt": temp_storage_path, "storage": temp_storage_name, "color": PARAM_COLOR}), | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | @ -367,10 +364,10 @@ fn print_function( | |||
|             } | ||||
| 
 | ||||
|             primary => { | ||||
|                 let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1); | ||||
|                 let (storage_name, [storage_path]) = transpiler.get_temp_storage_locations_array(); | ||||
|                 let location = DataLocation::Storage { | ||||
|                     storage_name, | ||||
|                     path: std::mem::take(&mut storage_paths[0]), | ||||
|                     path: storage_path, | ||||
|                     r#type: StorageType::Int, | ||||
|                 }; | ||||
|                 let cmds = transpiler.transpile_primary_expression( | ||||
|  | @ -388,10 +385,10 @@ fn print_function( | |||
|             } | ||||
|         }, | ||||
|         Expression::Binary(binary) => { | ||||
|             let (storage_name, mut storage_paths) = transpiler.get_temp_storage_locations(1); | ||||
|             let (storage_name, [storage_path]) = transpiler.get_temp_storage_locations_array(); | ||||
|             let location = DataLocation::Storage { | ||||
|                 storage_name, | ||||
|                 path: std::mem::take(&mut storage_paths[0]), | ||||
|                 path: storage_path, | ||||
|                 r#type: StorageType::Int, | ||||
|             }; | ||||
|             let cmds = | ||||
|  |  | |||
|  | @ -14,11 +14,11 @@ use crate::{ | |||
|     semantic::error::UnexpectedExpression, | ||||
|     syntax::syntax_tree::{ | ||||
|         declaration::{Declaration, ImportItems}, | ||||
|         expression::{Expression, FunctionCall, Primary}, | ||||
|         expression::{Expression, FunctionCall, PrefixOperator, Primary}, | ||||
|         program::{Namespace, ProgramFile}, | ||||
|         statement::{ | ||||
|             execute_block::{Conditional, Else, ExecuteBlock, ExecuteBlockHead, ExecuteBlockTail}, | ||||
|             SemicolonStatement, Statement, | ||||
|             ReturnStatement, SemicolonStatement, Statement, | ||||
|         }, | ||||
|         AnnotationAssignment, | ||||
|     }, | ||||
|  | @ -359,9 +359,6 @@ impl Transpiler { | |||
|             Statement::LiteralCommand(literal_command) => { | ||||
|                 Ok(vec![literal_command.clean_command().into()]) | ||||
|             } | ||||
|             Statement::Run(run) => { | ||||
|                 self.transpile_run_expression(run.expression(), program_identifier, scope, handler) | ||||
|             } | ||||
|             Statement::Block(_) => { | ||||
|                 unreachable!("Only literal commands are allowed in functions at this time.") | ||||
|             } | ||||
|  | @ -411,6 +408,11 @@ impl Transpiler { | |||
|                     Expression::Primary(Primary::FunctionCall(func)) => { | ||||
|                         self.transpile_function_call(func, scope, handler) | ||||
|                     } | ||||
|                     Expression::Primary(Primary::Prefix(prefix)) | ||||
|                         if matches!(prefix.operator(), PrefixOperator::Run(_)) => | ||||
|                     { | ||||
|                         self.transpile_run_expression(prefix.operand(), scope, handler) | ||||
|                     } | ||||
|                     unexpected => { | ||||
|                         let error = TranspileError::UnexpectedExpression(UnexpectedExpression( | ||||
|                             unexpected.clone(), | ||||
|  | @ -433,24 +435,155 @@ impl Transpiler { | |||
|                     scope, | ||||
|                     handler, | ||||
|                 ), | ||||
|                 SemicolonStatement::Return(ret) => { | ||||
|                     self.transpile_return_statement(ret, program_identifier, scope, handler) | ||||
|                 } | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[expect(clippy::only_used_in_recursion)] | ||||
|     fn transpile_run_expression( | ||||
|     #[expect(clippy::too_many_lines)] | ||||
|     fn transpile_return_statement( | ||||
|         &mut self, | ||||
|         expression: &Expression, | ||||
|         program_identifier: &str, | ||||
|         ret: &ReturnStatement, | ||||
|         _program_identifier: &str, | ||||
|         scope: &Arc<Scope>, | ||||
|         handler: &impl Handler<base::Error>, | ||||
|     ) -> TranspileResult<Vec<Command>> { | ||||
|         let comptime_val = ret | ||||
|             .expression() | ||||
|             .comptime_eval(scope, handler) | ||||
|             .map(|val| val.to_macro_string()); | ||||
| 
 | ||||
|         let (prepare_cmds, ret_cmd) = if let Ok(val) = comptime_val { | ||||
|             (Vec::new(), datapack::ReturnCommand::Value(val.into())) | ||||
|         } else { | ||||
|             match ret.expression() { | ||||
|                 Expression::Primary(Primary::Prefix(prefix)) | ||||
|                     if matches!(prefix.operator(), PrefixOperator::Run(_)) => | ||||
|                 { | ||||
|                     let ret_cmds = | ||||
|                         self.transpile_run_expression(prefix.operand(), scope, handler)?; | ||||
|                     let cmd = if ret_cmds.len() == 1 { | ||||
|                         ret_cmds.into_iter().next().unwrap() | ||||
|                     } else { | ||||
|                         Command::Group(ret_cmds) | ||||
|                     }; | ||||
|                     (Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd))) | ||||
|                 } | ||||
|                 Expression::Primary(Primary::FunctionCall(func)) => { | ||||
|                     let ret_cmds = self.transpile_function_call(func, scope, handler)?; | ||||
|                     let cmd = if ret_cmds.len() == 1 { | ||||
|                         ret_cmds.into_iter().next().unwrap() | ||||
|                     } else { | ||||
|                         Command::Group(ret_cmds) | ||||
|                     }; | ||||
|                     (Vec::new(), datapack::ReturnCommand::Command(Box::new(cmd))) | ||||
|                 } | ||||
|                 Expression::Primary(Primary::Identifier(ident)) => { | ||||
|                     if let Some(var) = scope.get_variable(ident.span.str()) { | ||||
|                         match var.as_ref() { | ||||
|                             VariableData::BooleanStorage { storage_name, path } => ( | ||||
|                                 Vec::new(), | ||||
|                                 datapack::ReturnCommand::Command(Box::new(Command::Raw(format!( | ||||
|                                     "data get storage {storage_name} {path}" | ||||
|                                 )))), | ||||
|                             ), | ||||
|                             VariableData::ComptimeValue { | ||||
|                                 value, | ||||
|                                 read_only: _, | ||||
|                             } => value.read().unwrap().as_ref().map_or_else( | ||||
|                                 || { | ||||
|                                     let error = TranspileError::MissingValue(MissingValue { | ||||
|                                         expression: ident.span.clone(), | ||||
|                                     }); | ||||
|                                     handler.receive(error.clone()); | ||||
|                                     Err(error) | ||||
|                                 }, | ||||
|                                 |val| { | ||||
|                                     let cmd = val.to_string_no_macro().map_or_else( | ||||
|                                         || Command::UsesMacro(val.to_macro_string().into()), | ||||
|                                         Command::Raw, | ||||
|                                     ); | ||||
|                                     Ok(( | ||||
|                                         Vec::new(), | ||||
|                                         datapack::ReturnCommand::Command(Box::new(cmd)), | ||||
|                                     )) | ||||
|                                 }, | ||||
|                             )?, | ||||
|                             VariableData::MacroParameter { | ||||
|                                 index: _, | ||||
|                                 macro_name, | ||||
|                             } => ( | ||||
|                                 Vec::new(), | ||||
|                                 datapack::ReturnCommand::Command(Box::new(Command::UsesMacro( | ||||
|                                     shulkerbox::util::MacroString::MacroString(vec![ | ||||
|                                         shulkerbox::util::MacroStringPart::MacroUsage( | ||||
|                                             macro_name.clone(), | ||||
|                                         ), | ||||
|                                     ]), | ||||
|                                 ))), | ||||
|                             ), | ||||
|                             VariableData::ScoreboardValue { objective, target } => ( | ||||
|                                 Vec::new(), | ||||
|                                 datapack::ReturnCommand::Command(Box::new(Command::Raw(format!( | ||||
|                                     "scoreboard players get {target} {objective}" | ||||
|                                 )))), | ||||
|                             ), | ||||
|                             _ => { | ||||
|                                 let error = | ||||
|                                     TranspileError::UnexpectedExpression(UnexpectedExpression( | ||||
|                                         Expression::Primary(Primary::Identifier(ident.clone())), | ||||
|                                     )); | ||||
|                                 handler.receive(error.clone()); | ||||
|                                 return Err(error); | ||||
|                             } | ||||
|                         } | ||||
|                     } else { | ||||
|                         let error = TranspileError::UnknownIdentifier(UnknownIdentifier { | ||||
|                             identifier: ident.span.clone(), | ||||
|                         }); | ||||
|                         handler.receive(error.clone()); | ||||
|                         return Err(error); | ||||
|                     } | ||||
|                 } | ||||
|                 _ => { | ||||
|                     let (temp_objective, [temp_target]) = | ||||
|                         self.get_temp_scoreboard_locations_array(); | ||||
|                     let ret_cmd = datapack::ReturnCommand::Command(Box::new(Command::Raw( | ||||
|                         format!("scoreboard players get {temp_target} {temp_objective}"), | ||||
|                     ))); | ||||
|                     let cmds = self.transpile_expression( | ||||
|                         ret.expression(), | ||||
|                         &super::expression::DataLocation::ScoreboardValue { | ||||
|                             objective: temp_objective, | ||||
|                             target: temp_target, | ||||
|                         }, | ||||
|                         scope, | ||||
|                         handler, | ||||
|                     )?; | ||||
|                     (cmds, ret_cmd) | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         let cmds = prepare_cmds | ||||
|             .into_iter() | ||||
|             .chain(std::iter::once(Command::Return(ret_cmd))) | ||||
|             .collect(); | ||||
| 
 | ||||
|         Ok(cmds) | ||||
|     } | ||||
| 
 | ||||
|     pub(super) fn transpile_run_expression( | ||||
|         &mut self, | ||||
|         expression: &Primary, | ||||
|         scope: &Arc<Scope>, | ||||
|         handler: &impl Handler<base::Error>, | ||||
|     ) -> TranspileResult<Vec<Command>> { | ||||
|         match expression { | ||||
|             Expression::Primary(Primary::FunctionCall(func)) => { | ||||
|                 self.transpile_function_call(func, scope, handler) | ||||
|             } | ||||
|             Expression::Primary(Primary::Identifier(ident)) => { | ||||
|                 match scope.get_variable(ident.span.str()).as_deref() { | ||||
|             Primary::FunctionCall(func) => self.transpile_function_call(func, scope, handler), | ||||
|             Primary::Identifier(ident) => match scope.get_variable(ident.span.str()).as_deref() { | ||||
|                 Some(VariableData::ComptimeValue { | ||||
|                     value, | ||||
|                     read_only: _, | ||||
|  | @ -472,7 +605,7 @@ impl Transpiler { | |||
|                 ), | ||||
|                 Some(_) => { | ||||
|                     let error = TranspileError::UnexpectedExpression(UnexpectedExpression( | ||||
|                             expression.clone(), | ||||
|                         Expression::Primary(expression.clone()), | ||||
|                     )); | ||||
|                     handler.receive(error.clone()); | ||||
|                     Err(error) | ||||
|  | @ -484,26 +617,23 @@ impl Transpiler { | |||
|                     handler.receive(error.clone()); | ||||
|                     Err(error) | ||||
|                 } | ||||
|                 } | ||||
|             } | ||||
|             expression @ Expression::Primary( | ||||
|             }, | ||||
| 
 | ||||
|             Primary::Integer(_) | ||||
|             | Primary::Boolean(_) | ||||
|             | Primary::Prefix(_) | ||||
|                 | Primary::Indexed(_), | ||||
|             ) => { | ||||
|                 let error = | ||||
|                     TranspileError::UnexpectedExpression(UnexpectedExpression(expression.clone())); | ||||
|             | Primary::Indexed(_) => { | ||||
|                 let error = TranspileError::UnexpectedExpression(UnexpectedExpression( | ||||
|                     Expression::Primary(expression.clone()), | ||||
|                 )); | ||||
|                 handler.receive(error.clone()); | ||||
|                 Err(error) | ||||
|             } | ||||
|             Expression::Primary(Primary::StringLiteral(string)) => { | ||||
|             Primary::StringLiteral(string) => { | ||||
|                 Ok(vec![Command::Raw(string.str_content().to_string())]) | ||||
|             } | ||||
|             Expression::Primary(Primary::MacroStringLiteral(string)) => { | ||||
|                 Ok(vec![Command::UsesMacro(string.into())]) | ||||
|             } | ||||
|             Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? { | ||||
|             Primary::MacroStringLiteral(string) => Ok(vec![Command::UsesMacro(string.into())]), | ||||
|             Primary::Lua(code) => match code.eval_comptime(scope, handler)? { | ||||
|                 Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]), | ||||
|                 Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]), | ||||
|                 Ok(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => { | ||||
|  | @ -523,13 +653,10 @@ impl Transpiler { | |||
|                 } | ||||
|             }, | ||||
| 
 | ||||
|             Expression::Primary(Primary::Parenthesized(parenthesized)) => self | ||||
|                 .transpile_run_expression( | ||||
|                     parenthesized.expression(), | ||||
|                     program_identifier, | ||||
|                     scope, | ||||
|                     handler, | ||||
|                 ), | ||||
|             Primary::Parenthesized(parenthesized) => match parenthesized.expression().as_ref() { | ||||
|                 Expression::Primary(expression) => { | ||||
|                     self.transpile_run_expression(expression, scope, handler) | ||||
|                 } | ||||
|                 Expression::Binary(bin) => match bin.comptime_eval(scope, handler) { | ||||
|                     Ok(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]), | ||||
|                     Ok(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]), | ||||
|  | @ -542,6 +669,7 @@ impl Transpiler { | |||
|                         Err(err) | ||||
|                     } | ||||
|                 }, | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -381,8 +381,7 @@ impl Transpiler { | |||
|                 handler, | ||||
|             )?; | ||||
|             if is_global { | ||||
|                 let (temp_objective, temp_targets) = self.get_temp_scoreboard_locations(1); | ||||
|                 let temp_target = &temp_targets[0]; | ||||
|                 let (temp_objective, [temp_target]) = self.get_temp_scoreboard_locations_array(); | ||||
|                 let test_cmd = match declaration.variable_type().keyword { | ||||
|                     KeywordKind::Int => { | ||||
|                         Command::Raw(format!("scoreboard players get {name} {target}")) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue