From 7fb153572d62fbf3e9ab259738be866bd60c5d85 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Sat, 6 Dec 2025 01:19:30 -0700 Subject: [PATCH] syscall aliases and more syscalls --- .../libs/compiler/src/test/syscall.rs | 30 ++++ rust_compiler/libs/compiler/src/v1.rs | 165 +++++++++++++++--- rust_compiler/libs/helpers/src/syscall.rs | 22 ++- rust_compiler/libs/parser/src/lib.rs | 114 +++++++++--- rust_compiler/libs/parser/src/sys_call.rs | 50 +++--- 5 files changed, 306 insertions(+), 75 deletions(-) diff --git a/rust_compiler/libs/compiler/src/test/syscall.rs b/rust_compiler/libs/compiler/src/test/syscall.rs index ec323e5..a36e672 100644 --- a/rust_compiler/libs/compiler/src/test/syscall.rs +++ b/rust_compiler/libs/compiler/src/test/syscall.rs @@ -106,6 +106,36 @@ fn test_set_on_device_batched() -> anyhow::Result<()> { 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] fn test_load_from_device() -> anyhow::Result<()> { let compiled = compile! { diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index 236e20f..0a69f7f 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -693,7 +693,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // check for a hash expression or a literal let value = match const_value { 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))), 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 { 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. @@ -1585,22 +1592,34 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { span: Span, scope: &mut VariableScope<'v>, ) -> Result, Error> { + macro_rules! cleanup { + ($($to_clean:expr),*) => { + $( + if let Some(to_clean) = $to_clean { + scope.free_temp(to_clean)?; + } + )* + }; + } match expr { System::Yield => { self.write_output("yield")?; Ok(None) } 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}"))?; - if let Some(temp) = cleanup { - scope.free_temp(temp)?; - } + + cleanup!(var_cleanup); Ok(None) } 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( "Arg1 expected to be a string literal.".into(), span, @@ -1619,7 +1638,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { System::SetOnDevice(device, logic_type, variable) => { 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( "Arg1 expected to be a variable".into(), span, @@ -1641,7 +1664,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { .cloned() .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( "Arg2 expected to be a string".into(), span, @@ -1650,17 +1677,19 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { self.write_output(format!("s {} {} {}", device_val, logic_type, variable))?; - if let Some(temp_var) = var_cleanup { - scope.free_temp(temp_var)?; - } + cleanup!(var_cleanup); Ok(None) } System::SetOnDeviceBatched(device_hash, logic_type, variable) => { let (var, var_cleanup) = self.compile_operand(*variable, scope)?; let (device_hash_val, device_hash_cleanup) = - self.compile_literal_or_variable(device_hash, scope)?; - let Literal::String(logic_type) = logic_type else { + self.compile_literal_or_variable(device_hash.node, scope)?; + let Spanned { + node: Literal::String(logic_type), + .. + } = logic_type + else { return Err(Error::AgrumentMismatch( "Arg2 expected to be a string".into(), 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))?; - 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)?; - } + cleanup!(var_cleanup, device_hash_cleanup); 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) => { - let LiteralOrVariable::Variable(device_spanned) = device else { + let Spanned { + node: LiteralOrVariable::Variable(device_spanned), + .. + } = device + else { return Err(Error::AgrumentMismatch( "Arg1 expected to be a variable".into(), span, @@ -1702,7 +1754,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { .cloned() .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( "Arg2 expected to be a string".into(), span, @@ -1721,11 +1777,68 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { 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( - format!("{t:?}\n\nNot yet implemented"), - Some(span), - )), + self.write_output(format!( + "lb r{} {} {} {}", + 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, + })) + } } } diff --git a/rust_compiler/libs/helpers/src/syscall.rs b/rust_compiler/libs/helpers/src/syscall.rs index 37daaa9..e859c75 100644 --- a/rust_compiler/libs/helpers/src/syscall.rs +++ b/rust_compiler/libs/helpers/src/syscall.rs @@ -2,15 +2,16 @@ macro_rules! with_syscalls { ($matcher:ident) => { $matcher!( + // Big names "yield", "sleep", "hash", - "loadFromDevice", - "loadBatchNamed", - "loadBatch", - "setOnDevice", - "setOnDeviceBatched", - "setOnDeviceBatchedNamed", + "load", + "loadBatched", + "loadBatchedNamed", + "set", + "setBatched", + "setBatchedNamed", "acos", "asin", "atan", @@ -26,7 +27,14 @@ macro_rules! with_syscalls { "sin", "sqrt", "tan", - "trunc" + "trunc", + // Lil' names + "l", + "lb", + "lbn", + "s", + "sb", + "sbn" ); }; } diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index e2f5690..76c4e04 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -1587,18 +1587,25 @@ impl<'a> Parser<'a> { macro_rules! literal_or_variable { ($iter:expr) => { match $iter { - Some(expr) => match &expr.node { - Expression::Literal(literal) => { - LiteralOrVariable::Literal(literal.node.clone()) + Some(expr) => { + let span = expr.span; + match &expr.node { + Expression::Literal(literal) => Spanned { + span, + node: LiteralOrVariable::Literal(literal.node.clone()), + }, + Expression::Variable(ident) => Spanned { + span, + node: LiteralOrVariable::Variable(ident.clone()), + }, + _ => { + return Err(Error::UnexpectedToken( + self.current_span(), + self.current_token.clone().ok_or(Error::UnexpectedEOF)?, + )); + } } - Expression::Variable(ident) => LiteralOrVariable::Variable(ident.clone()), - _ => { - return Err(Error::UnexpectedToken( - self.current_span(), - self.current_token.clone().ok_or(Error::UnexpectedEOF)?, - )) - } - }, + } _ => { return Err(Error::UnexpectedToken( self.current_span(), @@ -1611,8 +1618,11 @@ impl<'a> Parser<'a> { macro_rules! get_arg { ($matcher: ident, $arg: expr) => { - match $arg { - LiteralOrVariable::$matcher(i) => i, + match $arg.node { + LiteralOrVariable::$matcher(i) => Spanned { + node: i, + span: $arg.span, + }, _ => { return Err(Error::InvalidSyntax( self.current_span(), @@ -1641,16 +1651,23 @@ impl<'a> Parser<'a> { let mut args = invocation.arguments.into_iter(); 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( self.current_span(), 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)?; let mut args = invocation.arguments.into_iter(); @@ -1660,7 +1677,10 @@ impl<'a> Parser<'a> { let variable = match next_arg { Some(expr) => match expr.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( self.current_span(), @@ -1685,10 +1705,38 @@ impl<'a> Parser<'a> { Ok(SysCall::System(sys_call::System::LoadFromDevice( 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)?; let mut args = invocation.arguments.into_iter(); let device = literal_or_variable!(args.next()); @@ -1696,22 +1744,44 @@ impl<'a> Parser<'a> { let variable = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(sys_call::System::SetOnDevice( device, - Literal::String(logic_type.to_string().replace("\"", "")), + Spanned { + node: Literal::String(logic_type.node.to_string().replace("\"", "")), + span: logic_type.span, + }, boxed!(variable), ))) } - "setOnDeviceBatched" => { + "setBatched" | "sb" => { 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 variable = args.next().ok_or(Error::UnexpectedEOF)?; + Ok(SysCall::System(sys_call::System::SetOnDeviceBatched( device_hash, - Literal::String(logic_type.to_string().replace("\"", "")), + Spanned { + node: Literal::String(logic_type.to_string().replace("\"", "")), + span: logic_type.span, + }, 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( self.current_span(), self.current_token.clone().ok_or(Error::UnexpectedEOF)?, diff --git a/rust_compiler/libs/parser/src/sys_call.rs b/rust_compiler/libs/parser/src/sys_call.rs index 6a4aa88..8102644 100644 --- a/rust_compiler/libs/parser/src/sys_call.rs +++ b/rust_compiler/libs/parser/src/sys_call.rs @@ -142,58 +142,68 @@ documented! { /// ## Slang /// `sleep(number|var);` Sleep(Box>), - /// 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 /// `HASH("prefabName")` /// ## Slang - /// `HASH("prefabName");` - Hash(Literal), + /// `hash("prefabName");` + /// + /// ## Example + /// ``` + /// const compDoor = hash("StructureCompositeDoor"); + /// setOnDeviceBatched(compDoor, "Lock", true); + /// ``` + Hash(Spanned), /// Represents a function which loads a device variable into a register. /// ## IC10 /// `l r? d? var` /// ## Slang - /// `loadFromDevice(deviceType, "LogicType");` - LoadFromDevice(LiteralOrVariable, Literal), + /// `load(deviceType, "LogicType");` + LoadFromDevice(Spanned, Spanned), /// Function which gets a LogicType from all connected network devices that match /// the provided device hash and name, aggregating them via a batchMode /// ## IC10 /// `lbn r? deviceHash nameHash logicType batchMode` /// ## Slang - /// `loadFromDeviceBatchedNamed(deviceHash, deviceName, "LogicType", "BatchMode");` + /// `loadBatchedNamed(deviceHash, deviceName, "LogicType", "BatchMode");` LoadBatchNamed( - LiteralOrVariable, - Box>, - Literal, - Literal, + Spanned, + Spanned, + Spanned, + Spanned, ), /// Loads a LogicType from all connected network devices, aggregating them via a - /// batchMode + /// BatchMode /// ## IC10 /// `lb r? deviceHash logicType batchMode` /// ## Slang - /// `loadFromDeviceBatched(deviceHash, "Variable", "LogicType");` - LoadBatch(LiteralOrVariable, Literal, Literal), + /// `loadBatched(deviceHash, "Variable", "LogicType");` + LoadBatch(Spanned, Spanned, Spanned), /// Represents a function which stores a setting into a specific device. /// ## IC10 /// `s d? logicType r?` /// ## Slang - /// `setOnDevice(deviceType, "Variable", (number|var));` - SetOnDevice(LiteralOrVariable, Literal, Box>), + /// `set(deviceType, "Variable", (number|var));` + SetOnDevice(Spanned, Spanned, Box>), /// Represents a function which stores a setting to all devices that match /// the given deviceHash /// ## IC10 /// `sb deviceHash logicType r?` - SetOnDeviceBatched(LiteralOrVariable, Literal, Box>), + /// ## Slang + /// `setBatched(deviceHash, "LogicType", (number|var))` + SetOnDeviceBatched(Spanned, Spanned, Box>), /// Represents a function which stores a setting to all devices that match /// both the given deviceHash AND the given nameHash /// ## IC10 /// `sbn deviceHash nameHash logicType r?` /// ## Slang - /// `setOnDeviceBatchedNamed(deviceType, nameHash, "LogicType", (number|var))` + /// `setBatchedNamed(deviceHash, nameHash, "LogicType", (number|var))` SetOnDeviceBatchedNamed( - LiteralOrVariable, - LiteralOrVariable, - Literal, + Spanned, + Spanned, + Spanned, Box>, ), }