implement more missing functionality marked with todo!

This commit is contained in:
Moritz Hölting 2025-03-12 13:38:27 +01:00
parent f6dadf881a
commit 5740172ddb
5 changed files with 439 additions and 191 deletions

View File

@ -38,7 +38,7 @@ 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 }
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "c36c87c3db311cea0b373501c370c12edc6d051f", default-features = false, optional = true }
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "e9f2b9b91d72322ec2e063ce7b83415071306468", default-features = false, optional = true }
strsim = "0.11.1"
strum = { version = "0.27.0", features = ["derive"] }
thiserror = "2.0.11"

View File

@ -24,7 +24,6 @@ use super::{
#[cfg(feature = "shulkerbox")]
use crate::{
base::{self, source_file::SourceElement, Handler},
semantic::error::UnexpectedExpression,
transpile::{error::FunctionArgumentsNotAllowed, TranspileError},
};
@ -166,7 +165,7 @@ impl Expression {
pub fn can_yield_type(&self, r#type: ValueType, scope: &Arc<Scope>) -> bool {
match self {
Self::Primary(primary) => primary.can_yield_type(r#type, scope),
Self::Binary(_binary) => todo!(),
Self::Binary(binary) => binary.can_yield_type(r#type, scope),
}
}
@ -217,10 +216,21 @@ impl Primary {
&& prefix.operand().can_yield_type(r#type, scope)
}
},
// TODO: Add support for Lua.
#[expect(clippy::match_same_arms)]
Self::Lua(_) => false,
Self::StringLiteral(_) | Self::MacroStringLiteral(_) => false,
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,
}
} else {
false
}
}
Self::StringLiteral(_) | Self::MacroStringLiteral(_) => {
matches!(r#type, ValueType::String | ValueType::Boolean)
}
}
}
@ -246,12 +256,8 @@ impl Primary {
_ => None,
}
}),
// TODO: correctly evaluate lua code
Self::Lua(lua) => lua
.eval_string(&VoidHandler)
.ok()
.flatten()
.map(ComptimeValue::String),
// TODO: throw error
Self::Lua(lua) => lua.eval_comptime(&VoidHandler).ok().flatten(),
Self::MacroStringLiteral(macro_string_literal) => {
if macro_string_literal
.parts()
@ -270,6 +276,46 @@ impl Primary {
}
impl Binary {
/// Returns whether the binary can yield a certain type.
#[must_use]
pub fn can_yield_type(&self, r#type: ValueType, scope: &Arc<Scope>) -> bool {
match self.operator() {
BinaryOperator::Add(_) => {
matches!(r#type, ValueType::Integer | ValueType::String)
&& self.left_operand().can_yield_type(r#type, scope)
&& self.right_operand().can_yield_type(r#type, scope)
}
BinaryOperator::Subtract(_)
| BinaryOperator::Multiply(_)
| BinaryOperator::Divide(_)
| BinaryOperator::Modulo(_) => {
matches!(r#type, ValueType::Integer)
&& self.left_operand().can_yield_type(r#type, scope)
&& self.right_operand().can_yield_type(r#type, scope)
}
BinaryOperator::Equal(..) | BinaryOperator::NotEqual(..) => {
matches!(r#type, ValueType::Boolean)
}
BinaryOperator::GreaterThan(_)
| BinaryOperator::GreaterThanOrEqual(..)
| BinaryOperator::LessThan(_)
| BinaryOperator::LessThanOrEqual(..) => {
matches!(r#type, ValueType::Boolean)
&& self
.left_operand()
.can_yield_type(ValueType::Integer, scope)
&& self
.right_operand()
.can_yield_type(ValueType::Integer, scope)
}
BinaryOperator::LogicalAnd(..) | BinaryOperator::LogicalOr(..) => {
matches!(r#type, ValueType::Boolean)
&& self.left_operand().can_yield_type(r#type, scope)
&& self.right_operand().can_yield_type(r#type, scope)
}
}
}
/// Evaluate at compile-time.
#[must_use]
pub fn comptime_eval(&self, scope: &Arc<Scope>) -> Option<ComptimeValue> {
@ -388,40 +434,12 @@ impl Transpiler {
handler: &impl Handler<base::Error>,
) -> TranspileResult<Vec<Command>> {
match primary {
Primary::Boolean(boolean) => match target {
DataLocation::Tag { tag_name, entity } => {
let cmd = format!(
"tag {target} {op} {tag}",
target = entity,
op = if boolean.value() { "add" } else { "remove" },
tag = tag_name
);
Ok(vec![shulkerbox::prelude::Command::Raw(cmd)])
}
DataLocation::Storage {
storage_name,
path,
r#type,
} => {
let cmd = format!(
"data modify storage {storage} {path} set value {value}{suffix}",
storage = storage_name,
path = path,
value = if boolean.value() { "1" } else { "0" },
suffix = r#type.suffix()
);
Ok(vec![shulkerbox::prelude::Command::Raw(cmd)])
}
DataLocation::ScoreboardValue { objective, target } => {
let cmd = format!(
"scoreboard players set {target} {objective} {value}",
target = target,
objective = objective,
value = if boolean.value() { "1" } else { "0" }
);
Ok(vec![shulkerbox::prelude::Command::Raw(cmd)])
}
},
Primary::Boolean(boolean) => self.store_comptime_value(
&ComptimeValue::Boolean(boolean.value()),
target,
boolean,
handler,
),
Primary::FunctionCall(func) => match target {
DataLocation::ScoreboardValue { objective, target } => {
let call_cmd = self.transpile_function_call(func, scope, handler)?;
@ -456,13 +474,15 @@ impl Transpiler {
.as_ref()
.is_some_and(|args| !args.is_empty())
{
Err(TranspileError::FunctionArgumentsNotAllowed(
let err = TranspileError::FunctionArgumentsNotAllowed(
FunctionArgumentsNotAllowed {
arguments: func.arguments().as_ref().unwrap().span(),
message: "Assigning results to a tag does not support arguments."
.into(),
},
))
);
handler.receive(err.clone());
Err(err)
} else {
let prepare_cmd = Command::Raw(format!("tag {entity} remove {tag_name}"));
let success_cmd = Command::Raw(format!("tag {entity} add {tag_name}"));
@ -482,60 +502,46 @@ impl Transpiler {
}
}
},
Primary::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.as_i64()
))])
}
DataLocation::Tag { .. } => Err(TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ValueType::Boolean,
expression: primary.span(),
})),
DataLocation::Storage {
storage_name,
path,
r#type,
} => {
if matches!(
r#type,
StorageType::Byte
| StorageType::Double
| StorageType::Int
| StorageType::Long
) {
Ok(vec![Command::Raw(format!(
"data modify storage {storage} {path} set value {value}{suffix}",
storage = storage_name,
path = path,
value = int.as_i64(),
suffix = r#type.suffix()
))])
} else {
Err(TranspileError::MismatchedTypes(MismatchedTypes {
expression: primary.span(),
expected_type: ValueType::Integer,
}))
}
}
},
Primary::Integer(int) => self.store_comptime_value(
&ComptimeValue::Integer(int.as_i64()),
target,
int,
handler,
),
Primary::Parenthesized(parenthesized) => {
self.transpile_expression(parenthesized.expression(), target, scope, handler)
}
Primary::Lua(_) => {
// TODO: Add support for Lua.
Err(TranspileError::UnexpectedExpression(UnexpectedExpression(
Expression::Primary(primary.clone()),
)))
Primary::Lua(lua) => {
if let Some(value) = lua.eval_comptime(handler)? {
self.store_comptime_value(&value, target, lua, handler)
} else {
todo!("handle no return value from lua")
}
}
Primary::StringLiteral(_) | Primary::MacroStringLiteral(_) => {
Err(TranspileError::MismatchedTypes(MismatchedTypes {
if matches!(
target,
DataLocation::Storage {
r#type: StorageType::Boolean,
..
} | DataLocation::Tag { .. }
) {
let (mut cmds, cond) =
self.transpile_primary_expression_as_condition(primary, scope, handler)?;
let store_cmds =
self.store_condition_success(cond, target, primary, handler)?;
cmds.extend(store_cmds);
Ok(cmds)
} else {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: target.value_type(),
expression: primary.span(),
}))
});
handler.receive(err.clone());
Err(err)
}
}
Primary::Prefix(prefix) => match prefix.operator() {
PrefixOperator::Negate(_) => match target {
@ -544,7 +550,7 @@ impl Transpiler {
target: score_target,
} => {
let mut expr_cmds = self.transpile_primary_expression(
prefix.operand(),
dbg!(prefix).operand(),
target,
scope,
handler,
@ -555,9 +561,65 @@ impl Transpiler {
Ok(expr_cmds)
}
_ => todo!("Negate operator for other types"),
DataLocation::Storage {
storage_name,
path,
r#type,
} if matches!(
r#type,
StorageType::Byte | StorageType::Int | StorageType::Long
) =>
{
let (target_objective, mut targets) = self.get_temp_scoreboard_locations(1);
let target_ident = targets.pop().expect("at least size 1");
let score_to_storage_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 {target_ident} {target_objective} "
))))),
));
let mut scoreboard_cmds = self.transpile_primary_expression(
primary,
&DataLocation::ScoreboardValue {
objective: target_objective,
target: target_ident,
},
PrefixOperator::LogicalNot(_) => todo!("Logical not operator"),
scope,
handler,
)?;
scoreboard_cmds.push(score_to_storage_cmd);
Ok(scoreboard_cmds)
}
_ => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expression: prefix.span(),
expected_type: ValueType::Integer,
});
handler.receive(err.clone());
Err(err)
}
},
PrefixOperator::LogicalNot(_) => {
let (mut cmds, cond) = self
.transpile_primary_expression_as_condition(primary, scope, handler)
.inspect_err(|err| {
dbg!(err);
})?;
let store_cmds =
self.store_condition_success(cond, target, primary, handler)?;
cmds.extend(store_cmds);
Ok(cmds)
}
},
Primary::Identifier(ident) => {
let variable = scope.get_variable(ident.span.str());
@ -708,56 +770,8 @@ impl Transpiler {
let (mut cmds, cond) =
self.transpile_binary_expression_as_condition(binary, scope, handler)?;
let (success_cmd, else_cmd) = match target {
DataLocation::ScoreboardValue { objective, target } => (
format!("scoreboard players set {target} {objective} 1"),
format!("scoreboard players set {target} {objective} 0"),
),
DataLocation::Storage {
storage_name,
path,
r#type,
} => {
if matches!(r#type, StorageType::Boolean) {
(
format!(
"data modify storage {storage_name} {path} set value 1b"
),
format!(
"data modify storage {storage_name} {path} set value 0b"
),
)
} else {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ValueType::Boolean,
expression: binary.span(),
});
handler.receive(err.clone());
return Err(err);
}
}
DataLocation::Tag { tag_name, entity } => (
format!("tag {entity} add {tag_name}"),
format!("tag {entity} remove {tag_name}"),
),
};
let cmd = match cond {
ExtendedCondition::Runtime(cond) => Command::Execute(Execute::If(
cond,
Box::new(Execute::Run(Box::new(Command::Raw(success_cmd)))),
Some(Box::new(Execute::Run(Box::new(Command::Raw(else_cmd))))),
)),
ExtendedCondition::Comptime(cond) => {
if cond {
Command::Raw(success_cmd)
} else {
Command::Raw(else_cmd)
}
}
};
cmds.push(cmd);
let store_cmds = self.store_condition_success(cond, target, binary, handler)?;
cmds.extend(store_cmds);
Ok(cmds)
}
@ -904,7 +918,21 @@ impl Transpiler {
Err(err)
}
},
Primary::Lua(_) => todo!("Lua code as condition"),
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"),
}
}
}
}
@ -1011,7 +1039,7 @@ impl Transpiler {
StorageType::Byte | StorageType::Double | StorageType::Int | StorageType::Long => {
Some(Command::Execute(Execute::Store(
format!(
"result storage {storage_name} {path} {t} 1",
"result storage {storage_name} {path} {t} 1.0",
t = r#type.as_str()
)
.into(),
@ -1228,22 +1256,175 @@ impl Transpiler {
suffix = r#type.suffix(),
))])
} else {
Err(TranspileError::MismatchedTypes(MismatchedTypes {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expression: original.span(),
expected_type: target.value_type(),
}))
});
handler.receive(err.clone());
Err(err)
}
}
},
ComptimeValue::String(_) | ComptimeValue::MacroString(_) => {
Err(TranspileError::MismatchedTypes(MismatchedTypes {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: target.value_type(),
expression: original.span(),
}))
});
handler.receive(err.clone());
Err(err)
}
}
}
fn store_comptime_value(
&mut self,
value: &ComptimeValue,
target: &DataLocation,
source: &impl SourceElement,
handler: &impl Handler<base::Error>,
) -> 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::Tag { .. } => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ValueType::Boolean,
expression: source.span(),
});
handler.receive(err.clone());
Err(err)
}
DataLocation::Storage {
storage_name,
path,
r#type,
} => {
if matches!(
r#type,
StorageType::Byte
| StorageType::Double
| StorageType::Int
| StorageType::Long
) {
Ok(vec![Command::Raw(format!(
"data modify storage {storage} {path} set value {value}{suffix}",
storage = storage_name,
path = path,
value = int,
suffix = r#type.suffix()
))])
} else {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expression: source.span(),
expected_type: ValueType::Integer,
});
handler.receive(err.clone());
Err(err)
}
}
},
&ComptimeValue::Boolean(boolean) => match target {
DataLocation::Tag { tag_name, entity } => {
let cmd = format!(
"tag {target} {op} {tag}",
target = entity,
op = if boolean { "add" } else { "remove" },
tag = tag_name
);
Ok(vec![shulkerbox::prelude::Command::Raw(cmd)])
}
DataLocation::Storage {
storage_name,
path,
r#type,
} => {
let cmd = format!(
"data modify storage {storage} {path} set value {value}{suffix}",
storage = storage_name,
path = path,
value = if boolean { "1" } else { "0" },
suffix = r#type.suffix()
);
Ok(vec![shulkerbox::prelude::Command::Raw(cmd)])
}
DataLocation::ScoreboardValue { objective, target } => {
let cmd = format!(
"scoreboard players set {target} {objective} {value}",
target = target,
objective = objective,
value = if boolean { "1" } else { "0" }
);
Ok(vec![shulkerbox::prelude::Command::Raw(cmd)])
}
},
ComptimeValue::String(_) | ComptimeValue::MacroString(_) => {
todo!("store string comptime value")
}
}
}
fn store_condition_success(
&mut self,
cond: ExtendedCondition,
target: &DataLocation,
source: &impl SourceElement,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Vec<Command>> {
let (success_cmd, else_cmd) = match target {
DataLocation::ScoreboardValue { objective, target } => (
format!("scoreboard players set {target} {objective} 1"),
format!("scoreboard players set {target} {objective} 0"),
),
DataLocation::Storage {
storage_name,
path,
r#type,
} => {
if matches!(r#type, StorageType::Boolean) {
(
format!("data modify storage {storage_name} {path} set value 1b"),
format!("data modify storage {storage_name} {path} set value 0b"),
)
} else {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ValueType::Boolean,
expression: source.span(),
});
handler.receive(err.clone());
return Err(err);
}
}
DataLocation::Tag { tag_name, entity } => (
format!("tag {entity} add {tag_name}"),
format!("tag {entity} remove {tag_name}"),
),
};
let cmd = match cond {
ExtendedCondition::Runtime(cond) => Command::Execute(Execute::If(
cond,
Box::new(Execute::Run(Box::new(Command::Raw(success_cmd)))),
Some(Box::new(Execute::Run(Box::new(Command::Raw(else_cmd))))),
)),
ExtendedCondition::Comptime(cond) => {
if cond {
Command::Raw(success_cmd)
} else {
Command::Raw(else_cmd)
}
}
};
Ok(vec![cmd])
}
/// Get temporary scoreboard locations.
fn get_temp_scoreboard_locations(&mut self, amount: usize) -> (String, Vec<String>) {
let objective = "shu_temp_".to_string()

View File

@ -7,19 +7,19 @@ mod enabled {
use crate::{
base::{self, source_file::SourceElement, Handler},
syntax::syntax_tree::expression::LuaCode,
transpile::error::{LuaRuntimeError, TranspileError, TranspileResult},
transpile::{
error::{LuaRuntimeError, TranspileError, TranspileResult},
expression::ComptimeValue,
},
};
impl LuaCode {
/// Evaluates the Lua code and returns the resulting command.
/// Evaluated the Lua code and returns the resulting value.
///
/// # Errors
/// - If Lua code evaluation is disabled.
/// - If evaluation fails
#[tracing::instrument(level = "debug", name = "eval_lua", skip_all, ret)]
pub fn eval_string(
&self,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<String>> {
pub fn eval(&self, handler: &impl Handler<base::Error>) -> TranspileResult<mlua::Value> {
tracing::debug!("Evaluating Lua code");
let lua = Lua::new();
@ -48,8 +48,7 @@ mod enabled {
self.add_globals(&lua).unwrap();
let lua_result = lua
.load(self.code())
lua.load(self.code())
.set_name(name)
.eval::<Value>()
.map_err(|err| {
@ -57,7 +56,19 @@ mod enabled {
TranspileError::from(LuaRuntimeError::from_lua_err(&err, self.span()));
handler.receive(crate::Error::from(err.clone()));
err
})?;
})
}
/// Evaluates the Lua code and returns the resulting [`ComptimeValue`].
///
/// # Errors
/// - If Lua code evaluation is disabled.
#[tracing::instrument(level = "debug", name = "eval_lua", skip_all, ret)]
pub fn eval_comptime(
&self,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<ComptimeValue>> {
let lua_result = self.eval(handler)?;
self.handle_lua_result(lua_result).inspect_err(|err| {
handler.receive(err.clone());
@ -77,28 +88,32 @@ mod enabled {
Ok(())
}
fn handle_lua_result(&self, value: Value) -> TranspileResult<Option<String>> {
fn handle_lua_result(&self, value: Value) -> TranspileResult<Option<ComptimeValue>> {
match value {
Value::Nil => Ok(None),
Value::String(s) => Ok(Some(s.to_string_lossy())),
Value::Integer(i) => Ok(Some(i.to_string())),
Value::Number(n) => Ok(Some(n.to_string())),
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::Boolean(_)
| Value::Error(_)
Value::Boolean(boolean) => Ok(Some(ComptimeValue::Boolean(boolean))),
Value::Error(_)
| Value::Table(_)
| Value::Thread(_)
| Value::UserData(_)
| Value::LightUserData(_)
| Value::Other(..) => Err(TranspileError::LuaRuntimeError(LuaRuntimeError {
| Value::Other(..) => {
let err = TranspileError::LuaRuntimeError(LuaRuntimeError {
code_block: self.span(),
error_message: format!("invalid return type {}", value.type_name()),
})),
});
todo!("pass error to handler: {err}")
}
}
}
}
@ -112,16 +127,30 @@ mod disabled {
transpile::error::{TranspileError, TranspileResult},
};
use super::expression::ComptimeValue;
impl LuaCode {
/// Will always return an error because Lua code evaluation is disabled.
/// Enable the feature `lua` to enable Lua code evaluation.
///
/// # 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> {
handler.receive(TranspileError::LuaDisabled);
tracing::error!("Lua code evaluation is disabled");
Err(TranspileError::LuaDisabled)
}
/// Will always return an error because Lua code evaluation is disabled.
/// Enable the feature `lua` to enable Lua code evaluation.
///
/// # Errors
/// - If Lua code evaluation is disabled.
pub fn eval_string(
pub fn eval_comptime(
&self,
handler: &impl Handler<base::Error>,
) -> TranspileResult<Option<String>> {
) -> TranspileResult<Option<ComptimeValue>> {
handler.receive(TranspileError::LuaDisabled);
tracing::error!("Lua code evaluation is disabled");
Err(TranspileError::LuaDisabled)

View File

@ -30,8 +30,8 @@ use crate::{
};
use super::{
error::{TranspileError, TranspileResult},
expression::{ComptimeValue, ExtendedCondition},
error::{MismatchedTypes, TranspileError, TranspileResult},
expression::{ComptimeValue, ExtendedCondition, ValueType},
variables::{Scope, VariableData},
FunctionData, TranspileAnnotationValue,
};
@ -419,7 +419,19 @@ impl Transpiler {
_ => unreachable!("Function call should always return a raw command"),
}),
Expression::Primary(Primary::Lua(lua)) => {
lua.eval_string(handler).map(Option::unwrap_or_default)
lua.eval_comptime(handler).and_then(|opt| {
opt.map_or_else(
|| {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expression: expression.span(),
expected_type: ValueType::String,
});
handler.receive(err.clone());
Err(err)
},
|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()),
@ -597,9 +609,22 @@ impl Transpiler {
Expression::Primary(Primary::MacroStringLiteral(string)) => {
Ok(vec![Command::UsesMacro(string.into())])
}
Expression::Primary(Primary::Lua(code)) => Ok(code
.eval_string(handler)?
.map_or_else(Vec::new, |cmd| vec![Command::Raw(cmd)])),
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::Boolean(_) | ComptimeValue::Integer(_)) => {
let err = TranspileError::MismatchedTypes(MismatchedTypes {
expected_type: ValueType::String,
expression: code.span(),
});
handler.receive(err.clone());
Err(err)
}
None => Ok(Vec::new()),
},
Expression::Primary(Primary::Parenthesized(parenthesized)) => self
.transpile_run_expression(
parenthesized.expression(),
@ -607,7 +632,16 @@ impl Transpiler {
scope,
handler,
),
Expression::Binary(_) => todo!("transpile binary expression in run statement"),
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")
}
}
}
}

View File

@ -269,7 +269,7 @@ impl Transpiler {
},
);
}
_ => todo!("implement other variable types"),
_ => unreachable!("no other variable types"),
}
single.assignment().as_ref().map_or_else(
@ -306,7 +306,7 @@ impl Transpiler {
})
}
VariableData::Function { .. } | VariableData::FunctionArgument { .. } => {
Err(TranspileError::AssignmentError(AssignmentError {
let err = TranspileError::AssignmentError(AssignmentError {
identifier: identifier.span(),
message: format!(
"Cannot assign to a {}.",
@ -316,16 +316,20 @@ impl Transpiler {
"function argument"
}
),
}))
});
handler.receive(err.clone());
Err(err)
}
_ => todo!("implement other variable types"),
}?;
self.transpile_expression(expression, &data_location, scope, handler)
} else {
Err(TranspileError::AssignmentError(AssignmentError {
let err = TranspileError::AssignmentError(AssignmentError {
identifier: identifier.span(),
message: "Variable does not exist.".to_string(),
}))
});
handler.receive(err.clone());
Err(err)
}
}
}