more working syscalls

This commit is contained in:
2025-11-25 22:54:05 -07:00
parent 97b462eac8
commit 1579d9f905
4 changed files with 199 additions and 22 deletions

View File

@@ -33,6 +33,7 @@ fn test_sleep() -> anyhow::Result<()> {
sleep(3);
let sleepAmount = 15;
sleep(sleepAmount);
sleep(sleepAmount * 2);
"
};
@@ -45,6 +46,8 @@ fn test_sleep() -> anyhow::Result<()> {
sleep 3
move r8 15 #sleepAmount
sleep r8
mul r1 r8 2
sleep r1
"
}
);
@@ -60,8 +63,7 @@ fn test_set_on_device() -> anyhow::Result<()> {
device airConditioner = "d0";
let internalTemp = 20c;
let shouldToggleOn = internalTemp > 25c;
setOnDevice(airConditioner, "On", shouldToggleOn);
setOnDevice(airConditioner, "On", internalTemp > 25c);
"#
};
@@ -73,8 +75,7 @@ fn test_set_on_device() -> anyhow::Result<()> {
main:
move r8 293.15 #internalTemp
sgt r1 r8 298.15
move r9 r1 #shouldToggleOn
s d0 On r9
s d0 On r1
"
}
);
@@ -82,6 +83,31 @@ fn test_set_on_device() -> anyhow::Result<()> {
Ok(())
}
#[test]
fn test_set_on_device_batched() -> anyhow::Result<()> {
let compiled = compile! {
debug
r#"
let doorHash = hash("Door");
setOnDeviceBatched(doorHash, "Lock", true);
"#
};
assert_eq!(
compiled,
indoc! {
r#"
j main
main:
move r15 HASH("Door") #hash_ret
move r8 r15 #doorHash
sb r8 Lock 1
"#
}
);
Ok(())
}
#[test]
fn test_load_from_device() -> anyhow::Result<()> {
let compiled = compile! {
@@ -107,3 +133,27 @@ fn test_load_from_device() -> anyhow::Result<()> {
Ok(())
}
#[test]
fn test_hash() -> anyhow::Result<()> {
let compiled = compile! {
debug
r#"
let nameHash = hash("testValue");
"#
};
assert_eq!(
compiled,
indoc! {
r#"
j main
main:
move r15 HASH("testValue") #hash_ret
move r8 r15 #nameHash
"#
}
);
Ok(())
}

View File

@@ -1023,7 +1023,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(None)
}
System::Sleep(amt) => {
let (var, cleanup) = self.compile_literal_or_variable(amt, scope)?;
let (var, cleanup) = self.compile_operand(*amt, scope)?;
self.write_output(format!("sleep {var}"))?;
if let Some(temp) = cleanup {
scope.free_temp(temp)?;
@@ -1031,8 +1031,23 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(None)
}
System::Hash(hash_arg) => {
let Literal::String(str_lit) = hash_arg else {
return Err(Error::AgrumentMismatch(
"Arg1 expected to be a string literal.".into(),
));
};
let loc = VariableLocation::Persistant(VariableScope::RETURN_REGISTER);
self.emit_variable_assignment("hash_ret", &loc, format!(r#"HASH("{}")"#, str_lit))?;
Ok(Some(CompilationResult {
location: loc,
temp_name: None,
}))
}
System::SetOnDevice(device, logic_type, variable) => {
let (variable, var_cleanup) = self.compile_literal_or_variable(variable, scope)?;
let (variable, var_cleanup) = self.compile_operand(*variable, scope)?;
let LiteralOrVariable::Variable(device) = device else {
return Err(Error::AgrumentMismatch(
@@ -1058,6 +1073,28 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(None)
}
System::SetOnDeviceBatched(device_hash, logic_type, variable) => {
let (var, var_cleanup) = self.compile_operand(*variable, scope)?;
let (device_hash, device_hash_cleanup) =
self.compile_literal_or_variable(device_hash, scope)?;
let Literal::String(logic_type) = logic_type else {
return Err(Error::AgrumentMismatch(
"Arg2 expected to be a string".into(),
));
};
self.write_output(format!("sb {} {} {}", device_hash, logic_type, var))?;
if let Some(var_cleanup) = var_cleanup {
scope.free_temp(var_cleanup)?;
}
if let Some(device_cleanup) = device_hash_cleanup {
scope.free_temp(device_cleanup)?;
}
Ok(None)
}
System::LoadFromDevice(device, logic_type) => {
let LiteralOrVariable::Variable(device) = device else {
return Err(Error::AgrumentMismatch(

View File

@@ -13,6 +13,8 @@ use tokenizer::{
};
use tree_node::*;
use crate::sys_call::System;
#[macro_export]
/// A macro to create a boxed value.
macro_rules! boxed {
@@ -63,6 +65,12 @@ macro_rules! token_from_option {
None => return Err(Error::UnexpectedEOF),
}
};
(owned $token:expr) => {
match $token {
Some(token) => token,
None => return Err(Error::UnexpectedEOF),
}
};
}
macro_rules! extract_token_data {
@@ -1039,9 +1047,22 @@ impl Parser {
}
"sleep" => {
check_length(self, &invocation.arguments, 1)?;
let mut arg = invocation.arguments.iter();
let argument = literal_or_variable!(arg.next());
Ok(SysCall::System(sys_call::System::Sleep(argument)))
let mut arg = invocation.arguments.into_iter();
let expr = token_from_option!(owned arg.next());
Ok(SysCall::System(System::Sleep(boxed!(expr))))
}
"hash" => {
check_length(self, &invocation.arguments, 1)?;
let mut args = invocation.arguments.into_iter();
let lit_str = literal_or_variable!(args.next());
let LiteralOrVariable::Literal(lit_str) = lit_str else {
return Err(Error::UnexpectedToken(
token_from_option!(self.current_token).clone(),
));
};
Ok(SysCall::System(System::Hash(lit_str)))
}
"loadFromDevice" => {
check_length(self, &invocation.arguments, 2)?;
@@ -1076,23 +1097,23 @@ impl Parser {
}
"loadBatchNamed" => {
check_length(self, &invocation.arguments, 4)?;
let mut args = invocation.arguments.iter();
let mut args = invocation.arguments.into_iter();
let device_hash = literal_or_variable!(args.next());
let name_hash = get_arg!(Literal, literal_or_variable!(args.next()));
let name_hash = token_from_option!(owned 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(sys_call::System::LoadBatchNamed(
device_hash,
name_hash,
boxed!(name_hash),
logic_type,
batch_mode,
)))
}
"setOnDevice" => {
check_length(self, &invocation.arguments, 3)?;
let mut args = invocation.arguments.iter();
let mut args = invocation.arguments.into_iter();
let device = literal_or_variable!(args.next());
@@ -1104,12 +1125,54 @@ impl Parser {
));
};
let variable = literal_or_variable!(args.next());
let variable = token_from_option!(owned args.next());
Ok(SysCall::System(sys_call::System::SetOnDevice(
device,
Literal::String(logic_type),
variable,
boxed!(variable),
)))
}
"setOnDeviceBatched" => {
check_length(self, &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter();
let device = literal_or_variable!(args.next());
let Literal::String(logic_type) =
get_arg!(Literal, literal_or_variable!(args.next()))
else {
return Err(Error::UnexpectedToken(
token_from_option!(self.current_token).clone(),
));
};
let variable = token_from_option!(owned args.next());
Ok(SysCall::System(System::SetOnDeviceBatched(
device,
Literal::String(logic_type),
boxed!(variable),
)))
}
"setOnDeviceBatchedNamed" => {
check_length(self, &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter();
let device = literal_or_variable!(args.next());
let name = literal_or_variable!(args.next());
let Literal::String(logic_type) =
get_arg!(Literal, literal_or_variable!(args.next()))
else {
return Err(Error::UnexpectedToken(
token_from_option!(self.current_token).clone(),
));
};
let variable = token_from_option!(owned args.next());
Ok(SysCall::System(System::SetOnDeviceBatchedNamed(
device,
name,
Literal::String(logic_type),
boxed!(variable),
)))
}
// math calls

View File

@@ -1,4 +1,4 @@
use crate::tree_node::Literal;
use crate::tree_node::{Expression, Literal};
use super::LiteralOrVariable;
@@ -102,11 +102,11 @@ pub enum System {
/// Represents a function that can be called to sleep for a certain amount of time.
/// ## In Game
/// `sleep a(r?|num)`
Sleep(LiteralOrVariable),
Sleep(Box<Expression>),
/// Gets the in-game hash for a specific prefab name.
/// ## In Game
/// `HASH("prefabName")`
Hash(LiteralOrVariable),
Hash(Literal),
/// Represents a function which loads a device variable into a register.
/// ## In Game
/// `l r? d? var`
@@ -120,7 +120,7 @@ pub enum System {
/// lbn r? deviceHash nameHash logicType batchMode
/// ## Examples
/// lbn r0 HASH("StructureWallLight") HASH("wallLight") On Minimum
LoadBatchNamed(LiteralOrVariable, Literal, Literal, Literal),
LoadBatchNamed(LiteralOrVariable, Box<Expression>, Literal, Literal),
/// Loads a LogicType from all connected network devices, aggregating them via a
/// batchMode
/// ## In Game
@@ -133,7 +133,26 @@ pub enum System {
/// `s d? logicType r?`
/// ## Example
/// `s d0 Setting r0`
SetOnDevice(LiteralOrVariable, Literal, LiteralOrVariable),
SetOnDevice(LiteralOrVariable, Literal, Box<Expression>),
/// Represents a function which stores a setting to all devices that match
/// the given deviceHash
/// ## In Game
/// `sb deviceHash logictype r?`
/// ## Example
/// `sb HASH("Doors") Lock 1`
SetOnDeviceBatched(LiteralOrVariable, Literal, Box<Expression>),
/// Represents a function which stores a setting to all devices that match
/// both the given deviceHash AND the given nameHash
/// ## In Game
/// `sbn deviceHash nameHash logicType r?`
/// ## Example
/// `sbn HASH("Doors") HASH("Exterior") Lock 1`
SetOnDeviceBatchedNamed(
LiteralOrVariable,
LiteralOrVariable,
Literal,
Box<Expression>,
),
}
impl std::fmt::Display for System {
@@ -141,13 +160,19 @@ impl std::fmt::Display for System {
match self {
System::Yield => write!(f, "yield()"),
System::Sleep(a) => write!(f, "sleep({})", a),
System::Hash(a) => write!(f, "HASH({})", a),
System::Hash(a) => write!(f, "hash({})", a),
System::LoadFromDevice(a, b) => write!(f, "loadFromDevice({}, {})", a, b),
System::LoadBatch(a, b, c) => write!(f, "loadBatch({}, {}, {})", a, b, c),
System::LoadBatchNamed(a, b, c, d) => {
write!(f, "loadBatchNamed({}, {}, {}, {})", a, b, c, d)
}
System::SetOnDevice(a, b, c) => write!(f, "setOnDevice({}, {}, {})", a, b, c),
System::SetOnDeviceBatched(a, b, c) => {
write!(f, "setOnDeviceBatched({}, {}, {})", a, b, c)
}
System::SetOnDeviceBatchedNamed(a, b, c, d) => {
write!(f, "setOnDeviceBatchedNamed({}, {}, {}, {})", a, b, c, d)
}
}
}
}
@@ -175,9 +200,11 @@ impl SysCall {
identifier,
"yield"
| "sleep"
| "HASH"
| "hash"
| "loadFromDevice"
| "setOnDevice"
| "setOnDeviceBatched"
| "setOnDeviceBatchedNamed"
| "acos"
| "asin"
| "atan"