implement compile-time function arguments
This commit is contained in:
parent
bd8270bd5d
commit
3bc3ca180f
|
@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Member access (e.g. `.objective` to get objective name where int is stored)
|
||||
- Return statement
|
||||
- internal `print` function
|
||||
- reserve `while` and `for` keyword
|
||||
- Example: barebones compiler
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -219,7 +219,7 @@ Semicolon:
|
|||
|
||||
```ebnf
|
||||
FunctionVariableType:
|
||||
'macro' | 'int' | 'bool'
|
||||
'macro' | 'int' | 'bool' | 'val'
|
||||
;
|
||||
```
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ pub enum KeywordKind {
|
|||
Macro,
|
||||
Val,
|
||||
Return,
|
||||
While,
|
||||
For,
|
||||
}
|
||||
|
||||
impl Display for KeywordKind {
|
||||
|
@ -121,6 +123,8 @@ impl KeywordKind {
|
|||
Self::Macro => "macro",
|
||||
Self::Val => "val",
|
||||
Self::Return => "return",
|
||||
Self::While => "while",
|
||||
Self::For => "for",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,7 +161,12 @@ impl Function {
|
|||
) -> Result<(), error::Error> {
|
||||
let child_scope = SemanticScope::with_parent(scope);
|
||||
|
||||
if let Some(parameters) = self.parameters().as_ref().map(ConnectedList::elements) {
|
||||
if let Some(parameters) = self
|
||||
.parameters()
|
||||
.as_ref()
|
||||
.map(ConnectedList::elements)
|
||||
.map(Iterator::collect::<Vec<_>>)
|
||||
{
|
||||
if let Some(incompatible) = self.annotations().iter().find(|a| {
|
||||
["tick", "load", "uninstall"].contains(&a.assignment().identifier.span.str())
|
||||
}) {
|
||||
|
@ -174,6 +179,25 @@ impl Function {
|
|||
});
|
||||
handler.receive(err.clone());
|
||||
return Err(err);
|
||||
} else if parameters
|
||||
.iter()
|
||||
.any(|param| matches!(param.variable_type(), FunctionVariableType::Value(_)))
|
||||
{
|
||||
if let Some(incompatible) = self
|
||||
.annotations()
|
||||
.iter()
|
||||
.find(|a| a.assignment().identifier.span.str() == "deobfuscate")
|
||||
{
|
||||
let err =
|
||||
error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
|
||||
span: incompatible.assignment().identifier.span.clone(),
|
||||
reason:
|
||||
"functions with the `deobfuscate` annotation cannot have compile-time parameters"
|
||||
.to_string(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
for param in parameters {
|
||||
|
@ -182,6 +206,7 @@ impl Function {
|
|||
FunctionVariableType::Boolean(_) => VariableType::BooleanStorage,
|
||||
FunctionVariableType::Integer(_) => VariableType::ScoreboardValue,
|
||||
FunctionVariableType::Macro(_) => VariableType::MacroParameter,
|
||||
FunctionVariableType::Value(_) => VariableType::ComptimeValue,
|
||||
};
|
||||
child_scope.set_variable(name, var);
|
||||
}
|
||||
|
|
|
@ -179,7 +179,7 @@ impl SourceElement for Function {
|
|||
///
|
||||
/// ```ebnf
|
||||
/// FunctionVariableType:
|
||||
/// 'macro' | 'int' | 'bool'
|
||||
/// 'macro' | 'int' | 'bool' | 'val'
|
||||
/// ;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
|
@ -188,6 +188,7 @@ pub enum FunctionVariableType {
|
|||
Macro(Keyword),
|
||||
Integer(Keyword),
|
||||
Boolean(Keyword),
|
||||
Value(Keyword),
|
||||
}
|
||||
|
||||
/// Represents a function argument in the syntax tree.
|
||||
|
@ -607,12 +608,24 @@ impl Parser<'_> {
|
|||
identifier,
|
||||
})
|
||||
}
|
||||
Reading::Atomic(Token::Keyword(keyword)) if keyword.keyword == KeywordKind::Val => {
|
||||
let variable_type = FunctionVariableType::Value(keyword);
|
||||
self.forward();
|
||||
|
||||
let identifier = self.parse_identifier(handler)?;
|
||||
|
||||
Ok(FunctionParameter {
|
||||
variable_type,
|
||||
identifier,
|
||||
})
|
||||
}
|
||||
unexpected => {
|
||||
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||
expected: SyntaxKind::Either(&[
|
||||
SyntaxKind::Keyword(KeywordKind::Int),
|
||||
SyntaxKind::Keyword(KeywordKind::Bool),
|
||||
SyntaxKind::Keyword(KeywordKind::Macro),
|
||||
SyntaxKind::Keyword(KeywordKind::Val),
|
||||
]),
|
||||
found: unexpected.into_token(),
|
||||
});
|
||||
|
|
|
@ -17,16 +17,17 @@ use super::{
|
|||
},
|
||||
Scope, TranspileResult, Transpiler, VariableData,
|
||||
};
|
||||
use crate::syntax::syntax_tree::expression::{Indexed, Parenthesized};
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use crate::{
|
||||
base::{self, source_file::SourceElement, Handler, VoidHandler},
|
||||
lexical::token::{Identifier, MacroStringLiteralPart, StringLiteral},
|
||||
syntax::syntax_tree::expression::{
|
||||
Binary, BinaryOperator, Expression, MemberAccess, PrefixOperator, Primary,
|
||||
Binary, BinaryOperator, Expression, Indexed, MemberAccess, Parenthesized, PrefixOperator,
|
||||
Primary,
|
||||
},
|
||||
transpile::{
|
||||
error::{FunctionArgumentsNotAllowed, MissingValue},
|
||||
variables::FunctionVariableDataType,
|
||||
TranspileError,
|
||||
},
|
||||
};
|
||||
|
@ -579,7 +580,10 @@ impl Identifier {
|
|||
}),
|
||||
}
|
||||
}
|
||||
VariableData::Function { path, .. } => {
|
||||
VariableData::Function {
|
||||
variable_data: FunctionVariableDataType::Simple { path, .. },
|
||||
..
|
||||
} => {
|
||||
match member_access.member().span.str() {
|
||||
"path" => {
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
|
@ -597,7 +601,11 @@ impl Identifier {
|
|||
}),
|
||||
}
|
||||
}
|
||||
VariableData::InternalFunction { .. } => Err(NotComptime {
|
||||
VariableData::Function {
|
||||
variable_data: FunctionVariableDataType::ComptimeArguments { .. },
|
||||
..
|
||||
}
|
||||
| VariableData::InternalFunction { .. } => Err(NotComptime {
|
||||
expression: member_access.member().span(),
|
||||
}),
|
||||
VariableData::MacroParameter { macro_name, .. } => {
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use chksum_md5 as md5;
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use std::{borrow::ToOwned, collections::BTreeMap, sync::Arc};
|
||||
use itertools::Itertools;
|
||||
use std::{
|
||||
borrow::{Cow, ToOwned},
|
||||
collections::BTreeMap,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use shulkerbox::datapack::{Command, Execute};
|
||||
|
||||
|
@ -19,6 +24,7 @@ use crate::{
|
|||
transpile::{
|
||||
error::{IllegalAnnotationContent, MissingFunctionDeclaration},
|
||||
util::{MacroString, MacroStringPart},
|
||||
variables::FunctionVariableDataType,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -39,7 +45,6 @@ pub enum TranspiledFunctionArguments {
|
|||
impl Transpiler {
|
||||
/// Gets the function at the given path, or transpiles it if it hasn't been transpiled yet.
|
||||
/// Returns the location of the function or None if the function does not exist.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
#[tracing::instrument(level = "trace", skip(self, handler))]
|
||||
pub(super) fn get_or_transpile_function(
|
||||
&mut self,
|
||||
|
@ -50,13 +55,6 @@ impl Transpiler {
|
|||
) -> TranspileResult<(String, TranspiledFunctionArguments)> {
|
||||
let program_identifier = identifier_span.source_file().identifier();
|
||||
let function = scope.get_variable(identifier_span.str());
|
||||
let already_transpiled = function
|
||||
.as_ref()
|
||||
.expect("called function should be in scope")
|
||||
.as_ref()
|
||||
.as_function()
|
||||
.map(|(_, path, _)| path.get().is_some())
|
||||
.expect("called variable should be of type function");
|
||||
|
||||
let function_data = function.ok_or_else(|| {
|
||||
let err = TranspileError::MissingFunctionDeclaration(
|
||||
|
@ -66,21 +64,120 @@ impl Transpiler {
|
|||
err
|
||||
})?;
|
||||
|
||||
let VariableData::Function {
|
||||
function_data,
|
||||
let (function_data, function_scope, function_type) = function_data
|
||||
.as_ref()
|
||||
.as_function()
|
||||
.expect("called variable should be of type function");
|
||||
|
||||
match function_type {
|
||||
FunctionVariableDataType::Simple {
|
||||
path: function_path,
|
||||
function_scope,
|
||||
} = function_data.as_ref()
|
||||
else {
|
||||
unreachable!("must be of correct type, otherwise errored out before");
|
||||
};
|
||||
} => {
|
||||
let already_transpiled = function_path.get().is_some();
|
||||
|
||||
if !already_transpiled {
|
||||
self.prepare_transpile_function(
|
||||
function_data,
|
||||
program_identifier,
|
||||
identifier_span,
|
||||
|path| function_path.set(path).expect("not set before"),
|
||||
std::iter::repeat(None),
|
||||
None,
|
||||
function_scope,
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
}
|
||||
|
||||
let function_location = function_path
|
||||
.get()
|
||||
.ok_or_else(|| {
|
||||
let err = TranspileError::MissingFunctionDeclaration(
|
||||
MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
|
||||
);
|
||||
handler.receive(Box::new(err.clone()));
|
||||
err
|
||||
})
|
||||
.map(String::to_owned)?;
|
||||
|
||||
let args = self.transpile_function_arguments(
|
||||
function_data,
|
||||
&function_location,
|
||||
arguments,
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Ok((function_location, args))
|
||||
}
|
||||
FunctionVariableDataType::ComptimeArguments { function_paths } => {
|
||||
let comptime_args = self.transpile_comptime_function_arguments(
|
||||
function_data,
|
||||
arguments,
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
let hash = comptime_args_hash(&comptime_args);
|
||||
|
||||
let read_guard = function_paths.read().unwrap();
|
||||
let function_location = if let Some(data) = read_guard.get(&hash) {
|
||||
data.to_owned()
|
||||
} else {
|
||||
drop(read_guard);
|
||||
let function_scope = Scope::with_parent(function_scope.to_owned());
|
||||
let mut path = String::new();
|
||||
|
||||
self.prepare_transpile_function(
|
||||
function_data,
|
||||
program_identifier,
|
||||
identifier_span,
|
||||
|p| path = p,
|
||||
comptime_args.into_iter(),
|
||||
Some(&hash),
|
||||
&function_scope,
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
|
||||
function_paths
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(hash.clone(), path.clone());
|
||||
|
||||
path
|
||||
};
|
||||
|
||||
let args = self.transpile_function_arguments(
|
||||
function_data,
|
||||
&function_location,
|
||||
arguments,
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Ok((function_location, args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
|
||||
fn prepare_transpile_function(
|
||||
&mut self,
|
||||
function_data: &FunctionData,
|
||||
program_identifier: &str,
|
||||
identifier_span: &Span,
|
||||
set_function_path: impl FnOnce(String),
|
||||
comptime_args: impl Iterator<Item = Option<ComptimeValue>>,
|
||||
function_suffix: Option<&str>,
|
||||
function_scope: &Arc<Scope>,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<()> {
|
||||
tracing::trace!("Function not transpiled yet, transpiling.");
|
||||
|
||||
let statements = function_data.statements.clone();
|
||||
|
||||
let modified_name = function_data.annotations.get("deobfuscate").map_or_else(
|
||||
let mut modified_name = function_data.annotations.get("deobfuscate").map_or_else(
|
||||
|| {
|
||||
let hash_data = program_identifier.to_string() + "\0" + identifier_span.str();
|
||||
Ok("shu/".to_string() + &md5::hash(hash_data).to_hex_lowercase())
|
||||
|
@ -92,19 +189,16 @@ impl Transpiler {
|
|||
.ok()
|
||||
.and_then(|val| val.to_string_no_macro())
|
||||
.ok_or_else(|| {
|
||||
let err = TranspileError::IllegalAnnotationContent(
|
||||
IllegalAnnotationContent {
|
||||
let err =
|
||||
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||
annotation: identifier_span.clone(),
|
||||
message: "Cannot evaluate annotation at compile time"
|
||||
.to_string(),
|
||||
},
|
||||
);
|
||||
message: "Cannot evaluate annotation at compile time".to_string(),
|
||||
});
|
||||
handler.receive(Box::new(err.clone()));
|
||||
err
|
||||
}),
|
||||
TranspileAnnotationValue::Map(_, span) => {
|
||||
let err =
|
||||
TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||
let err = TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
|
||||
annotation: span.clone(),
|
||||
message: "Deobfuscate annotation cannot be a map.".to_string(),
|
||||
});
|
||||
|
@ -114,14 +208,25 @@ impl Transpiler {
|
|||
},
|
||||
)?;
|
||||
|
||||
if let Some(suffix) = function_suffix {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
let _ = write!(modified_name, "/{suffix}");
|
||||
}
|
||||
|
||||
let function_location = format!(
|
||||
"{namespace}:{modified_name}",
|
||||
namespace = function_data.namespace
|
||||
);
|
||||
|
||||
function_path.set(function_location.clone()).unwrap();
|
||||
set_function_path(function_location.clone());
|
||||
|
||||
for (i, param) in function_data.parameters.iter().enumerate() {
|
||||
for (i, (param, comptime_arg)) in function_data
|
||||
.parameters
|
||||
.iter()
|
||||
.zip(comptime_args)
|
||||
.enumerate()
|
||||
{
|
||||
let param_str = param.identifier().span.str();
|
||||
match param.variable_type() {
|
||||
FunctionVariableType::Macro(_) => {
|
||||
|
@ -162,6 +267,15 @@ impl Transpiler {
|
|||
},
|
||||
);
|
||||
}
|
||||
FunctionVariableType::Value(_) => {
|
||||
function_scope.set_variable(
|
||||
param_str,
|
||||
VariableData::ComptimeValue {
|
||||
value: Arc::new(RwLock::new(comptime_arg)),
|
||||
read_only: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,28 +302,8 @@ impl Transpiler {
|
|||
if function_data.annotations.contains_key("load") {
|
||||
self.datapack.add_load(&function_location);
|
||||
}
|
||||
}
|
||||
|
||||
let function_location = function_path
|
||||
.get()
|
||||
.ok_or_else(|| {
|
||||
let err = TranspileError::MissingFunctionDeclaration(
|
||||
MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
|
||||
);
|
||||
handler.receive(Box::new(err.clone()));
|
||||
err
|
||||
})
|
||||
.map(String::to_owned)?;
|
||||
|
||||
let args = self.transpile_function_arguments(
|
||||
function_data,
|
||||
&function_location,
|
||||
arguments,
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Ok((function_location, args))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transpile_function(
|
||||
|
@ -238,6 +332,51 @@ impl Transpiler {
|
|||
Ok(commands)
|
||||
}
|
||||
|
||||
#[expect(clippy::unused_self, clippy::needless_pass_by_ref_mut)]
|
||||
fn transpile_comptime_function_arguments(
|
||||
&mut self,
|
||||
function_data: &FunctionData,
|
||||
arguments: Option<&[&Expression]>,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Vec<Option<ComptimeValue>>> {
|
||||
let parameters = &function_data.parameters;
|
||||
let arg_count = arguments.map(<[&Expression]>::len).unwrap_or_default();
|
||||
|
||||
let mut has_comptime = parameters
|
||||
.iter()
|
||||
.any(|param| matches!(param.variable_type(), FunctionVariableType::Value(_)));
|
||||
|
||||
if has_comptime {
|
||||
if parameters.len() == arg_count {
|
||||
let vals = parameters
|
||||
.iter()
|
||||
.zip(arguments.iter().flat_map(|args| args.iter()))
|
||||
.map(|(param, arg)| match param.variable_type() {
|
||||
FunctionVariableType::Value(_) => {
|
||||
has_comptime = true;
|
||||
let val = arg.comptime_eval(scope, handler)?;
|
||||
Ok(Some(val))
|
||||
}
|
||||
_ => Ok(None),
|
||||
})
|
||||
.collect::<TranspileResult<Vec<Option<ComptimeValue>>>>()?;
|
||||
|
||||
Ok(vals)
|
||||
} else {
|
||||
let err = TranspileError::InvalidFunctionArguments(InvalidFunctionArguments {
|
||||
expected: parameters.len(),
|
||||
actual: arg_count,
|
||||
span: function_data.identifier_span.clone(),
|
||||
});
|
||||
handler.receive(Box::new(err.clone()));
|
||||
Err(err)
|
||||
}
|
||||
} else {
|
||||
Ok(vec![None; parameters.len()])
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn transpile_function_arguments(
|
||||
&mut self,
|
||||
|
@ -264,6 +403,7 @@ impl Transpiler {
|
|||
Some(arg_count) if arg_count > 0 => {
|
||||
#[derive(Debug, Clone, EnumAsInner)]
|
||||
enum Parameter {
|
||||
Comptime,
|
||||
Static(MacroString),
|
||||
Storage {
|
||||
prepare_cmds: Vec<Command>,
|
||||
|
@ -275,8 +415,19 @@ impl Transpiler {
|
|||
let mut compiled_args = Vec::<Parameter>::new();
|
||||
let mut errs = Vec::new();
|
||||
|
||||
for expression in arguments.iter().flat_map(|expressions| expressions.iter()) {
|
||||
let value = match expression {
|
||||
for (expression, is_comptime) in arguments
|
||||
.iter()
|
||||
.flat_map(|expressions| expressions.iter())
|
||||
.zip(
|
||||
parameters
|
||||
.iter()
|
||||
.map(|p| matches!(p.variable_type(), FunctionVariableType::Value(_))),
|
||||
)
|
||||
{
|
||||
let value = if is_comptime {
|
||||
Ok(Parameter::Comptime)
|
||||
} else {
|
||||
match expression {
|
||||
Expression::Primary(Primary::Lua(lua)) => {
|
||||
lua.eval_comptime(scope, handler).and_then(|val| match val {
|
||||
Ok(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
||||
|
@ -301,8 +452,10 @@ impl Transpiler {
|
|||
Ok(Parameter::Static(literal.into()))
|
||||
}
|
||||
Expression::Primary(primary @ Primary::Identifier(ident)) => {
|
||||
let var = scope.get_variable(ident.span.str()).ok_or_else(|| {
|
||||
let err = TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||
let var =
|
||||
scope.get_variable(ident.span.str()).ok_or_else(|| {
|
||||
let err =
|
||||
TranspileError::UnknownIdentifier(UnknownIdentifier {
|
||||
identifier: ident.span(),
|
||||
});
|
||||
handler.receive(Box::new(err.clone()));
|
||||
|
@ -345,7 +498,8 @@ impl Transpiler {
|
|||
})
|
||||
}
|
||||
_ => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
let err =
|
||||
TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: expression.span(),
|
||||
expected_type: ExpectedType::AnyOf(vec![
|
||||
ExpectedType::Integer,
|
||||
|
@ -412,6 +566,7 @@ impl Transpiler {
|
|||
path: temp_path,
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match value {
|
||||
|
@ -437,6 +592,7 @@ impl Transpiler {
|
|||
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(
|
||||
|
@ -469,6 +625,7 @@ impl Transpiler {
|
|||
let target = crate::util::identifier_to_scoreboard_target(param_str);
|
||||
|
||||
match data {
|
||||
Parameter::Comptime => {}
|
||||
Parameter::Static(s) => {
|
||||
match s.as_str() {
|
||||
Ok(s) => {
|
||||
|
@ -502,6 +659,7 @@ impl Transpiler {
|
|||
let target_path = crate::util::identifier_to_scoreboard_target(param_str);
|
||||
|
||||
match data {
|
||||
Parameter::Comptime => {}
|
||||
Parameter::Static(s) => {
|
||||
match s.as_str() {
|
||||
Ok(s) => {
|
||||
|
@ -525,6 +683,9 @@ impl Transpiler {
|
|||
}
|
||||
}
|
||||
},
|
||||
FunctionVariableType::Value(_) => {
|
||||
// handled before in `transpile_comptime_function_arguments`
|
||||
}
|
||||
}
|
||||
(require_dyn_params, acc_setup, acc_move, statics)},
|
||||
);
|
||||
|
@ -608,3 +769,31 @@ impl Transpiler {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn comptime_args_hash(args: &[Option<ComptimeValue>]) -> String {
|
||||
let combined = args
|
||||
.iter()
|
||||
.filter_map(Option::as_ref)
|
||||
.map(|arg| match arg {
|
||||
ComptimeValue::Boolean(b) => Cow::Owned(b.to_string()),
|
||||
ComptimeValue::Integer(i) => Cow::Owned(i.to_string()),
|
||||
ComptimeValue::String(s) => Cow::Borrowed(s.as_str()),
|
||||
ComptimeValue::MacroString(s) => match s.as_str() {
|
||||
Ok(s) => s,
|
||||
Err(parts) => {
|
||||
let s = parts
|
||||
.iter()
|
||||
.map(|p| match p {
|
||||
MacroStringPart::String(s) => Cow::Borrowed(s.as_str()),
|
||||
MacroStringPart::MacroUsage(u) => Cow::Owned(format!("`{u}`")),
|
||||
})
|
||||
.join("\0\0");
|
||||
|
||||
Cow::Owned(s)
|
||||
}
|
||||
},
|
||||
})
|
||||
.join("\0");
|
||||
|
||||
md5::hash(combined).to_hex_lowercase()
|
||||
}
|
||||
|
|
|
@ -426,7 +426,7 @@ mod disabled {
|
|||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<((), ())> {
|
||||
let _ = scope;
|
||||
handler.receive(TranspileError::LuaDisabled);
|
||||
handler.receive(Box::new(TranspileError::LuaDisabled));
|
||||
tracing::error!("Lua code evaluation is disabled");
|
||||
Err(TranspileError::LuaDisabled)
|
||||
}
|
||||
|
@ -440,9 +440,9 @@ mod disabled {
|
|||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<ComptimeValue>> {
|
||||
) -> TranspileResult<Result<ComptimeValue, crate::transpile::error::NotComptime>> {
|
||||
let _ = scope;
|
||||
handler.receive(TranspileError::LuaDisabled);
|
||||
handler.receive(Box::new(TranspileError::LuaDisabled));
|
||||
tracing::error!("Lua code evaluation is disabled");
|
||||
Err(TranspileError::LuaDisabled)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::{
|
|||
#[doc(hidden)]
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
pub mod conversions;
|
||||
|
||||
pub mod error;
|
||||
|
||||
pub mod expression;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
//! Transpiler for `Shulkerscript`
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet, HashSet},
|
||||
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
||||
ops::Deref,
|
||||
sync::{Arc, OnceLock},
|
||||
sync::{Arc, OnceLock, RwLock},
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||
base::{self, source_file::SourceElement, Handler},
|
||||
semantic::error::UnexpectedExpression,
|
||||
syntax::syntax_tree::{
|
||||
declaration::{Declaration, ImportItems},
|
||||
declaration::{Declaration, FunctionVariableType, ImportItems},
|
||||
expression::{Expression, FunctionCall, PrefixOperator, Primary},
|
||||
program::{Namespace, ProgramFile},
|
||||
statement::{
|
||||
|
@ -25,6 +25,7 @@ use crate::{
|
|||
transpile::{
|
||||
error::IllegalAnnotationContent,
|
||||
util::{MacroString, MacroStringPart},
|
||||
variables::FunctionVariableDataType,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -240,6 +241,7 @@ impl Transpiler {
|
|||
}
|
||||
|
||||
/// Transpiles the given declaration.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn transpile_declaration(
|
||||
&mut self,
|
||||
declaration: &Declaration,
|
||||
|
@ -283,12 +285,25 @@ impl Transpiler {
|
|||
public: function.is_public(),
|
||||
annotations,
|
||||
};
|
||||
let has_comptime_params = function_data
|
||||
.parameters
|
||||
.iter()
|
||||
.any(|param| matches!(param.variable_type(), FunctionVariableType::Value(_)));
|
||||
let function_scope = Scope::with_parent(scope.clone());
|
||||
scope.set_variable(
|
||||
&name,
|
||||
VariableData::Function {
|
||||
function_data,
|
||||
function_scope,
|
||||
variable_data: if has_comptime_params {
|
||||
FunctionVariableDataType::ComptimeArguments {
|
||||
function_paths: RwLock::new(HashMap::new()),
|
||||
}
|
||||
} else {
|
||||
FunctionVariableDataType::Simple {
|
||||
path: OnceLock::new(),
|
||||
function_scope: Scope::with_parent(scope.clone()),
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -297,8 +312,6 @@ impl Transpiler {
|
|||
let import_identifier =
|
||||
super::util::calculate_import_identifier(&program_identifier, path);
|
||||
|
||||
// let aliases = &mut self.aliases;
|
||||
|
||||
match import.items() {
|
||||
ImportItems::All(_) => {
|
||||
handler.receive(base::Error::Other(
|
||||
|
|
|
@ -47,10 +47,10 @@ pub enum VariableData {
|
|||
Function {
|
||||
/// The function data.
|
||||
function_data: FunctionData,
|
||||
/// The path to the function once it is generated.
|
||||
path: OnceLock<String>,
|
||||
/// The scope of the function.
|
||||
function_scope: Arc<Scope>,
|
||||
/// The variable data.
|
||||
variable_data: FunctionVariableDataType,
|
||||
},
|
||||
/// A macro function parameter.
|
||||
MacroParameter {
|
||||
|
@ -111,6 +111,34 @@ pub enum VariableData {
|
|||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
#[derive(Debug)]
|
||||
pub enum FunctionVariableDataType {
|
||||
Simple {
|
||||
/// The path to the function once it is generated.
|
||||
path: OnceLock<String>,
|
||||
},
|
||||
ComptimeArguments {
|
||||
/// The paths of the functions with different comptime arguments by hash.
|
||||
function_paths: RwLock<HashMap<String, String>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
impl Clone for FunctionVariableDataType {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Simple { path } => Self::Simple { path: path.clone() },
|
||||
Self::ComptimeArguments { function_paths } => {
|
||||
let function_paths = function_paths.read().unwrap();
|
||||
Self::ComptimeArguments {
|
||||
function_paths: RwLock::new(function_paths.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, EnumAsInner)]
|
||||
pub enum TranspileAssignmentTarget<'a> {
|
||||
Identifier(&'a Identifier),
|
||||
|
|
|
@ -62,7 +62,7 @@ pub fn identifier_to_macro(ident: &str) -> std::borrow::Cow<'_, str> {
|
|||
/// Does only strip invalid characters if the `shulkerbox` feature is not enabled.
|
||||
#[cfg(not(feature = "shulkerbox"))]
|
||||
#[must_use]
|
||||
pub fn identifier_to_macro(ident: &str) -> std::borrow::Cow<str> {
|
||||
pub fn identifier_to_macro(ident: &str) -> std::borrow::Cow<'_, str> {
|
||||
if ident.contains("__")
|
||||
|| ident
|
||||
.chars()
|
||||
|
@ -98,7 +98,7 @@ pub fn identifier_to_scoreboard_target(ident: &str) -> std::borrow::Cow<'_, str>
|
|||
/// Does only strip invalid characters if the `shulkerbox` feature is not enabled.
|
||||
#[cfg(not(feature = "shulkerbox"))]
|
||||
#[must_use]
|
||||
pub fn identifier_to_scoreboard_target(ident: &str) -> std::borrow::Cow<str> {
|
||||
pub fn identifier_to_scoreboard_target(ident: &str) -> std::borrow::Cow<'_, str> {
|
||||
if !(..=16).contains(&ident.len())
|
||||
|| ident
|
||||
.chars()
|
||||
|
|
Loading…
Reference in New Issue