Compare commits

..

2 Commits

Author SHA1 Message Date
Moritz Hölting 7290806a2b fix compile error when compiling without shulkerbox feature 2025-03-14 13:20:24 +01:00
Moritz Hölting e4c06e56ee allow passing some variables into lua 2025-03-14 12:51:17 +01:00
6 changed files with 223 additions and 80 deletions

View File

@ -349,7 +349,7 @@ pub struct LuaCode {
left_parenthesis: Punctuation,
/// The arguments of the lua code.
#[get = "pub"]
variables: Option<ConnectedList<Identifier, Punctuation>>,
inputs: Option<ConnectedList<Identifier, Punctuation>>,
/// The right parenthesis of the lua code.
#[get = "pub"]
right_parenthesis: Punctuation,
@ -550,10 +550,7 @@ impl<'a> Parser<'a> {
Delimiter::Parenthesis,
',',
|parser| match parser.next_significant_token() {
Reading::Atomic(Token::Identifier(identifier)) => {
parser.forward();
Ok(identifier)
}
Reading::Atomic(Token::Identifier(identifier)) => Ok(identifier),
unexpected => {
let err = Error::UnexpectedSyntax(UnexpectedSyntax {
expected: syntax::error::SyntaxKind::Identifier,
@ -597,7 +594,7 @@ impl<'a> Parser<'a> {
Ok(Primary::Lua(Box::new(LuaCode {
lua_keyword,
left_parenthesis: variables.open,
variables: variables.list,
inputs: variables.list,
right_parenthesis: variables.close,
left_brace: tree.open,
code: tree.tree?,

View File

@ -1,57 +1,50 @@
//! Conversion functions for converting between tokens/ast-nodes and [`shulkerbox`] types
use shulkerbox::util::{MacroString, MacroStringPart};
use shulkerbox::util::{MacroString as ExtMacroString, MacroStringPart as ExtMacroStringPart};
use crate::{
lexical::token::{MacroStringLiteral, MacroStringLiteralPart},
syntax::syntax_tree::AnyStringLiteral,
};
use crate::{lexical::token::MacroStringLiteral, syntax::syntax_tree::AnyStringLiteral};
impl From<&AnyStringLiteral> for MacroString {
fn from(value: &AnyStringLiteral) -> Self {
use super::util::{MacroString, MacroStringPart};
impl From<MacroString> for ExtMacroString {
fn from(value: MacroString) -> Self {
match value {
AnyStringLiteral::StringLiteral(literal) => Self::from(literal.str_content().as_ref()),
AnyStringLiteral::MacroStringLiteral(literal) => Self::from(literal),
MacroString::String(s) => Self::String(s),
MacroString::MacroString(parts) => {
Self::MacroString(parts.into_iter().map(ExtMacroStringPart::from).collect())
}
}
}
}
impl From<AnyStringLiteral> for MacroString {
impl From<MacroStringPart> for ExtMacroStringPart {
fn from(value: MacroStringPart) -> Self {
match value {
MacroStringPart::String(s) => Self::String(s),
MacroStringPart::MacroUsage(m) => Self::MacroUsage(m),
}
}
}
impl From<&AnyStringLiteral> for ExtMacroString {
fn from(value: &AnyStringLiteral) -> Self {
Self::from(MacroString::from(value))
}
}
impl From<AnyStringLiteral> for ExtMacroString {
fn from(value: AnyStringLiteral) -> Self {
Self::from(&value)
}
}
impl From<&MacroStringLiteral> for MacroString {
impl From<&MacroStringLiteral> for ExtMacroString {
fn from(value: &MacroStringLiteral) -> Self {
if value
.parts()
.iter()
.any(|p| matches!(p, MacroStringLiteralPart::MacroUsage { .. }))
{
Self::MacroString(
value
.parts()
.iter()
.map(|part| match part {
MacroStringLiteralPart::Text(span) => MacroStringPart::String(
crate::util::unescape_macro_string(span.str()).to_string(),
),
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
MacroStringPart::MacroUsage(
crate::util::identifier_to_macro(identifier.span.str()).to_string(),
)
}
})
.collect(),
)
} else {
Self::String(value.str_content())
}
Self::from(MacroString::from(value))
}
}
impl From<MacroStringLiteral> for MacroString {
impl From<MacroStringLiteral> for ExtMacroString {
fn from(value: MacroStringLiteral) -> Self {
Self::from(&value)
}

View File

@ -2,7 +2,7 @@
use std::{fmt::Display, sync::Arc};
use super::{Scope, VariableData};
use super::{util::MacroString, Scope, VariableData};
use crate::{
base::{self, Handler, VoidHandler},
lexical::token::MacroStringLiteralPart,
@ -15,10 +15,7 @@ use crate::{
use enum_as_inner::EnumAsInner;
#[cfg(feature = "shulkerbox")]
use shulkerbox::{
prelude::{Command, Condition, Execute},
util::MacroString,
};
use shulkerbox::prelude::{Command, Condition, Execute};
#[cfg(feature = "shulkerbox")]
use super::{
@ -51,7 +48,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.compile()),
Self::MacroString(macro_string) => write!(f, "{macro_string}"),
}
}
}
@ -296,7 +293,7 @@ impl Primary {
Self::Lua(lua) => {
cfg_if::cfg_if! {
if #[cfg(feature = "lua")] {
lua.eval(&VoidHandler).map_or(false, |value| match value {
lua.eval(scope, &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),
@ -345,7 +342,7 @@ impl Primary {
})
}
Self::Lua(lua) => lua
.eval_comptime(&VoidHandler)
.eval_comptime(scope, &VoidHandler)
.inspect_err(|err| {
handler.receive(err.clone());
})
@ -621,7 +618,7 @@ impl Transpiler {
Primary::Lua(lua) =>
{
#[expect(clippy::option_if_let_else)]
if let Some(value) = lua.eval_comptime(handler)? {
if let Some(value) = lua.eval_comptime(scope, handler)? {
self.store_comptime_value(&value, target, lua, handler)
} else {
let err = TranspileError::MissingValue(MissingValue {
@ -1033,14 +1030,14 @@ impl Transpiler {
Err(err)
}
},
Primary::Lua(lua) => match lua.eval_comptime(handler)? {
Primary::Lua(lua) => match lua.eval_comptime(scope, 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)),
ExtendedCondition::Runtime(Condition::Atom(value.into())),
)),
Some(ComptimeValue::Boolean(boolean)) => {
Ok((Vec::new(), ExtendedCondition::Comptime(boolean)))
@ -1397,7 +1394,7 @@ impl Transpiler {
..
}
| DataLocation::Tag { .. } => self.store_condition_success(
ExtendedCondition::Runtime(Condition::Atom(value.clone())),
ExtendedCondition::Runtime(Condition::Atom(value.clone().into())),
target,
source,
handler,

View File

@ -2,6 +2,8 @@
#[cfg(feature = "lua")]
mod enabled {
use std::sync::Arc;
use mlua::{Lua, Value};
use crate::{
@ -10,6 +12,7 @@ mod enabled {
transpile::{
error::{LuaRuntimeError, TranspileError, TranspileResult},
expression::ComptimeValue,
Scope, VariableData,
},
};
@ -19,7 +22,11 @@ mod enabled {
/// # Errors
/// - If evaluation fails
#[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,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<(mlua::Value, mlua::Lua)> {
tracing::debug!("Evaluating Lua code");
let lua = Lua::new();
@ -46,9 +53,17 @@ mod enabled {
)
};
self.add_globals(&lua).unwrap();
if let Err(err) = self.add_globals(&lua, scope) {
let err = TranspileError::LuaRuntimeError(LuaRuntimeError::from_lua_err(
&err,
self.span(),
));
handler.receive(crate::Error::from(err.clone()));
return Err(err);
}
lua.load(self.code())
let res = lua
.load(self.code())
.set_name(name)
.eval::<Value>()
.map_err(|err| {
@ -56,6 +71,11 @@ mod enabled {
TranspileError::from(LuaRuntimeError::from_lua_err(&err, self.span()));
handler.receive(crate::Error::from(err.clone()));
err
});
res.map(|v| {
tracing::debug!("Lua code evaluated successfully");
(v, lua)
})
}
@ -66,17 +86,16 @@ mod enabled {
#[tracing::instrument(level = "debug", name = "eval_lua", skip_all, ret)]
pub fn eval_comptime(
&self,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<ComptimeValue>> {
let lua_result = self.eval(handler)?;
// required to keep the lua instance alive
let (lua_result, _lua) = self.eval(scope, handler)?;
self.handle_lua_result(lua_result, handler)
.inspect_err(|err| {
handler.receive(err.clone());
})
}
fn add_globals(&self, lua: &Lua) -> mlua::Result<()> {
fn add_globals(&self, lua: &Lua, scope: &Arc<Scope>) -> mlua::Result<()> {
let globals = lua.globals();
let location = {
@ -86,6 +105,32 @@ mod enabled {
};
globals.set("shu_location", location.to_string_lossy())?;
if let Some(inputs) = self.inputs() {
for x in inputs.elements() {
let name = x.span.str();
let value = match scope.get_variable(name).as_deref() {
Some(VariableData::MacroParameter { macro_name, .. }) => {
Value::String(lua.create_string(format!("$({macro_name})"))?)
}
Some(VariableData::ScoreboardValue { objective, target }) => {
let table = lua.create_table()?;
table.set("objective", lua.create_string(objective)?)?;
table.set("target", lua.create_string(target)?)?;
Value::Table(table)
}
Some(VariableData::BooleanStorage { storage_name, path }) => {
let table = lua.create_table()?;
table.set("storage", lua.create_string(storage_name)?)?;
table.set("path", lua.create_string(path)?)?;
Value::Table(table)
}
Some(_) => todo!("allow other types"),
None => todo!("throw correct error"),
};
globals.set(name, value)?;
}
}
Ok(())
}
@ -130,13 +175,15 @@ mod enabled {
#[cfg(not(feature = "lua"))]
mod disabled {
use std::sync::Arc;
use crate::{
base::{self, Handler},
syntax::syntax_tree::expression::LuaCode,
transpile::error::{TranspileError, TranspileResult},
};
use crate::transpile::expression::ComptimeValue;
use crate::transpile::{expression::ComptimeValue, Scope};
impl LuaCode {
/// Will always return an error because Lua code evaluation is disabled.
@ -145,7 +192,12 @@ 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<()> {
pub fn eval(
&self,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<()> {
let _ = scope;
handler.receive(TranspileError::LuaDisabled);
tracing::error!("Lua code evaluation is disabled");
Err(TranspileError::LuaDisabled)
@ -158,8 +210,10 @@ mod disabled {
/// - If Lua code evaluation is disabled.
pub fn eval_comptime(
&self,
scope: &Arc<Scope>,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<ComptimeValue>> {
let _ = scope;
handler.receive(TranspileError::LuaDisabled);
tracing::error!("Lua code evaluation is disabled");
Err(TranspileError::LuaDisabled)

View File

@ -8,10 +8,7 @@ use std::{
sync::{Arc, OnceLock},
};
use shulkerbox::{
datapack::{self, Command, Datapack, Execute},
util::{MacroString, MacroStringPart},
};
use shulkerbox::datapack::{self, Command, Datapack, Execute};
use crate::{
base::{
@ -30,7 +27,10 @@ use crate::{
},
AnnotationAssignment,
},
transpile::error::{IllegalAnnotationContent, MissingFunctionDeclaration},
transpile::{
error::{IllegalAnnotationContent, MissingFunctionDeclaration},
util::{MacroString, MacroStringPart},
},
};
use super::{
@ -439,7 +439,7 @@ impl Transpiler {
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 {
lua.eval_comptime(scope, handler).and_then(|val| match val {
Some(ComptimeValue::MacroString(s)) => Ok(Parameter::Static(s)),
Some(val) => Ok(Parameter::Static(val.to_string().into())),
None => {
@ -604,16 +604,17 @@ impl Transpiler {
MacroString::String(s) => Command::Raw(format!(
r#"data merge storage shulkerscript:function_arguments {{{s}}}"#
)),
MacroString::MacroString(_) => {
Command::UsesMacro(super::util::join_macro_strings([
MacroString::MacroString(_) => Command::UsesMacro(
super::util::join_macro_strings([
MacroString::String(
"data merge storage shulkerscript:function_arguments {"
.to_string(),
),
joined_statics,
MacroString::String("}".to_string()),
]))
}
])
.into(),
),
};
setup_cmds.push(statics_cmd);
setup_cmds.extend(move_cmds);
@ -782,9 +783,9 @@ impl Transpiler {
Expression::Primary(Primary::MacroStringLiteral(string)) => {
Ok(vec![Command::UsesMacro(string.into())])
}
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(handler)? {
Expression::Primary(Primary::Lua(code)) => match code.eval_comptime(scope, handler)? {
Some(ComptimeValue::String(cmd)) => Ok(vec![Command::Raw(cmd)]),
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd)]),
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
Some(ComptimeValue::Boolean(_) | ComptimeValue::Integer(_)) => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ExpectedType::String,
@ -805,7 +806,7 @@ impl Transpiler {
),
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)]),
Some(ComptimeValue::MacroString(cmd)) => Ok(vec![Command::UsesMacro(cmd.into())]),
_ => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expression: bin.span(),
@ -870,7 +871,7 @@ impl Transpiler {
function_call.push_str(" {");
parts.insert(0, MacroStringPart::String(function_call));
parts.push(MacroStringPart::String('}'.to_string()));
Command::UsesMacro(MacroString::MacroString(parts))
Command::UsesMacro(MacroString::MacroString(parts).into())
}
};

View File

@ -1,6 +1,48 @@
//! Utility methods for transpiling
use shulkerbox::util::{MacroString, MacroStringPart};
use std::fmt::Display;
use crate::{
lexical::token::{MacroStringLiteral, MacroStringLiteralPart},
syntax::syntax_tree::AnyStringLiteral,
};
/// String that can contain macros
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum MacroString {
/// A normal string
String(String),
/// A string containing macros
MacroString(Vec<MacroStringPart>),
}
/// Part of a [`MacroString`]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum MacroStringPart {
/// A normal string
String(String),
/// A macro usage
MacroUsage(String),
}
impl Display for MacroString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(s) => s.fmt(f),
Self::MacroString(parts) => {
for part in parts {
match part {
MacroStringPart::String(s) => s.fmt(f)?,
MacroStringPart::MacroUsage(m) => write!(f, "$({m})")?,
}
}
Ok(())
}
}
}
}
fn normalize_program_identifier<S>(identifier: S) -> String
where
@ -71,3 +113,62 @@ where
},
})
}
impl<S> From<S> for MacroString
where
S: Into<String>,
{
fn from(value: S) -> Self {
Self::String(value.into())
}
}
impl From<&AnyStringLiteral> for MacroString {
fn from(value: &AnyStringLiteral) -> Self {
match value {
AnyStringLiteral::StringLiteral(literal) => Self::from(literal.str_content().as_ref()),
AnyStringLiteral::MacroStringLiteral(literal) => Self::from(literal),
}
}
}
impl From<AnyStringLiteral> for MacroString {
fn from(value: AnyStringLiteral) -> Self {
Self::from(&value)
}
}
impl From<&MacroStringLiteral> for MacroString {
fn from(value: &MacroStringLiteral) -> Self {
if value
.parts()
.iter()
.any(|p| matches!(p, MacroStringLiteralPart::MacroUsage { .. }))
{
Self::MacroString(
value
.parts()
.iter()
.map(|part| match part {
MacroStringLiteralPart::Text(span) => MacroStringPart::String(
crate::util::unescape_macro_string(span.str()).to_string(),
),
MacroStringLiteralPart::MacroUsage { identifier, .. } => {
MacroStringPart::MacroUsage(
crate::util::identifier_to_macro(identifier.span.str()).to_string(),
)
}
})
.collect(),
)
} else {
Self::String(value.str_content())
}
}
}
impl From<MacroStringLiteral> for MacroString {
fn from(value: MacroStringLiteral) -> Self {
Self::from(&value)
}
}