syscall aliases and more syscalls

This commit is contained in:
2025-12-06 01:19:30 -07:00
parent ee8f5daece
commit 7fb153572d
5 changed files with 306 additions and 75 deletions

View File

@@ -106,6 +106,36 @@ fn test_set_on_device_batched() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_set_on_device_batched_named() -> anyhow::Result<()> {
let compiled = compile! {
result
r#"
device dev = "d0";
const devName = hash("test");
let myVar = lbn(dev, devName, "On", 12);
"#
};
println!("{compiled:?}");
assert!(compiled.is_empty());
// assert_eq!(
// compiled,
// indoc! {
// "
// j main
// main:
// lbn r8 d0
// "
// }
// );
Ok(())
}
#[test] #[test]
fn test_load_from_device() -> anyhow::Result<()> { fn test_load_from_device() -> anyhow::Result<()> {
let compiled = compile! { let compiled = compile! {

View File

@@ -693,7 +693,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// check for a hash expression or a literal // check for a hash expression or a literal
let value = match const_value { let value = match const_value {
LiteralOr::Or(Spanned { LiteralOr::Or(Spanned {
node: SysCall::System(System::Hash(Literal::String(str_to_hash))), node:
SysCall::System(System::Hash(Spanned {
node: Literal::String(str_to_hash),
..
})),
.. ..
}) => Literal::Number(Number::Integer(crc_hash_signed(&str_to_hash))), }) => Literal::Number(Number::Integer(crc_hash_signed(&str_to_hash))),
LiteralOr::Or(Spanned { span, .. }) => { LiteralOr::Or(Spanned { span, .. }) => {
@@ -1148,6 +1152,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
if let Literal::Boolean(b) = spanned_lit.node { if let Literal::Boolean(b) = spanned_lit.node {
return Ok((if b { "1".to_string() } else { "0".to_string() }, None)); return Ok((if b { "1".to_string() } else { "0".to_string() }, None));
} }
if let Literal::String(ref s) = spanned_lit.node {
return Ok((s.to_string(), None));
}
} }
// Optimization for negated literals used as operands. // Optimization for negated literals used as operands.
@@ -1585,22 +1592,34 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
span: Span, span: Span,
scope: &mut VariableScope<'v>, scope: &mut VariableScope<'v>,
) -> Result<Option<CompilationResult>, Error> { ) -> Result<Option<CompilationResult>, Error> {
macro_rules! cleanup {
($($to_clean:expr),*) => {
$(
if let Some(to_clean) = $to_clean {
scope.free_temp(to_clean)?;
}
)*
};
}
match expr { match expr {
System::Yield => { System::Yield => {
self.write_output("yield")?; self.write_output("yield")?;
Ok(None) Ok(None)
} }
System::Sleep(amt) => { System::Sleep(amt) => {
let (var, cleanup) = self.compile_operand(*amt, scope)?; let (var, var_cleanup) = self.compile_operand(*amt, scope)?;
self.write_output(format!("sleep {var}"))?; self.write_output(format!("sleep {var}"))?;
if let Some(temp) = cleanup {
scope.free_temp(temp)?; cleanup!(var_cleanup);
}
Ok(None) Ok(None)
} }
System::Hash(hash_arg) => { System::Hash(hash_arg) => {
let Literal::String(str_lit) = hash_arg else { let Spanned {
node: Literal::String(str_lit),
..
} = hash_arg
else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(
"Arg1 expected to be a string literal.".into(), "Arg1 expected to be a string literal.".into(),
span, span,
@@ -1619,7 +1638,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
System::SetOnDevice(device, logic_type, variable) => { System::SetOnDevice(device, logic_type, variable) => {
let (variable, var_cleanup) = self.compile_operand(*variable, scope)?; let (variable, var_cleanup) = self.compile_operand(*variable, scope)?;
let LiteralOrVariable::Variable(device_spanned) = device else { let Spanned {
node: LiteralOrVariable::Variable(device_spanned),
..
} = device
else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(
"Arg1 expected to be a variable".into(), "Arg1 expected to be a variable".into(),
span, span,
@@ -1641,7 +1664,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
.cloned() .cloned()
.unwrap_or("d0".to_string()); .unwrap_or("d0".to_string());
let Literal::String(logic_type) = logic_type else { let Spanned {
node: Literal::String(logic_type),
..
} = logic_type
else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(
"Arg2 expected to be a string".into(), "Arg2 expected to be a string".into(),
span, span,
@@ -1650,17 +1677,19 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
self.write_output(format!("s {} {} {}", device_val, logic_type, variable))?; self.write_output(format!("s {} {} {}", device_val, logic_type, variable))?;
if let Some(temp_var) = var_cleanup { cleanup!(var_cleanup);
scope.free_temp(temp_var)?;
}
Ok(None) Ok(None)
} }
System::SetOnDeviceBatched(device_hash, logic_type, variable) => { System::SetOnDeviceBatched(device_hash, logic_type, variable) => {
let (var, var_cleanup) = self.compile_operand(*variable, scope)?; let (var, var_cleanup) = self.compile_operand(*variable, scope)?;
let (device_hash_val, device_hash_cleanup) = let (device_hash_val, device_hash_cleanup) =
self.compile_literal_or_variable(device_hash, scope)?; self.compile_literal_or_variable(device_hash.node, scope)?;
let Literal::String(logic_type) = logic_type else { let Spanned {
node: Literal::String(logic_type),
..
} = logic_type
else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(
"Arg2 expected to be a string".into(), "Arg2 expected to be a string".into(),
span, span,
@@ -1669,18 +1698,41 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
self.write_output(format!("sb {} {} {}", device_hash_val, logic_type, var))?; self.write_output(format!("sb {} {} {}", device_hash_val, logic_type, var))?;
if let Some(var_cleanup) = var_cleanup { cleanup!(var_cleanup, device_hash_cleanup);
scope.free_temp(var_cleanup)?;
}
if let Some(device_cleanup) = device_hash_cleanup {
scope.free_temp(device_cleanup)?;
}
Ok(None) Ok(None)
} }
System::SetOnDeviceBatchedNamed(device_hash, name_hash, logic_type, val_expr) => {
let (value, value_cleanup) = self.compile_operand(*val_expr, scope)?;
let (device_hash, device_hash_cleanup) =
self.compile_literal_or_variable(device_hash.node, scope)?;
let (name_hash, name_hash_cleanup) =
self.compile_literal_or_variable(name_hash.node, scope)?;
let (logic_type, logic_type_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(logic_type.node),
scope,
)?;
self.write_output(format!(
"snb {} {} {} {}",
device_hash, name_hash, logic_type, value
))?;
cleanup!(
value_cleanup,
device_hash_cleanup,
name_hash_cleanup,
logic_type_cleanup
);
todo!()
}
System::LoadFromDevice(device, logic_type) => { System::LoadFromDevice(device, logic_type) => {
let LiteralOrVariable::Variable(device_spanned) = device else { let Spanned {
node: LiteralOrVariable::Variable(device_spanned),
..
} = device
else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(
"Arg1 expected to be a variable".into(), "Arg1 expected to be a variable".into(),
span, span,
@@ -1702,7 +1754,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
.cloned() .cloned()
.unwrap_or("d0".to_string()); .unwrap_or("d0".to_string());
let Literal::String(logic_type) = logic_type else { let Spanned {
node: Literal::String(logic_type),
..
} = logic_type
else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(
"Arg2 expected to be a string".into(), "Arg2 expected to be a string".into(),
span, span,
@@ -1721,11 +1777,68 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
temp_name: None, temp_name: None,
})) }))
} }
System::LoadBatch(device_hash, logic_type, batch_mode) => {
let (device_hash, device_hash_cleanup) =
self.compile_literal_or_variable(device_hash.node, scope)?;
let (logic_type, logic_type_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(logic_type.node),
scope,
)?;
let (batch_mode, batch_mode_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(batch_mode.node),
scope,
)?;
t => Err(Error::Unknown( self.write_output(format!(
format!("{t:?}\n\nNot yet implemented"), "lb r{} {} {} {}",
Some(span), VariableScope::RETURN_REGISTER,
)), device_hash,
logic_type,
batch_mode
))?;
cleanup!(device_hash_cleanup, logic_type_cleanup, batch_mode_cleanup);
Ok(Some(CompilationResult {
location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
temp_name: None,
}))
}
System::LoadBatchNamed(device_hash, name_hash, logic_type, batch_mode) => {
let (device_hash, device_hash_cleanup) =
self.compile_literal_or_variable(device_hash.node, scope)?;
let (name_hash, name_hash_cleanup) =
self.compile_literal_or_variable(name_hash.node, scope)?;
let (logic_type, logic_type_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(logic_type.node),
scope,
)?;
let (batch_mode, batch_mode_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(batch_mode.node),
scope,
)?;
self.write_output(format!(
"lbn r{} {} {} {} {}",
VariableScope::RETURN_REGISTER,
device_hash,
name_hash,
logic_type,
batch_mode
))?;
cleanup!(
device_hash_cleanup,
name_hash_cleanup,
logic_type_cleanup,
batch_mode_cleanup
);
Ok(Some(CompilationResult {
location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER),
temp_name: None,
}))
}
} }
} }

View File

@@ -2,15 +2,16 @@
macro_rules! with_syscalls { macro_rules! with_syscalls {
($matcher:ident) => { ($matcher:ident) => {
$matcher!( $matcher!(
// Big names
"yield", "yield",
"sleep", "sleep",
"hash", "hash",
"loadFromDevice", "load",
"loadBatchNamed", "loadBatched",
"loadBatch", "loadBatchedNamed",
"setOnDevice", "set",
"setOnDeviceBatched", "setBatched",
"setOnDeviceBatchedNamed", "setBatchedNamed",
"acos", "acos",
"asin", "asin",
"atan", "atan",
@@ -26,7 +27,14 @@ macro_rules! with_syscalls {
"sin", "sin",
"sqrt", "sqrt",
"tan", "tan",
"trunc" "trunc",
// Lil' names
"l",
"lb",
"lbn",
"s",
"sb",
"sbn"
); );
}; };
} }

View File

@@ -1587,18 +1587,25 @@ impl<'a> Parser<'a> {
macro_rules! literal_or_variable { macro_rules! literal_or_variable {
($iter:expr) => { ($iter:expr) => {
match $iter { match $iter {
Some(expr) => match &expr.node { Some(expr) => {
Expression::Literal(literal) => { let span = expr.span;
LiteralOrVariable::Literal(literal.node.clone()) match &expr.node {
} Expression::Literal(literal) => Spanned {
Expression::Variable(ident) => LiteralOrVariable::Variable(ident.clone()), span,
node: LiteralOrVariable::Literal(literal.node.clone()),
},
Expression::Variable(ident) => Spanned {
span,
node: LiteralOrVariable::Variable(ident.clone()),
},
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
self.current_span(), self.current_span(),
self.current_token.clone().ok_or(Error::UnexpectedEOF)?, self.current_token.clone().ok_or(Error::UnexpectedEOF)?,
)) ));
}
}
} }
},
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
self.current_span(), self.current_span(),
@@ -1611,8 +1618,11 @@ impl<'a> Parser<'a> {
macro_rules! get_arg { macro_rules! get_arg {
($matcher: ident, $arg: expr) => { ($matcher: ident, $arg: expr) => {
match $arg { match $arg.node {
LiteralOrVariable::$matcher(i) => i, LiteralOrVariable::$matcher(i) => Spanned {
node: i,
span: $arg.span,
},
_ => { _ => {
return Err(Error::InvalidSyntax( return Err(Error::InvalidSyntax(
self.current_span(), self.current_span(),
@@ -1641,16 +1651,23 @@ impl<'a> Parser<'a> {
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let lit_str = literal_or_variable!(args.next()); let lit_str = literal_or_variable!(args.next());
let LiteralOrVariable::Literal(lit_str) = lit_str else { let Spanned {
node: LiteralOrVariable::Literal(lit_str),
span,
} = lit_str
else {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
self.current_span(), self.current_span(),
self.current_token.clone().ok_or(Error::UnexpectedEOF)?, self.current_token.clone().ok_or(Error::UnexpectedEOF)?,
)); ));
}; };
Ok(SysCall::System(System::Hash(lit_str))) Ok(SysCall::System(System::Hash(Spanned {
node: lit_str,
span,
})))
} }
"loadFromDevice" => { "load" | "l" => {
check_length(self, &invocation.arguments, 2)?; check_length(self, &invocation.arguments, 2)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
@@ -1660,7 +1677,10 @@ impl<'a> Parser<'a> {
let variable = match next_arg { let variable = match next_arg {
Some(expr) => match expr.node { Some(expr) => match expr.node {
Expression::Literal(spanned_lit) => match spanned_lit.node { Expression::Literal(spanned_lit) => match spanned_lit.node {
Literal::String(s) => s, Literal::String(s) => Spanned {
node: s,
span: spanned_lit.span,
},
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::UnexpectedToken(
self.current_span(), self.current_span(),
@@ -1685,10 +1705,38 @@ impl<'a> Parser<'a> {
Ok(SysCall::System(sys_call::System::LoadFromDevice( Ok(SysCall::System(sys_call::System::LoadFromDevice(
device, device,
Literal::String(variable), Spanned {
node: Literal::String(variable.node),
span: variable.span,
},
))) )))
} }
"setOnDevice" => { "loadBatched" | "lb" => {
check_length(self, &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter();
let device_hash = literal_or_variable!(args.next());
let logic_type = get_arg!(Literal, literal_or_variable!(args.next()));
let batch_mode = get_arg!(Literal, literal_or_variable!(args.next()));
Ok(SysCall::System(System::LoadBatch(
device_hash,
logic_type,
batch_mode,
)))
}
"loadBatchedNamed" | "lbn" => {
check_length(self, &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter();
let dev_hash = literal_or_variable!(args.next());
let name_hash = literal_or_variable!(args.next());
let logic_type = get_arg!(Literal, literal_or_variable!(args.next()));
let batch_mode = get_arg!(Literal, literal_or_variable!(args.next()));
Ok(SysCall::System(System::LoadBatchNamed(
dev_hash, name_hash, logic_type, batch_mode,
)))
}
"set" | "s" => {
check_length(self, &invocation.arguments, 3)?; check_length(self, &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let device = literal_or_variable!(args.next()); let device = literal_or_variable!(args.next());
@@ -1696,22 +1744,44 @@ impl<'a> Parser<'a> {
let variable = args.next().ok_or(Error::UnexpectedEOF)?; let variable = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::System(sys_call::System::SetOnDevice( Ok(SysCall::System(sys_call::System::SetOnDevice(
device, device,
Literal::String(logic_type.to_string().replace("\"", "")), Spanned {
node: Literal::String(logic_type.node.to_string().replace("\"", "")),
span: logic_type.span,
},
boxed!(variable), boxed!(variable),
))) )))
} }
"setOnDeviceBatched" => { "setBatched" | "sb" => {
check_length(self, &invocation.arguments, 3)?; check_length(self, &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let device_hash = literal_or_variable!(args.next()); let device_hash = literal_or_variable!(args.next());
let logic_type = get_arg!(Literal, literal_or_variable!(args.next())); let logic_type = get_arg!(Literal, literal_or_variable!(args.next()));
let variable = args.next().ok_or(Error::UnexpectedEOF)?; let variable = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::System(sys_call::System::SetOnDeviceBatched( Ok(SysCall::System(sys_call::System::SetOnDeviceBatched(
device_hash, device_hash,
Literal::String(logic_type.to_string().replace("\"", "")), Spanned {
node: Literal::String(logic_type.to_string().replace("\"", "")),
span: logic_type.span,
},
boxed!(variable), boxed!(variable),
))) )))
} }
"setBatchedNamed" | "sbn" => {
check_length(self, &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter();
let device_hash = literal_or_variable!(args.next());
let name_hash = literal_or_variable!(args.next());
let logic_type = get_arg!(Literal, literal_or_variable!(args.next()));
let expr = Box::new(args.next().ok_or(Error::UnexpectedEOF)?);
Ok(SysCall::System(System::SetOnDeviceBatchedNamed(
device_hash,
name_hash,
logic_type,
expr,
)))
}
_ => Err(Error::UnsupportedKeyword( _ => Err(Error::UnsupportedKeyword(
self.current_span(), self.current_span(),
self.current_token.clone().ok_or(Error::UnexpectedEOF)?, self.current_token.clone().ok_or(Error::UnexpectedEOF)?,

View File

@@ -142,58 +142,68 @@ documented! {
/// ## Slang /// ## Slang
/// `sleep(number|var);` /// `sleep(number|var);`
Sleep(Box<Spanned<Expression>>), Sleep(Box<Spanned<Expression>>),
/// Gets the in-game hash for a specific prefab name. /// Gets the in-game hash for a specific prefab name. NOTE! This call is COMPLETELY
/// optimized away unless you bind it to a `let` variable. If you use a `const` variable
/// however, the hash is correctly computed at compile time and substitued automatically.
/// ## IC10 /// ## IC10
/// `HASH("prefabName")` /// `HASH("prefabName")`
/// ## Slang /// ## Slang
/// `HASH("prefabName");` /// `hash("prefabName");`
Hash(Literal), ///
/// ## Example
/// ```
/// const compDoor = hash("StructureCompositeDoor");
/// setOnDeviceBatched(compDoor, "Lock", true);
/// ```
Hash(Spanned<Literal>),
/// Represents a function which loads a device variable into a register. /// Represents a function which loads a device variable into a register.
/// ## IC10 /// ## IC10
/// `l r? d? var` /// `l r? d? var`
/// ## Slang /// ## Slang
/// `loadFromDevice(deviceType, "LogicType");` /// `load(deviceType, "LogicType");`
LoadFromDevice(LiteralOrVariable, Literal), LoadFromDevice(Spanned<LiteralOrVariable>, Spanned<Literal>),
/// Function which gets a LogicType from all connected network devices that match /// Function which gets a LogicType from all connected network devices that match
/// the provided device hash and name, aggregating them via a batchMode /// the provided device hash and name, aggregating them via a batchMode
/// ## IC10 /// ## IC10
/// `lbn r? deviceHash nameHash logicType batchMode` /// `lbn r? deviceHash nameHash logicType batchMode`
/// ## Slang /// ## Slang
/// `loadFromDeviceBatchedNamed(deviceHash, deviceName, "LogicType", "BatchMode");` /// `loadBatchedNamed(deviceHash, deviceName, "LogicType", "BatchMode");`
LoadBatchNamed( LoadBatchNamed(
LiteralOrVariable, Spanned<LiteralOrVariable>,
Box<Spanned<Expression>>, Spanned<LiteralOrVariable>,
Literal, Spanned<Literal>,
Literal, Spanned<Literal>,
), ),
/// Loads a LogicType from all connected network devices, aggregating them via a /// Loads a LogicType from all connected network devices, aggregating them via a
/// batchMode /// BatchMode
/// ## IC10 /// ## IC10
/// `lb r? deviceHash logicType batchMode` /// `lb r? deviceHash logicType batchMode`
/// ## Slang /// ## Slang
/// `loadFromDeviceBatched(deviceHash, "Variable", "LogicType");` /// `loadBatched(deviceHash, "Variable", "LogicType");`
LoadBatch(LiteralOrVariable, Literal, Literal), LoadBatch(Spanned<LiteralOrVariable>, Spanned<Literal>, Spanned<Literal>),
/// Represents a function which stores a setting into a specific device. /// Represents a function which stores a setting into a specific device.
/// ## IC10 /// ## IC10
/// `s d? logicType r?` /// `s d? logicType r?`
/// ## Slang /// ## Slang
/// `setOnDevice(deviceType, "Variable", (number|var));` /// `set(deviceType, "Variable", (number|var));`
SetOnDevice(LiteralOrVariable, Literal, Box<Spanned<Expression>>), SetOnDevice(Spanned<LiteralOrVariable>, Spanned<Literal>, Box<Spanned<Expression>>),
/// Represents a function which stores a setting to all devices that match /// Represents a function which stores a setting to all devices that match
/// the given deviceHash /// the given deviceHash
/// ## IC10 /// ## IC10
/// `sb deviceHash logicType r?` /// `sb deviceHash logicType r?`
SetOnDeviceBatched(LiteralOrVariable, Literal, Box<Spanned<Expression>>), /// ## Slang
/// `setBatched(deviceHash, "LogicType", (number|var))`
SetOnDeviceBatched(Spanned<LiteralOrVariable>, Spanned<Literal>, Box<Spanned<Expression>>),
/// Represents a function which stores a setting to all devices that match /// Represents a function which stores a setting to all devices that match
/// both the given deviceHash AND the given nameHash /// both the given deviceHash AND the given nameHash
/// ## IC10 /// ## IC10
/// `sbn deviceHash nameHash logicType r?` /// `sbn deviceHash nameHash logicType r?`
/// ## Slang /// ## Slang
/// `setOnDeviceBatchedNamed(deviceType, nameHash, "LogicType", (number|var))` /// `setBatchedNamed(deviceHash, nameHash, "LogicType", (number|var))`
SetOnDeviceBatchedNamed( SetOnDeviceBatchedNamed(
LiteralOrVariable, Spanned<LiteralOrVariable>,
LiteralOrVariable, Spanned<LiteralOrVariable>,
Literal, Spanned<Literal>,
Box<Spanned<Expression>>, Box<Spanned<Expression>>,
), ),
} }