implement dynamic (scoreboard, storages) values passed in as macro parameters
- does not compile without the 'shulkerbox' feature enabled
This commit is contained in:
parent
5740172ddb
commit
cebe3e9cb0
|
@ -22,10 +22,8 @@ serde = ["dep:serde", "dep:flexbuffers", "shulkerbox?/serde"]
|
|||
shulkerbox = ["dep:shulkerbox", "dep:chksum-md5"]
|
||||
zip = ["shulkerbox?/zip"]
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
path-absolutize = { version = "3.1.1", features = ["use_unix_paths_on_wasm"] }
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
chksum-md5 = { version = "0.1.0", optional = true }
|
||||
colored = "3.0.0"
|
||||
derive_more = { version = "2.0.1", default-features = false, features = ["deref", "deref_mut", "from"] }
|
||||
|
@ -34,7 +32,6 @@ flexbuffers = { version = "25.2.10", optional = true }
|
|||
getset = "0.1.2"
|
||||
itertools = "0.14.0"
|
||||
mlua = { version = "0.10.2", features = ["lua54", "vendored"], optional = true }
|
||||
path-absolutize = "3.1.1"
|
||||
pathdiff = "0.2.3"
|
||||
serde = { version = "1.0.217", features = ["derive"], optional = true }
|
||||
# shulkerbox = { version = "0.1.0", default-features = false, optional = true }
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
semantic::error::{ConflictingFunctionNames, InvalidFunctionArguments, UnexpectedExpression},
|
||||
};
|
||||
|
||||
use super::{expression::ValueType, FunctionData};
|
||||
use super::{expression::ExpectedType, FunctionData};
|
||||
|
||||
/// Errors that can occur during transpilation.
|
||||
#[allow(clippy::module_name_repetitions, missing_docs)]
|
||||
|
@ -40,6 +40,8 @@ pub enum TranspileError {
|
|||
AssignmentError(#[from] AssignmentError),
|
||||
#[error(transparent)]
|
||||
UnknownIdentifier(#[from] UnknownIdentifier),
|
||||
#[error(transparent)]
|
||||
MissingValue(#[from] MissingValue),
|
||||
}
|
||||
|
||||
/// The result of a transpilation operation.
|
||||
|
@ -193,7 +195,7 @@ impl std::error::Error for IllegalAnnotationContent {}
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MismatchedTypes {
|
||||
pub expression: Span,
|
||||
pub expected_type: ValueType,
|
||||
pub expected_type: ExpectedType,
|
||||
}
|
||||
|
||||
impl Display for MismatchedTypes {
|
||||
|
@ -280,3 +282,30 @@ impl Display for UnknownIdentifier {
|
|||
}
|
||||
|
||||
impl std::error::Error for UnknownIdentifier {}
|
||||
|
||||
/// An error that occurs when there is a value expected but none provided.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct MissingValue {
|
||||
pub expression: Span,
|
||||
}
|
||||
|
||||
impl Display for MissingValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
Message::new(
|
||||
Severity::Error,
|
||||
"The expression is expected to return a value, but no value is found."
|
||||
)
|
||||
)?;
|
||||
|
||||
write!(
|
||||
f,
|
||||
"\n{}",
|
||||
SourceCodeDisplay::new(&self.expression, Option::<u8>::None)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for MissingValue {}
|
||||
|
|
|
@ -4,17 +4,21 @@ use std::{fmt::Display, sync::Arc};
|
|||
|
||||
use super::{Scope, VariableData};
|
||||
use crate::{
|
||||
base::VoidHandler,
|
||||
base::{self, Handler, VoidHandler},
|
||||
lexical::token::MacroStringLiteralPart,
|
||||
syntax::syntax_tree::expression::{
|
||||
Binary, BinaryOperator, Expression, PrefixOperator, Primary,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use enum_as_inner::EnumAsInner;
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use shulkerbox::prelude::{Command, Condition, Execute};
|
||||
use shulkerbox::{
|
||||
prelude::{Command, Condition, Execute},
|
||||
util::MacroString,
|
||||
};
|
||||
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use super::{
|
||||
|
@ -23,8 +27,11 @@ use super::{
|
|||
};
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
use crate::{
|
||||
base::{self, source_file::SourceElement, Handler},
|
||||
transpile::{error::FunctionArgumentsNotAllowed, TranspileError},
|
||||
base::source_file::SourceElement,
|
||||
transpile::{
|
||||
error::{FunctionArgumentsNotAllowed, MissingValue},
|
||||
TranspileError,
|
||||
},
|
||||
};
|
||||
|
||||
/// Compile-time evaluated value
|
||||
|
@ -34,7 +41,7 @@ pub enum ComptimeValue {
|
|||
Boolean(bool),
|
||||
Integer(i64),
|
||||
String(String),
|
||||
MacroString(String),
|
||||
MacroString(MacroString),
|
||||
}
|
||||
|
||||
impl Display for ComptimeValue {
|
||||
|
@ -43,7 +50,7 @@ impl Display for ComptimeValue {
|
|||
Self::Boolean(boolean) => write!(f, "{boolean}"),
|
||||
Self::Integer(int) => write!(f, "{int}"),
|
||||
Self::String(string) => write!(f, "{string}"),
|
||||
Self::MacroString(macro_string) => write!(f, "{macro_string}"),
|
||||
Self::MacroString(macro_string) => write!(f, "{}", macro_string.compile()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +87,69 @@ impl Display for ValueType {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ExpectedType {
|
||||
Boolean,
|
||||
Integer,
|
||||
String,
|
||||
Any,
|
||||
AnyOf(Vec<ExpectedType>),
|
||||
}
|
||||
|
||||
impl ExpectedType {
|
||||
/// Add another expected type to the list of expected types.
|
||||
#[must_use]
|
||||
pub fn or(self, or: Self) -> Self {
|
||||
match self {
|
||||
Self::Boolean | Self::Integer | Self::String => match or {
|
||||
Self::Boolean | Self::Integer | Self::String => Self::AnyOf(vec![self, or]),
|
||||
Self::Any => Self::Any,
|
||||
Self::AnyOf(mut types) => {
|
||||
types.push(self);
|
||||
Self::AnyOf(types)
|
||||
}
|
||||
},
|
||||
Self::Any => Self::Any,
|
||||
Self::AnyOf(mut types) => {
|
||||
types.push(or);
|
||||
Self::AnyOf(types)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ExpectedType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Boolean => write!(f, "boolean"),
|
||||
Self::Integer => write!(f, "integer"),
|
||||
Self::String => write!(f, "string"),
|
||||
Self::Any => write!(f, "any"),
|
||||
Self::AnyOf(types) => {
|
||||
write!(f, "any of [")?;
|
||||
for (i, r#type) in types.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{type}")?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValueType> for ExpectedType {
|
||||
fn from(value: ValueType) -> Self {
|
||||
match value {
|
||||
ValueType::Boolean => Self::Boolean,
|
||||
ValueType::Integer => Self::Integer,
|
||||
ValueType::String => Self::String,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Location of data
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -151,6 +221,7 @@ impl StorageType {
|
|||
}
|
||||
|
||||
/// Condition
|
||||
#[cfg(feature = "shulkerbox")]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, EnumAsInner)]
|
||||
pub enum ExtendedCondition {
|
||||
/// Runtime condition
|
||||
|
@ -171,10 +242,14 @@ impl Expression {
|
|||
|
||||
/// Evaluate at compile-time.
|
||||
#[must_use]
|
||||
pub fn comptime_eval(&self, scope: &Arc<Scope>) -> Option<ComptimeValue> {
|
||||
pub fn comptime_eval(
|
||||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ComptimeValue> {
|
||||
match self {
|
||||
Self::Primary(primary) => primary.comptime_eval(scope),
|
||||
Self::Binary(binary) => binary.comptime_eval(scope),
|
||||
Self::Primary(primary) => primary.comptime_eval(scope, handler),
|
||||
Self::Binary(binary) => binary.comptime_eval(scope, handler),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,16 +291,19 @@ impl Primary {
|
|||
&& prefix.operand().can_yield_type(r#type, scope)
|
||||
}
|
||||
},
|
||||
#[cfg_attr(not(feature = "lua"), expect(unused_variables))]
|
||||
Self::Lua(lua) => {
|
||||
if let Ok(value) = lua.eval(&VoidHandler) {
|
||||
match value {
|
||||
mlua::Value::Boolean(_) => matches!(r#type, ValueType::Boolean),
|
||||
mlua::Value::Integer(_) => matches!(r#type, ValueType::Integer),
|
||||
mlua::Value::String(_) => matches!(r#type, ValueType::String),
|
||||
_ => false,
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "lua")] {
|
||||
lua.eval(&VoidHandler).map_or(false, |value| match value {
|
||||
mlua::Value::Boolean(_) => matches!(r#type, ValueType::Boolean),
|
||||
mlua::Value::Integer(_) => matches!(r#type, ValueType::Integer),
|
||||
mlua::Value::String(_) => matches!(r#type, ValueType::String),
|
||||
_ => false,
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Self::StringLiteral(_) | Self::MacroStringLiteral(_) => {
|
||||
|
@ -236,7 +314,11 @@ impl Primary {
|
|||
|
||||
/// Evaluate at compile-time.
|
||||
#[must_use]
|
||||
pub fn comptime_eval(&self, scope: &Arc<Scope>) -> Option<ComptimeValue> {
|
||||
pub fn comptime_eval(
|
||||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ComptimeValue> {
|
||||
match self {
|
||||
Self::Boolean(boolean) => Some(ComptimeValue::Boolean(boolean.value())),
|
||||
Self::Integer(int) => Some(ComptimeValue::Integer(int.as_i64())),
|
||||
|
@ -244,20 +326,30 @@ impl Primary {
|
|||
string_literal.str_content().to_string(),
|
||||
)),
|
||||
Self::Identifier(_) | Self::FunctionCall(_) => None,
|
||||
Self::Parenthesized(parenthesized) => parenthesized.expression().comptime_eval(scope),
|
||||
Self::Prefix(prefix) => prefix.operand().comptime_eval(scope).and_then(|val| {
|
||||
match (prefix.operator(), val) {
|
||||
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
||||
Some(ComptimeValue::Boolean(!boolean))
|
||||
}
|
||||
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
||||
Some(ComptimeValue::Integer(-int))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}),
|
||||
// TODO: throw error
|
||||
Self::Lua(lua) => lua.eval_comptime(&VoidHandler).ok().flatten(),
|
||||
Self::Parenthesized(parenthesized) => {
|
||||
parenthesized.expression().comptime_eval(scope, handler)
|
||||
}
|
||||
Self::Prefix(prefix) => {
|
||||
prefix
|
||||
.operand()
|
||||
.comptime_eval(scope, handler)
|
||||
.and_then(|val| match (prefix.operator(), val) {
|
||||
(PrefixOperator::LogicalNot(_), ComptimeValue::Boolean(boolean)) => {
|
||||
Some(ComptimeValue::Boolean(!boolean))
|
||||
}
|
||||
(PrefixOperator::Negate(_), ComptimeValue::Integer(int)) => {
|
||||
Some(ComptimeValue::Integer(-int))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
Self::Lua(lua) => lua
|
||||
.eval_comptime(&VoidHandler)
|
||||
.inspect_err(|err| {
|
||||
handler.receive(err.clone());
|
||||
})
|
||||
.ok()
|
||||
.flatten(),
|
||||
Self::MacroStringLiteral(macro_string_literal) => {
|
||||
if macro_string_literal
|
||||
.parts()
|
||||
|
@ -265,7 +357,7 @@ impl Primary {
|
|||
.any(|part| matches!(part, MacroStringLiteralPart::MacroUsage { .. }))
|
||||
{
|
||||
Some(ComptimeValue::MacroString(
|
||||
macro_string_literal.str_content(),
|
||||
macro_string_literal.clone().into(),
|
||||
))
|
||||
} else {
|
||||
Some(ComptimeValue::String(macro_string_literal.str_content()))
|
||||
|
@ -318,9 +410,13 @@ impl Binary {
|
|||
|
||||
/// Evaluate at compile-time.
|
||||
#[must_use]
|
||||
pub fn comptime_eval(&self, scope: &Arc<Scope>) -> Option<ComptimeValue> {
|
||||
let left = self.left_operand().comptime_eval(scope)?;
|
||||
let right = self.right_operand().comptime_eval(scope)?;
|
||||
pub fn comptime_eval(
|
||||
&self,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> Option<ComptimeValue> {
|
||||
let left = self.left_operand().comptime_eval(scope, handler)?;
|
||||
let right = self.right_operand().comptime_eval(scope, handler)?;
|
||||
|
||||
match (left, right) {
|
||||
(ComptimeValue::Boolean(true), _) | (_, ComptimeValue::Boolean(true)) if matches!(self.operator(), BinaryOperator::LogicalOr(..))
|
||||
|
@ -426,7 +522,7 @@ impl Transpiler {
|
|||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn transpile_primary_expression(
|
||||
pub(super) fn transpile_primary_expression(
|
||||
&mut self,
|
||||
primary: &Primary,
|
||||
target: &DataLocation,
|
||||
|
@ -442,31 +538,39 @@ impl Transpiler {
|
|||
),
|
||||
Primary::FunctionCall(func) => match target {
|
||||
DataLocation::ScoreboardValue { objective, target } => {
|
||||
let call_cmd = self.transpile_function_call(func, scope, handler)?;
|
||||
Ok(vec![Command::Execute(Execute::Store(
|
||||
format!("result score {target} {objective}").into(),
|
||||
Box::new(Execute::Run(Box::new(call_cmd))),
|
||||
))])
|
||||
let mut cmds = self.transpile_function_call(func, scope, handler)?;
|
||||
if let Some(call_cmd) = cmds.pop() {
|
||||
let modified = Command::Execute(Execute::Store(
|
||||
format!("result score {target} {objective}").into(),
|
||||
Box::new(Execute::Run(Box::new(call_cmd))),
|
||||
));
|
||||
cmds.push(modified);
|
||||
}
|
||||
Ok(cmds)
|
||||
}
|
||||
DataLocation::Storage {
|
||||
storage_name,
|
||||
path,
|
||||
r#type,
|
||||
} => {
|
||||
let call_cmd = self.transpile_function_call(func, scope, handler)?;
|
||||
let result_success = if matches!(r#type, StorageType::Boolean) {
|
||||
"success"
|
||||
} else {
|
||||
"result"
|
||||
};
|
||||
Ok(vec![Command::Execute(Execute::Store(
|
||||
format!(
|
||||
"{result_success} storage {storage_name} {path} {type} 1.0d",
|
||||
r#type = r#type.as_str()
|
||||
)
|
||||
.into(),
|
||||
Box::new(Execute::Run(Box::new(call_cmd))),
|
||||
))])
|
||||
let mut cmds = self.transpile_function_call(func, scope, handler)?;
|
||||
if let Some(call_cmd) = cmds.pop() {
|
||||
let result_success = if matches!(r#type, StorageType::Boolean) {
|
||||
"success"
|
||||
} else {
|
||||
"result"
|
||||
};
|
||||
let modified = Command::Execute(Execute::Store(
|
||||
format!(
|
||||
"{result_success} storage {storage_name} {path} {type} 1.0d",
|
||||
r#type = r#type.as_str()
|
||||
)
|
||||
.into(),
|
||||
Box::new(Execute::Run(Box::new(call_cmd))),
|
||||
));
|
||||
cmds.push(modified);
|
||||
}
|
||||
Ok(cmds)
|
||||
}
|
||||
DataLocation::Tag { tag_name, entity } => {
|
||||
if func
|
||||
|
@ -511,11 +615,17 @@ impl Transpiler {
|
|||
Primary::Parenthesized(parenthesized) => {
|
||||
self.transpile_expression(parenthesized.expression(), target, scope, handler)
|
||||
}
|
||||
Primary::Lua(lua) => {
|
||||
Primary::Lua(lua) =>
|
||||
{
|
||||
#[expect(clippy::option_if_let_else)]
|
||||
if let Some(value) = lua.eval_comptime(handler)? {
|
||||
self.store_comptime_value(&value, target, lua, handler)
|
||||
} else {
|
||||
todo!("handle no return value from lua")
|
||||
let err = TranspileError::MissingValue(MissingValue {
|
||||
expression: lua.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
Primary::StringLiteral(_) | Primary::MacroStringLiteral(_) => {
|
||||
|
@ -536,7 +646,7 @@ impl Transpiler {
|
|||
Ok(cmds)
|
||||
} else {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: target.value_type(),
|
||||
expected_type: target.value_type().into(),
|
||||
expression: primary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -601,7 +711,7 @@ impl Transpiler {
|
|||
_ => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: prefix.span(),
|
||||
expected_type: ValueType::Integer,
|
||||
expected_type: ExpectedType::Integer,
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
|
@ -665,7 +775,7 @@ impl Transpiler {
|
|||
} else {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: primary.span(),
|
||||
expected_type: target.value_type(),
|
||||
expected_type: target.value_type().into(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
|
@ -697,16 +807,21 @@ impl Transpiler {
|
|||
| StorageType::Int
|
||||
| StorageType::Long
|
||||
) {
|
||||
let cmd = Command::Raw(format!(
|
||||
"data modify storage {storage_name} {path} set value {value}{suffix}",
|
||||
value = score_target,
|
||||
suffix = r#type.suffix()
|
||||
let cmd = Command::Execute(Execute::Store(
|
||||
format!(
|
||||
"result storage {storage_name} {path} {t} 1.0",
|
||||
t = r#type.as_str()
|
||||
)
|
||||
.into(),
|
||||
Box::new(Execute::Run(Box::new(Command::Raw(format!(
|
||||
"scoreboard players get {score_target} {objective}"
|
||||
))))),
|
||||
));
|
||||
Ok(vec![cmd])
|
||||
} else {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: primary.span(),
|
||||
expected_type: target.value_type(),
|
||||
expected_type: target.value_type().into(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
|
@ -714,7 +829,7 @@ impl Transpiler {
|
|||
}
|
||||
DataLocation::Tag { .. } => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: primary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -723,7 +838,7 @@ impl Transpiler {
|
|||
},
|
||||
_ => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: target.value_type(),
|
||||
expected_type: target.value_type().into(),
|
||||
expression: primary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -748,7 +863,7 @@ impl Transpiler {
|
|||
scope: &Arc<super::Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Vec<Command>> {
|
||||
if let Some(value) = binary.comptime_eval(scope) {
|
||||
if let Some(value) = binary.comptime_eval(scope, handler) {
|
||||
self.transpile_comptime_value(&value, binary, target, scope, handler)
|
||||
} else {
|
||||
match binary.operator() {
|
||||
|
@ -808,7 +923,7 @@ impl Transpiler {
|
|||
}
|
||||
Primary::Integer(_) => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: primary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -874,7 +989,7 @@ impl Transpiler {
|
|||
)),
|
||||
_ => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: primary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -911,28 +1026,34 @@ impl Transpiler {
|
|||
}
|
||||
PrefixOperator::Negate(_) => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: primary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
Primary::Lua(lua) => {
|
||||
match lua.eval_comptime(handler)? {
|
||||
Some(ComptimeValue::String(value) | ComptimeValue::MacroString(value)) => {
|
||||
// TODO: mark condition as containing macro if so
|
||||
Ok((
|
||||
Vec::new(),
|
||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||
))
|
||||
}
|
||||
Some(ComptimeValue::Boolean(boolean)) => {
|
||||
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
|
||||
}
|
||||
_ => todo!("invalid or none lua return value"),
|
||||
Primary::Lua(lua) => match lua.eval_comptime(handler)? {
|
||||
Some(ComptimeValue::String(value)) => Ok((
|
||||
Vec::new(),
|
||||
ExtendedCondition::Runtime(Condition::Atom(value.into())),
|
||||
)),
|
||||
Some(ComptimeValue::MacroString(value)) => Ok((
|
||||
Vec::new(),
|
||||
ExtendedCondition::Runtime(Condition::Atom(value)),
|
||||
)),
|
||||
Some(ComptimeValue::Boolean(boolean)) => {
|
||||
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: primary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -956,7 +1077,7 @@ impl Transpiler {
|
|||
}
|
||||
_ => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: binary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -1025,7 +1146,7 @@ impl Transpiler {
|
|||
DataLocation::ScoreboardValue { .. } => None,
|
||||
DataLocation::Tag { .. } => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: binary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -1052,7 +1173,7 @@ impl Transpiler {
|
|||
}
|
||||
StorageType::Boolean => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: binary.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -1201,7 +1322,7 @@ impl Transpiler {
|
|||
)]),
|
||||
DataLocation::Tag { .. } => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: original.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -1226,7 +1347,7 @@ impl Transpiler {
|
|||
} else {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: original.span(),
|
||||
expected_type: target.value_type(),
|
||||
expected_type: target.value_type().into(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
|
@ -1258,7 +1379,7 @@ impl Transpiler {
|
|||
} else {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: original.span(),
|
||||
expected_type: target.value_type(),
|
||||
expected_type: target.value_type().into(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
|
@ -1267,7 +1388,7 @@ impl Transpiler {
|
|||
},
|
||||
ComptimeValue::String(_) | ComptimeValue::MacroString(_) => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: target.value_type(),
|
||||
expected_type: target.value_type().into(),
|
||||
expression: original.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -1276,6 +1397,7 @@ impl Transpiler {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_ref_mut)]
|
||||
fn store_comptime_value(
|
||||
&mut self,
|
||||
value: &ComptimeValue,
|
||||
|
@ -1285,17 +1407,12 @@ impl Transpiler {
|
|||
) -> TranspileResult<Vec<Command>> {
|
||||
match value {
|
||||
ComptimeValue::Integer(int) => match target {
|
||||
DataLocation::ScoreboardValue { objective, target } => {
|
||||
Ok(vec![Command::Raw(format!(
|
||||
"scoreboard players set {target} {objective} {value}",
|
||||
target = target,
|
||||
objective = objective,
|
||||
value = int
|
||||
))])
|
||||
}
|
||||
DataLocation::ScoreboardValue { objective, target } => Ok(vec![Command::Raw(
|
||||
format!("scoreboard players set {target} {objective} {int}"),
|
||||
)]),
|
||||
DataLocation::Tag { .. } => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: source.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -1323,7 +1440,7 @@ impl Transpiler {
|
|||
} else {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: source.span(),
|
||||
expected_type: ValueType::Integer,
|
||||
expected_type: ExpectedType::Integer,
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
|
@ -1370,6 +1487,7 @@ impl Transpiler {
|
|||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::unused_self, clippy::needless_pass_by_ref_mut)]
|
||||
fn store_condition_success(
|
||||
&mut self,
|
||||
cond: ExtendedCondition,
|
||||
|
@ -1394,7 +1512,7 @@ impl Transpiler {
|
|||
)
|
||||
} else {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::Boolean,
|
||||
expected_type: ExpectedType::Boolean,
|
||||
expression: source.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -1426,7 +1544,7 @@ impl Transpiler {
|
|||
}
|
||||
|
||||
/// Get temporary scoreboard locations.
|
||||
fn get_temp_scoreboard_locations(&mut self, amount: usize) -> (String, Vec<String>) {
|
||||
pub(super) fn get_temp_scoreboard_locations(&mut self, amount: usize) -> (String, Vec<String>) {
|
||||
let objective = "shu_temp_".to_string()
|
||||
+ &chksum_md5::hash(&self.main_namespace_name).to_hex_lowercase();
|
||||
|
||||
|
@ -1449,4 +1567,26 @@ impl Transpiler {
|
|||
|
||||
(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()
|
||||
+ &chksum_md5::hash(&self.main_namespace_name).to_hex_lowercase();
|
||||
|
||||
let paths = (0..amount)
|
||||
.map(|i| {
|
||||
chksum_md5::hash(format!(
|
||||
"{namespace}\0{j}",
|
||||
namespace = self.main_namespace_name,
|
||||
j = i + self.temp_counter
|
||||
))
|
||||
.to_hex_lowercase()
|
||||
.split_off(16)
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.temp_counter = self.temp_counter.wrapping_add(amount);
|
||||
|
||||
(storage_name, paths)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,10 @@ mod enabled {
|
|||
) -> TranspileResult<Option<ComptimeValue>> {
|
||||
let lua_result = self.eval(handler)?;
|
||||
|
||||
self.handle_lua_result(lua_result).inspect_err(|err| {
|
||||
handler.receive(err.clone());
|
||||
})
|
||||
self.handle_lua_result(lua_result, handler)
|
||||
.inspect_err(|err| {
|
||||
handler.receive(err.clone());
|
||||
})
|
||||
}
|
||||
|
||||
fn add_globals(&self, lua: &Lua) -> mlua::Result<()> {
|
||||
|
@ -88,19 +89,26 @@ mod enabled {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_lua_result(&self, value: Value) -> TranspileResult<Option<ComptimeValue>> {
|
||||
fn handle_lua_result(
|
||||
&self,
|
||||
value: Value,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Option<ComptimeValue>> {
|
||||
match value {
|
||||
Value::Nil => Ok(None),
|
||||
Value::String(s) => Ok(Some(ComptimeValue::String(s.to_string_lossy()))),
|
||||
Value::Integer(i) => Ok(Some(ComptimeValue::Integer(i))),
|
||||
// TODO: change when floating point comptime numbers are supported
|
||||
Value::Number(n) => Ok(Some(ComptimeValue::String(n.to_string()))),
|
||||
Value::Function(f) => self.handle_lua_result(f.call(()).map_err(|err| {
|
||||
TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err(
|
||||
&err,
|
||||
self.span(),
|
||||
))
|
||||
})?),
|
||||
Value::Function(f) => self.handle_lua_result(
|
||||
f.call(()).map_err(|err| {
|
||||
TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err(
|
||||
&err,
|
||||
self.span(),
|
||||
))
|
||||
})?,
|
||||
handler,
|
||||
),
|
||||
Value::Boolean(boolean) => Ok(Some(ComptimeValue::Boolean(boolean))),
|
||||
Value::Error(_)
|
||||
| Value::Table(_)
|
||||
|
@ -112,7 +120,8 @@ mod enabled {
|
|||
code_block: self.span(),
|
||||
error_message: format!("invalid return type {}", value.type_name()),
|
||||
});
|
||||
todo!("pass error to handler: {err}")
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +136,7 @@ mod disabled {
|
|||
transpile::error::{TranspileError, TranspileResult},
|
||||
};
|
||||
|
||||
use super::expression::ComptimeValue;
|
||||
use crate::transpile::expression::ComptimeValue;
|
||||
|
||||
impl LuaCode {
|
||||
/// Will always return an error because Lua code evaluation is disabled.
|
||||
|
@ -136,7 +145,7 @@ mod disabled {
|
|||
/// # Errors
|
||||
/// - Always, as the lua feature is disabled
|
||||
#[tracing::instrument(level = "debug", name = "eval_lua", skip_all, ret)]
|
||||
pub fn eval(&self, handler: &impl Handler<base::Error>) -> TranspileResult<mlua::Value> {
|
||||
pub fn eval(&self, handler: &impl Handler<base::Error>) -> TranspileResult<()> {
|
||||
handler.receive(TranspileError::LuaDisabled);
|
||||
tracing::error!("Lua code evaluation is disabled");
|
||||
Err(TranspileError::LuaDisabled)
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
//! Transpiler for `Shulkerscript`
|
||||
|
||||
use chksum_md5 as md5;
|
||||
use enum_as_inner::EnumAsInner;
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
ops::Deref,
|
||||
sync::{Arc, OnceLock},
|
||||
};
|
||||
|
||||
use shulkerbox::datapack::{self, Command, Datapack, Execute};
|
||||
use shulkerbox::{
|
||||
datapack::{self, Command, Datapack, Execute},
|
||||
util::{MacroString, MacroStringPart},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
base::{
|
||||
|
@ -30,8 +34,8 @@ use crate::{
|
|||
};
|
||||
|
||||
use super::{
|
||||
error::{MismatchedTypes, TranspileError, TranspileResult},
|
||||
expression::{ComptimeValue, ExtendedCondition, ValueType},
|
||||
error::{MismatchedTypes, TranspileError, TranspileResult, UnknownIdentifier},
|
||||
expression::{ComptimeValue, ExpectedType, ExtendedCondition, StorageType},
|
||||
variables::{Scope, VariableData},
|
||||
FunctionData, TranspileAnnotationValue,
|
||||
};
|
||||
|
@ -52,6 +56,13 @@ pub struct Transpiler {
|
|||
aliases: HashMap<(String, String), (String, String)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TranspiledFunctionArguments {
|
||||
None,
|
||||
Static(BTreeMap<String, MacroString>),
|
||||
Dynamic(Vec<Command>),
|
||||
}
|
||||
|
||||
impl Transpiler {
|
||||
/// Creates a new transpiler.
|
||||
#[must_use]
|
||||
|
@ -265,22 +276,20 @@ impl Transpiler {
|
|||
arguments: Option<&[&Expression]>,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<(String, Option<BTreeMap<String, String>>)> {
|
||||
) -> TranspileResult<(String, TranspiledFunctionArguments)> {
|
||||
let program_identifier = identifier_span.source_file().identifier();
|
||||
let program_query = (
|
||||
program_identifier.to_string(),
|
||||
identifier_span.str().to_string(),
|
||||
);
|
||||
let alias_query = self.aliases.get(&program_query).cloned();
|
||||
let already_transpiled = match scope
|
||||
let already_transpiled = scope
|
||||
.get_variable(identifier_span.str())
|
||||
.expect("called function should be in scope")
|
||||
.as_ref()
|
||||
{
|
||||
VariableData::Function { path, .. } => Some(path.get().is_some()),
|
||||
_ => None,
|
||||
}
|
||||
.expect("called variable should be of type function");
|
||||
.as_function()
|
||||
.map(|(_, path)| path.get().is_some())
|
||||
.expect("called variable should be of type function");
|
||||
|
||||
let function_data = scope
|
||||
.get_variable(identifier_span.str())
|
||||
|
@ -331,7 +340,7 @@ impl Transpiler {
|
|||
|val| match val {
|
||||
TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
|
||||
TranspileAnnotationValue::Expression(expr) => expr
|
||||
.comptime_eval(scope)
|
||||
.comptime_eval(scope, handler)
|
||||
.and_then(|val| val.to_string_no_macro())
|
||||
.ok_or_else(|| {
|
||||
let err = TranspileError::IllegalAnnotationContent(
|
||||
|
@ -385,7 +394,7 @@ impl Transpiler {
|
|||
function_path.set(function_location.clone()).unwrap();
|
||||
}
|
||||
|
||||
let parameters = function_data.parameters.clone();
|
||||
let parameters = &function_data.parameters;
|
||||
|
||||
let function_location = function_path
|
||||
.get()
|
||||
|
@ -402,71 +411,185 @@ impl Transpiler {
|
|||
if arg_count.is_some_and(|arg_count| arg_count != parameters.len()) {
|
||||
let err = TranspileError::InvalidFunctionArguments(InvalidFunctionArguments {
|
||||
expected: parameters.len(),
|
||||
actual: arg_count.expect("checked in if condition"),
|
||||
actual: arg_count.unwrap_or_default(),
|
||||
span: identifier_span.clone(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
} else if arg_count.is_some_and(|arg_count| arg_count > 0) {
|
||||
let mut compiled_args = Vec::new();
|
||||
let mut errs = Vec::new();
|
||||
for expression in arguments.iter().flat_map(|x| x.iter()) {
|
||||
let value = match expression {
|
||||
Expression::Primary(Primary::FunctionCall(func)) => self
|
||||
.transpile_function_call(func, scope, handler)
|
||||
.map(|cmd| match cmd {
|
||||
Command::Raw(s) => s,
|
||||
_ => unreachable!("Function call should always return a raw command"),
|
||||
}),
|
||||
Expression::Primary(Primary::Lua(lua)) => {
|
||||
lua.eval_comptime(handler).and_then(|opt| {
|
||||
opt.map_or_else(
|
||||
|| {
|
||||
{
|
||||
#[derive(Debug, Clone, EnumAsInner)]
|
||||
enum Parameter {
|
||||
Static(MacroString),
|
||||
Dynamic {
|
||||
prepare_cmds: Vec<Command>,
|
||||
storage_name: String,
|
||||
path: String,
|
||||
},
|
||||
}
|
||||
|
||||
let mut compiled_args = Vec::new();
|
||||
let mut errs = Vec::new();
|
||||
|
||||
for expression in arguments.iter().flat_map(|x| x.iter()) {
|
||||
let value = match expression {
|
||||
Expression::Primary(Primary::Lua(lua)) => {
|
||||
lua.eval_comptime(handler).and_then(|val| match val {
|
||||
Some(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
|
||||
Some(val) => Ok(Parameter::Static(val.to_string().into())),
|
||||
None => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expression: expression.span(),
|
||||
expected_type: ValueType::String,
|
||||
expected_type: ExpectedType::String,
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
Err(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
Expression::Primary(Primary::Integer(num)) => {
|
||||
Ok(Parameter::Static(num.span.str().to_string().into()))
|
||||
}
|
||||
Expression::Primary(Primary::Boolean(bool)) => {
|
||||
Ok(Parameter::Static(bool.span.str().to_string().into()))
|
||||
}
|
||||
Expression::Primary(Primary::StringLiteral(string)) => {
|
||||
Ok(Parameter::Static(string.str_content().to_string().into()))
|
||||
}
|
||||
Expression::Primary(Primary::MacroStringLiteral(literal)) => {
|
||||
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 {
|
||||
identifier: ident.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
err
|
||||
})?;
|
||||
match var.as_ref() {
|
||||
VariableData::FunctionArgument { .. } => {
|
||||
Ok(Parameter::Static(MacroString::MacroString(vec![
|
||||
MacroStringPart::MacroUsage(
|
||||
crate::util::identifier_to_macro(ident.span.str())
|
||||
.to_string(),
|
||||
),
|
||||
])))
|
||||
}
|
||||
|
||||
VariableData::BooleanStorage { .. }
|
||||
| VariableData::ScoreboardValue { .. } => {
|
||||
let (temp_storage, mut temp_path) =
|
||||
self.get_temp_storage_locations(1);
|
||||
let prepare_cmds = self.transpile_primary_expression(
|
||||
primary,
|
||||
&super::expression::DataLocation::Storage {
|
||||
storage_name: temp_storage.clone(),
|
||||
path: temp_path[0].clone(),
|
||||
r#type: StorageType::Int,
|
||||
},
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Ok(Parameter::Dynamic {
|
||||
prepare_cmds: dbg!(prepare_cmds),
|
||||
storage_name: temp_storage,
|
||||
path: std::mem::take(&mut temp_path[0]),
|
||||
})
|
||||
}
|
||||
_ => todo!("other variable types"),
|
||||
}
|
||||
}
|
||||
Expression::Primary(
|
||||
Primary::Parenthesized(_)
|
||||
| Primary::Prefix(_)
|
||||
| Primary::FunctionCall(_),
|
||||
)
|
||||
| Expression::Binary(_) => {
|
||||
let (temp_storage, mut temp_path) = self.get_temp_storage_locations(1);
|
||||
let prepare_cmds = self.transpile_expression(
|
||||
expression,
|
||||
&super::expression::DataLocation::Storage {
|
||||
storage_name: temp_storage.clone(),
|
||||
path: temp_path[0].clone(),
|
||||
r#type: StorageType::Int,
|
||||
},
|
||||
|val| Ok(val.to_string()),
|
||||
)
|
||||
})
|
||||
}
|
||||
Expression::Primary(Primary::Integer(num)) => Ok(num.span.str().to_string()),
|
||||
Expression::Primary(Primary::Boolean(bool)) => Ok(bool.span.str().to_string()),
|
||||
Expression::Primary(Primary::StringLiteral(string)) => {
|
||||
Ok(string.str_content().to_string())
|
||||
}
|
||||
Expression::Primary(Primary::MacroStringLiteral(literal)) => {
|
||||
Ok(literal.str_content())
|
||||
}
|
||||
Expression::Primary(
|
||||
Primary::Identifier(_) | Primary::Parenthesized(_) | Primary::Prefix(_),
|
||||
) => {
|
||||
todo!("allow identifiers, parenthesized & prefix expressions as arguments")
|
||||
}
|
||||
scope,
|
||||
handler,
|
||||
)?;
|
||||
|
||||
Expression::Binary(_) => todo!("allow binary expressions as arguments"),
|
||||
};
|
||||
Ok(Parameter::Dynamic {
|
||||
prepare_cmds,
|
||||
storage_name: temp_storage,
|
||||
path: std::mem::take(&mut temp_path[0]),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
match value {
|
||||
Ok(value) => {
|
||||
compiled_args.push(value);
|
||||
}
|
||||
Err(err) => {
|
||||
compiled_args.push(String::new());
|
||||
errs.push(err.clone());
|
||||
match value {
|
||||
Ok(value) => {
|
||||
compiled_args.push(value);
|
||||
}
|
||||
Err(err) => {
|
||||
compiled_args
|
||||
.push(Parameter::Static(MacroString::String(String::new())));
|
||||
errs.push(err.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(err) = errs.first() {
|
||||
return Err(err.clone());
|
||||
}
|
||||
if compiled_args.iter().any(|arg| !arg.is_static()) {
|
||||
let (mut setup_cmds, move_cmds) = parameters.clone().into_iter().zip(compiled_args).fold(
|
||||
(Vec::new(), Vec::new()),
|
||||
|(mut acc_setup, mut acc_move), (arg_name, data)| {
|
||||
let arg_name = crate::util::identifier_to_macro(&arg_name);
|
||||
match data {
|
||||
Parameter::Static(s) => {
|
||||
// TODO: optimize by combining into single `data merge` command
|
||||
let move_cmd = match s {
|
||||
MacroString::String(value) => Command::Raw(format!(r#"data modify storage shulkerscript:function_arguments {arg_name} set value "{value}""#, value = crate::util::escape_str(&value))),
|
||||
MacroString::MacroString(mut parts) => {
|
||||
parts.insert(0, MacroStringPart::String(format!(r#"data modify storage shulkerscript:function_arguments {arg_name} set value ""#)));
|
||||
parts.push(MacroStringPart::String('"'.to_string()));
|
||||
Command::UsesMacro(MacroString::MacroString(parts))
|
||||
}
|
||||
};
|
||||
acc_move.push(move_cmd);
|
||||
}
|
||||
Parameter::Dynamic { prepare_cmds, storage_name, path } => {
|
||||
acc_setup.extend(prepare_cmds);
|
||||
acc_move.push(Command::Raw(format!(r#"data modify storage shulkerscript:function_arguments {arg_name} set from storage {storage_name} {path}"#)));
|
||||
}
|
||||
}
|
||||
(acc_setup, acc_move)},
|
||||
);
|
||||
setup_cmds.extend(move_cmds);
|
||||
|
||||
Ok((
|
||||
function_location,
|
||||
TranspiledFunctionArguments::Dynamic(setup_cmds),
|
||||
))
|
||||
} else {
|
||||
let function_args = parameters
|
||||
.clone()
|
||||
.into_iter()
|
||||
.zip(
|
||||
compiled_args
|
||||
.into_iter()
|
||||
.map(|arg| arg.into_static().expect("checked in if condition")),
|
||||
)
|
||||
.collect();
|
||||
Ok((
|
||||
function_location,
|
||||
TranspiledFunctionArguments::Static(function_args),
|
||||
))
|
||||
}
|
||||
}
|
||||
if let Some(err) = errs.first() {
|
||||
return Err(err.clone());
|
||||
}
|
||||
let function_args = parameters.into_iter().zip(compiled_args).collect();
|
||||
Ok((function_location, Some(function_args)))
|
||||
} else {
|
||||
Ok((function_location, None))
|
||||
Ok((function_location, TranspiledFunctionArguments::None))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,9 +679,9 @@ impl Transpiler {
|
|||
}
|
||||
Statement::Semicolon(semi) => match semi.statement() {
|
||||
SemicolonStatement::Expression(expr) => match expr {
|
||||
Expression::Primary(Primary::FunctionCall(func)) => self
|
||||
.transpile_function_call(func, scope, handler)
|
||||
.map(|cmd| vec![cmd]),
|
||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
self.transpile_function_call(func, scope, handler)
|
||||
}
|
||||
unexpected => {
|
||||
let error = TranspileError::UnexpectedExpression(UnexpectedExpression(
|
||||
unexpected.clone(),
|
||||
|
@ -589,9 +712,9 @@ impl Transpiler {
|
|||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Vec<Command>> {
|
||||
match expression {
|
||||
Expression::Primary(Primary::FunctionCall(func)) => self
|
||||
.transpile_function_call(func, scope, handler)
|
||||
.map(|cmd| vec![cmd]),
|
||||
Expression::Primary(Primary::FunctionCall(func)) => {
|
||||
self.transpile_function_call(func, scope, handler)
|
||||
}
|
||||
expression @ Expression::Primary(
|
||||
Primary::Integer(_)
|
||||
| Primary::Boolean(_)
|
||||
|
@ -610,13 +733,11 @@ impl Transpiler {
|
|||
Ok(vec![Command::UsesMacro(string.into())])
|
||||
}
|
||||
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(handler)? {
|
||||
Some(ComptimeValue::String(cmd) | ComptimeValue::MacroString(cmd)) => {
|
||||
// TODO: mark command as containing macro if so
|
||||
Ok(vec![Command::Raw(cmd)])
|
||||
}
|
||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd)]),
|
||||
Some(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
|
||||
let err = TranspileError::MismatchedTypes(MismatchedTypes {
|
||||
expected_type: ValueType::String,
|
||||
expected_type: ExpectedType::String,
|
||||
expression: code.span(),
|
||||
});
|
||||
handler.receive(err.clone());
|
||||
|
@ -632,16 +753,11 @@ impl Transpiler {
|
|||
scope,
|
||||
handler,
|
||||
),
|
||||
Expression::Binary(bin) => {
|
||||
if let Some(ComptimeValue::String(cmd) | ComptimeValue::MacroString(cmd)) =
|
||||
bin.comptime_eval(scope)
|
||||
{
|
||||
// TODO: mark as containing macro if so
|
||||
Ok(vec![Command::Raw(cmd)])
|
||||
} else {
|
||||
todo!("run binary expression")
|
||||
}
|
||||
}
|
||||
Expression::Binary(bin) => match bin.comptime_eval(scope, handler) {
|
||||
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
|
||||
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd)]),
|
||||
_ => todo!("run binary expression"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -650,7 +766,7 @@ impl Transpiler {
|
|||
func: &FunctionCall,
|
||||
scope: &Arc<Scope>,
|
||||
handler: &impl Handler<base::Error>,
|
||||
) -> TranspileResult<Command> {
|
||||
) -> TranspileResult<Vec<Command>> {
|
||||
let arguments = func
|
||||
.arguments()
|
||||
.as_ref()
|
||||
|
@ -662,22 +778,78 @@ impl Transpiler {
|
|||
handler,
|
||||
)?;
|
||||
let mut function_call = format!("function {location}");
|
||||
if let Some(arguments) = arguments {
|
||||
use std::fmt::Write;
|
||||
let arguments = arguments
|
||||
.iter()
|
||||
.map(|(ident, v)| {
|
||||
format!(
|
||||
r#"{macro_name}:"{escaped}""#,
|
||||
macro_name = crate::util::identifier_to_macro(ident),
|
||||
escaped = crate::util::escape_str(v)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
write!(function_call, " {{{arguments}}}").unwrap();
|
||||
match arguments {
|
||||
TranspiledFunctionArguments::Static(arguments) => {
|
||||
use std::fmt::Write;
|
||||
let arguments = arguments
|
||||
.iter()
|
||||
.map(|(ident, v)| match v {
|
||||
MacroString::String(s) => MacroString::String(format!(
|
||||
r#"{macro_name}:"{escaped}""#,
|
||||
macro_name = crate::util::identifier_to_macro(ident),
|
||||
escaped = crate::util::escape_str(s)
|
||||
)),
|
||||
MacroString::MacroString(parts) => MacroString::MacroString(
|
||||
std::iter::once(MacroStringPart::String(format!(
|
||||
r#"{macro_name}:""#,
|
||||
macro_name = crate::util::identifier_to_macro(ident)
|
||||
)))
|
||||
.chain(parts.clone().into_iter().map(|part| match part {
|
||||
MacroStringPart::String(s) => {
|
||||
MacroStringPart::String(crate::util::escape_str(&s).to_string())
|
||||
}
|
||||
macro_usage @ MacroStringPart::MacroUsage(_) => macro_usage,
|
||||
}))
|
||||
.chain(std::iter::once(MacroStringPart::String('"'.to_string())))
|
||||
.collect(),
|
||||
),
|
||||
})
|
||||
.fold(MacroString::String(String::new()), |acc, cur| match acc {
|
||||
MacroString::String(mut s) => match cur {
|
||||
MacroString::String(cur) => {
|
||||
s.push_str(&cur);
|
||||
MacroString::String(s)
|
||||
}
|
||||
MacroString::MacroString(cur) => {
|
||||
let mut parts = vec![MacroStringPart::String(s)];
|
||||
parts.extend(cur);
|
||||
MacroString::MacroString(parts)
|
||||
}
|
||||
},
|
||||
MacroString::MacroString(mut parts) => match cur {
|
||||
MacroString::String(cur) => {
|
||||
parts.push(MacroStringPart::String(cur));
|
||||
MacroString::MacroString(parts)
|
||||
}
|
||||
MacroString::MacroString(cur) => {
|
||||
parts.extend(cur);
|
||||
MacroString::MacroString(parts)
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
let cmd = match arguments {
|
||||
MacroString::String(arguments) => {
|
||||
write!(function_call, " {{{arguments}}}").unwrap();
|
||||
Command::Raw(function_call)
|
||||
}
|
||||
MacroString::MacroString(mut parts) => {
|
||||
function_call.push_str(" {");
|
||||
parts.insert(0, MacroStringPart::String(function_call));
|
||||
parts.push(MacroStringPart::String('}'.to_string()));
|
||||
Command::UsesMacro(MacroString::MacroString(parts))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(vec![cmd])
|
||||
}
|
||||
TranspiledFunctionArguments::Dynamic(mut cmds) => {
|
||||
function_call.push_str(" with storage shulkerscript:function_arguments");
|
||||
cmds.push(Command::Raw(function_call));
|
||||
Ok(cmds)
|
||||
}
|
||||
TranspiledFunctionArguments::None => Ok(vec![Command::Raw(function_call)]),
|
||||
}
|
||||
Ok(Command::Raw(function_call))
|
||||
}
|
||||
|
||||
fn transpile_execute_block(
|
||||
|
@ -830,7 +1002,7 @@ impl Transpiler {
|
|||
}
|
||||
});
|
||||
|
||||
if let Some(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope) {
|
||||
if let Some(ComptimeValue::Boolean(value)) = cond_expression.comptime_eval(scope, handler) {
|
||||
if value {
|
||||
Ok(Some((Vec::new(), then)))
|
||||
} else {
|
||||
|
|
|
@ -380,8 +380,12 @@ fn get_single_data_location_identifiers(
|
|||
) = (name, target)
|
||||
{
|
||||
if let (Some(name_eval), Some(target_eval)) = (
|
||||
objective.comptime_eval(scope).map(|val| val.to_string()),
|
||||
target.comptime_eval(scope).map(|val| val.to_string()),
|
||||
objective
|
||||
.comptime_eval(scope, handler)
|
||||
.map(|val| val.to_string()),
|
||||
target
|
||||
.comptime_eval(scope, handler)
|
||||
.map(|val| val.to_string()),
|
||||
) {
|
||||
// TODO: change invalid criteria if boolean
|
||||
if !crate::util::is_valid_scoreboard_objective_name(&name_eval) {
|
||||
|
|
Loading…
Reference in New Issue