diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs index e56d63c..8368a42 100644 --- a/src/semantic/mod.rs +++ b/src/semantic/mod.rs @@ -967,20 +967,10 @@ impl TemplateStringLiteral { for part in self.parts() { match part { TemplateStringLiteralPart::Expression { expression, .. } => { + expression.analyze_semantics(scope, handler)?; match expression.as_ref() { Expression::Primary(Primary::Identifier(identifier)) => { - if let Some(variable_type) = scope.get_variable(identifier.span.str()) { - // TODO: template string correct checks - // if variable_type != VariableType::MacroParameter { - // let err = error::Error::UnexpectedExpression(UnexpectedExpression( - // Box::new(Expression::Primary(Primary::Identifier( - // identifier.clone(), - // ))), - // )); - // handler.receive(err.clone()); - // errs.push(err); - // } - } else { + if scope.get_variable(identifier.span.str()).is_none() { let err = error::Error::UnknownIdentifier(UnknownIdentifier { identifier: identifier.span.clone(), }); @@ -1026,6 +1016,9 @@ impl TemplateStringLiteral { errs.push(err); } } + Expression::Primary(Primary::MemberAccess(member_access)) => { + member_access.parent().analyze_semantics(scope, handler)?; + } _ => { let err = error::Error::UnexpectedExpression(UnexpectedExpression( expression.clone(), diff --git a/src/syntax/syntax_tree/declaration.rs b/src/syntax/syntax_tree/declaration.rs index 88b458b..dcd814b 100644 --- a/src/syntax/syntax_tree/declaration.rs +++ b/src/syntax/syntax_tree/declaration.rs @@ -344,6 +344,7 @@ impl Parser<'_> { /// /// # Errors /// - cannot parse declaration from current position + #[expect(clippy::too_many_lines)] #[tracing::instrument(level = "trace", skip_all)] pub fn parse_declaration( &mut self, diff --git a/src/syntax/syntax_tree/statement.rs b/src/syntax/syntax_tree/statement.rs index 57a09a1..c16ea1e 100644 --- a/src/syntax/syntax_tree/statement.rs +++ b/src/syntax/syntax_tree/statement.rs @@ -1010,6 +1010,7 @@ impl Parser<'_> { /// /// # Errors /// - cannot parse variable declaration from current position + #[expect(clippy::too_many_lines)] #[tracing::instrument(level = "trace", skip_all)] pub fn parse_variable_declaration( &mut self, diff --git a/src/transpile/function.rs b/src/transpile/function.rs index 5dd5298..e32677c 100644 --- a/src/transpile/function.rs +++ b/src/transpile/function.rs @@ -412,7 +412,7 @@ impl Transpiler { }, } - let mut compiled_args = Vec::::new(); + let mut compiled_args = Vec::<(Parameter, Span)>::new(); let mut errs = Vec::new(); for (expression, is_comptime) in arguments @@ -571,11 +571,13 @@ impl Transpiler { match value { Ok(value) => { - compiled_args.push(value); + compiled_args.push((value, expression.span())); } Err(err) => { - compiled_args - .push(Parameter::Static(MacroString::String(String::new()))); + compiled_args.push(( + Parameter::Static(MacroString::String(String::new())), + expression.span(), + )); errs.push(err.clone()); } } @@ -584,111 +586,164 @@ impl Transpiler { return Err(err.clone()); } - if compiled_args.iter().any(|arg| !arg.is_static()) { - let (require_dyn_params, mut setup_cmds, move_cmds, static_params) = parameters.clone().into_iter().zip(compiled_args).fold( - (false, Vec::new(), Vec::new(), BTreeMap::new()), - |(mut require_dyn_params, mut acc_setup, mut acc_move, mut statics), (param, data)| { - match param.variable_type() { - FunctionVariableType::Macro(_) => { - let arg_name = crate::util::identifier_to_macro(param.identifier().span.str()); - match data { - Parameter::Comptime => {} - Parameter::Static(s) => { - match s { - MacroString::String(value) => statics.insert( - arg_name.to_string(), - MacroString::String(crate::util::escape_str(&value).to_string()) + if compiled_args.iter().any(|(arg, _)| !arg.is_static()) { + let mut require_dyn_params = false; + let mut setup_cmds = Vec::new(); + let mut move_cmds = Vec::new(); + let mut statics = BTreeMap::new(); + + for (param, (data, span)) in parameters.clone().into_iter().zip(compiled_args) { + match param.variable_type() { + FunctionVariableType::Macro(_) => { + let arg_name = + crate::util::identifier_to_macro(param.identifier().span.str()); + match data { + Parameter::Comptime => {} + Parameter::Static(s) => { + match s { + MacroString::String(value) => statics.insert( + arg_name.to_string(), + MacroString::String( + crate::util::escape_str(&value).to_string(), ), - MacroString::MacroString(parts) => { - let parts = parts.into_iter().map(|part| { - match part { - MacroStringPart::String(s) => MacroStringPart::String(crate::util::escape_str(&s).to_string()), - MacroStringPart::MacroUsage(m) => MacroStringPart::MacroUsage(m), + ), + MacroString::MacroString(parts) => { + let parts = parts + .into_iter() + .map(|part| match part { + MacroStringPart::String(s) => { + MacroStringPart::String( + crate::util::escape_str(&s) + .to_string(), + ) } - }).collect(); - statics.insert(arg_name.to_string(), MacroString::MacroString(parts)) - } - }; - } - Parameter::Storage { prepare_cmds, storage_name, path } => { - require_dyn_params = true; - acc_setup.extend(prepare_cmds); - acc_move.push(Command::Raw( + MacroStringPart::MacroUsage(m) => { + MacroStringPart::MacroUsage(m) + } + }) + .collect(); + statics.insert( + arg_name.to_string(), + MacroString::MacroString(parts), + ) + } + }; + } + Parameter::Storage { + prepare_cmds, + storage_name, + path, + } => { + require_dyn_params = true; + setup_cmds.extend(prepare_cmds); + move_cmds.push(Command::Raw( format!(r"data modify storage shulkerscript:function_arguments {arg_name} set from storage {storage_name} {path}") )); - } } } - FunctionVariableType::Integer(_) => { - let objective = format!("shu_arguments_{}", function_location.replace(['/', ':'], "_")); - let param_str = param.identifier().span.str(); - let target = crate::util::identifier_to_scoreboard_target(param_str); + } + FunctionVariableType::Integer(_) => { + let objective = format!( + "shu_arguments_{}", + function_location.replace(['/', ':'], "_") + ); + let param_str = param.identifier().span.str(); + let target = + crate::util::identifier_to_scoreboard_target(param_str); - match data { - Parameter::Comptime => {} - Parameter::Static(s) => { - match s.as_str() { - Ok(s) => { - if s.parse::().is_ok() { - acc_move.push(Command::Raw(format!(r"scoreboard players set {target} {objective} {s}"))); - } else { - panic!("non-integer static argument") - } - } - Err(parts) => { - acc_move.push(Command::UsesMacro(MacroString::MacroString( + match data { + Parameter::Comptime => {} + Parameter::Static(s) => match s.as_str() { + Ok(s) => { + if s.parse::().is_ok() { + move_cmds.push(Command::Raw(format!(r"scoreboard players set {target} {objective} {s}"))); + } else { + let err = TranspileError::MismatchedTypes( + MismatchedTypes { + expression: span, + expected_type: ExpectedType::Integer, + }, + ); + handler.receive(Box::new(err.clone())); + return Err(err); + } + } + Err(parts) => { + move_cmds.push(Command::UsesMacro(MacroString::MacroString( std::iter::once(MacroStringPart::String(format!("scoreboard players set {target} {objective} "))) .chain(parts.iter().cloned()).collect() ).into())); - } + } + }, + Parameter::Storage { + prepare_cmds, + storage_name, + path, + } => { + setup_cmds.extend(prepare_cmds); + move_cmds.push(Command::Execute(Execute::Store( + format!("result score {target} {objective}").into(), + Box::new(Execute::Run(Box::new(Command::Raw( + format!("data get storage {storage_name} {path}"), + )))), + ))); + } + } + } + FunctionVariableType::Boolean(_) => { + require_dyn_params = true; + let target_storage_name = format!( + "shulkerscript:arguments_{}", + function_location.replace(['/', ':'], "_") + ); + let param_str = param.identifier().span.str(); + let target_path = + crate::util::identifier_to_scoreboard_target(param_str); + + match data { + Parameter::Comptime => {} + Parameter::Static(s) => match s.as_str() { + Ok(s) => { + if let Ok(b) = s.parse::() { + move_cmds.push(Command::Raw(format!("data modify storage {target_storage_name} {target_path} set value {}", if b { "1b" } else { "0b" }))); + } else { + let err = TranspileError::MismatchedTypes( + MismatchedTypes { + expression: span, + expected_type: ExpectedType::Boolean, + }, + ); + handler.receive(Box::new(err.clone())); + return Err(err); } } - Parameter::Storage { prepare_cmds, storage_name, path } => { - acc_setup.extend(prepare_cmds); - acc_move.push(Command::Execute(Execute::Store( - format!("result score {target} {objective}").into(), - Box::new(Execute::Run(Box::new(Command::Raw(format!("data get storage {storage_name} {path}"))))) - ))); - } - } - }, - FunctionVariableType::Boolean(_) => { - require_dyn_params = true; - let target_storage_name = format!("shulkerscript:arguments_{}", function_location.replace(['/', ':'], "_")); - let param_str = param.identifier().span.str(); - let target_path = crate::util::identifier_to_scoreboard_target(param_str); - - match data { - Parameter::Comptime => {} - Parameter::Static(s) => { - match s.as_str() { - Ok(s) => { - if let Ok(b) = s.parse::() { - acc_move.push(Command::Raw(format!("data modify storage {target_storage_name} {target_path} set value {}", if b { "1b" } else { "0b" }))); - } else { - panic!("non-integer static argument") - } - } - Err(parts) => { - acc_move.push(Command::UsesMacro(MacroString::MacroString( + Err(parts) => { + move_cmds.push(Command::UsesMacro(MacroString::MacroString( std::iter::once(MacroStringPart::String(format!("data modify storage {target_storage_name} {target_path} set value "))) .chain(parts.iter().cloned()).collect() ).into())); - } - } - } - Parameter::Storage { prepare_cmds, storage_name, path } => { - acc_setup.extend(prepare_cmds); - acc_move.push(Command::Raw(format!("data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"))); } + }, + Parameter::Storage { + prepare_cmds, + storage_name, + path, + } => { + setup_cmds.extend(prepare_cmds); + move_cmds.push(Command::Raw(format!("data modify storage {target_storage_name} {target_path} set from storage {storage_name} {path}"))); } - }, - FunctionVariableType::Value(_) => { - // handled before in `transpile_comptime_function_arguments` } } - (require_dyn_params, acc_setup, acc_move, statics)}, - ); + FunctionVariableType::Value(_) => { + // handled before in `transpile_comptime_function_arguments` + } + } + } + + let require_dyn_params = require_dyn_params; + let move_cmds = move_cmds; + let static_params = statics; + if require_dyn_params { let statics_len = static_params.len(); let joined_statics = super::util::join_macro_strings( @@ -749,16 +804,15 @@ impl Transpiler { )) } } else { - let function_args = parameters - .clone() - .into_iter() - .zip( - compiled_args - .into_iter() - .map(|arg| arg.into_static().expect("checked in if condition")), - ) - .map(|(k, v)| (k.identifier().span.str().to_string(), v)) - .collect(); + let function_args = + parameters + .clone() + .into_iter() + .zip(compiled_args.into_iter().map(|(arg, _)| { + arg.into_static().expect("checked in if condition") + })) + .map(|(k, v)| (k.identifier().span.str().to_string(), v)) + .collect(); Ok(TranspiledFunctionArguments::Static( function_args, Vec::new(), diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index b15fa85..1a1eca3 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -69,6 +69,7 @@ impl Transpiler { /// /// # Errors /// - [`TranspileError::MissingFunctionDeclaration`] If a called function is missing + #[expect(clippy::too_many_lines)] #[tracing::instrument(level = "trace", skip_all)] pub fn transpile( mut self, diff --git a/src/transpile/util.rs b/src/transpile/util.rs index 50a9bf9..849ee54 100644 --- a/src/transpile/util.rs +++ b/src/transpile/util.rs @@ -329,6 +329,13 @@ impl TemplateStringLiteral { Err(err) } } + Expression::Primary(Primary::MemberAccess(member_access)) => { + let value = member_access.parent().comptime_member_access(member_access, scope, handler).inspect_err(|err| { + handler.receive(Box::new(TranspileError::NotComptime(err.clone()))); + })?.to_macro_string(); + + value.as_str().map_or_else(|_| todo!("comptime value resulting in macro string with macros"), |s| Ok(MacroStringPart::String(s.into_owned()))) + } _ => todo!("other expressions in template strings"), } }