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); sleep(3);
let sleepAmount = 15; let sleepAmount = 15;
sleep(sleepAmount); sleep(sleepAmount);
sleep(sleepAmount * 2);
" "
}; };
@@ -45,6 +46,8 @@ fn test_sleep() -> anyhow::Result<()> {
sleep 3 sleep 3
move r8 15 #sleepAmount move r8 15 #sleepAmount
sleep r8 sleep r8
mul r1 r8 2
sleep r1
" "
} }
); );
@@ -60,8 +63,7 @@ fn test_set_on_device() -> anyhow::Result<()> {
device airConditioner = "d0"; device airConditioner = "d0";
let internalTemp = 20c; let internalTemp = 20c;
let shouldToggleOn = internalTemp > 25c; setOnDevice(airConditioner, "On", internalTemp > 25c);
setOnDevice(airConditioner, "On", shouldToggleOn);
"# "#
}; };
@@ -73,8 +75,7 @@ fn test_set_on_device() -> anyhow::Result<()> {
main: main:
move r8 293.15 #internalTemp move r8 293.15 #internalTemp
sgt r1 r8 298.15 sgt r1 r8 298.15
move r9 r1 #shouldToggleOn s d0 On r1
s d0 On r9
" "
} }
); );
@@ -82,6 +83,31 @@ fn test_set_on_device() -> anyhow::Result<()> {
Ok(()) 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] #[test]
fn test_load_from_device() -> anyhow::Result<()> { fn test_load_from_device() -> anyhow::Result<()> {
let compiled = compile! { let compiled = compile! {
@@ -107,3 +133,27 @@ fn test_load_from_device() -> anyhow::Result<()> {
Ok(()) 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) Ok(None)
} }
System::Sleep(amt) => { 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}"))?; self.write_output(format!("sleep {var}"))?;
if let Some(temp) = cleanup { if let Some(temp) = cleanup {
scope.free_temp(temp)?; scope.free_temp(temp)?;
@@ -1031,8 +1031,23 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(None) 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) => { 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 { let LiteralOrVariable::Variable(device) = device else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(
@@ -1058,6 +1073,28 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(None) 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) => { System::LoadFromDevice(device, logic_type) => {
let LiteralOrVariable::Variable(device) = device else { let LiteralOrVariable::Variable(device) = device else {
return Err(Error::AgrumentMismatch( return Err(Error::AgrumentMismatch(

View File

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

View File

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