diff --git a/libs/compiler/src/test/syscall.rs b/libs/compiler/src/test/syscall.rs index f833639..7433880 100644 --- a/libs/compiler/src/test/syscall.rs +++ b/libs/compiler/src/test/syscall.rs @@ -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(()) +} diff --git a/libs/compiler/src/v1.rs b/libs/compiler/src/v1.rs index 370f02e..2b1709d 100644 --- a/libs/compiler/src/v1.rs +++ b/libs/compiler/src/v1.rs @@ -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( diff --git a/libs/parser/src/lib.rs b/libs/parser/src/lib.rs index 6747da8..8eca23a 100644 --- a/libs/parser/src/lib.rs +++ b/libs/parser/src/lib.rs @@ -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 diff --git a/libs/parser/src/sys_call.rs b/libs/parser/src/sys_call.rs index 6b97107..bb2c882 100644 --- a/libs/parser/src/sys_call.rs +++ b/libs/parser/src/sys_call.rs @@ -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), /// 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, 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), + /// 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), + /// 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, + ), } 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"