From b21d6cc73e3055d0462d9d664af695573f9b8574 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Tue, 9 Dec 2025 23:45:10 -0700 Subject: [PATCH] Found bug, unable to do an assignment expression with a syscall --- .../libs/compiler/src/test/math_syscall.rs | 16 ++ .../libs/compiler/src/test/syscall.rs | 51 ++++++ rust_compiler/libs/compiler/src/v1.rs | 50 ++++++ .../libs/compiler/src/variable_manager.rs | 6 +- rust_compiler/libs/helpers/src/syscall.rs | 6 +- rust_compiler/libs/parser/src/lib.rs | 164 +++++++++++++----- rust_compiler/libs/parser/src/sys_call.rs | 26 +++ rust_compiler/libs/tokenizer/src/token.rs | 2 +- 8 files changed, 270 insertions(+), 51 deletions(-) diff --git a/rust_compiler/libs/compiler/src/test/math_syscall.rs b/rust_compiler/libs/compiler/src/test/math_syscall.rs index 966a1e3..7eefafa 100644 --- a/rust_compiler/libs/compiler/src/test/math_syscall.rs +++ b/rust_compiler/libs/compiler/src/test/math_syscall.rs @@ -243,6 +243,22 @@ fn test_max() -> Result<()> { Ok(()) } +// #[test] +fn test_max_from_game() -> Result<()> { + let compiled = compile! { + result + r#" + let item = 0; + item = max(1, 2); + "# + }; + + println!("{compiled:?}"); + assert!(compiled.is_empty()); + + Ok(()) +} + #[test] fn test_min() -> Result<()> { let compiled = compile! { diff --git a/rust_compiler/libs/compiler/src/test/syscall.rs b/rust_compiler/libs/compiler/src/test/syscall.rs index 9455cb1..a8e5b9e 100644 --- a/rust_compiler/libs/compiler/src/test/syscall.rs +++ b/rust_compiler/libs/compiler/src/test/syscall.rs @@ -157,3 +157,54 @@ fn test_load_from_device() -> anyhow::Result<()> { Ok(()) } + +#[test] +fn test_load_from_slot() -> anyhow::Result<()> { + let compiled = compile! { + debug + r#" + device airCon = "d0"; + + let setting = ls(airCon, 0, "Occupied"); + "# + }; + + assert_eq!( + compiled, + indoc! { + " + j main + main: + ls r15 d0 0 Occupied + move r8 r15 #setting + " + } + ); + + Ok(()) +} + +#[test] +fn test_set_slot() -> anyhow::Result<()> { + let compiled = compile! { + debug + r#" + device airCon = "d0"; + + ss(airCon, 0, "Occupied", true); + "# + }; + + assert_eq!( + compiled, + indoc! { + " + j main + main: + ss d0 0 Occupied 1 + " + } + ); + + Ok(()) +} diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index ac3acf2..aab52ff 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -865,6 +865,7 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { scope.free_temp(c, None)?; } } + _ => { return Err(Error::Unknown( "Invalid assignment target. Only variables and member access are supported." @@ -1952,6 +1953,55 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { temp_name: None, })) } + System::LoadSlot(dev_name, slot_index, logic_type) => { + let (dev_hash, hash_cleanup) = + self.compile_literal_or_variable(dev_name.node, scope)?; + let (slot_index, slot_cleanup) = self.compile_literal_or_variable( + LiteralOrVariable::Literal(slot_index.node), + scope, + )?; + let (logic_type, logic_cleanup) = self.compile_literal_or_variable( + LiteralOrVariable::Literal(logic_type.node), + scope, + )?; + + self.write_output(format!( + "ls r{} {} {} {}", + VariableScope::RETURN_REGISTER, + dev_hash, + slot_index, + logic_type + ))?; + + cleanup!(hash_cleanup, slot_cleanup, logic_cleanup); + + Ok(Some(CompilationResult { + location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER), + temp_name: None, + })) + } + System::SetSlot(dev_name, slot_index, logic_type, var) => { + let (dev_name, name_cleanup) = + self.compile_literal_or_variable(dev_name.node, scope)?; + let (slot_index, index_cleanup) = self.compile_literal_or_variable( + LiteralOrVariable::Literal(slot_index.node), + scope, + )?; + let (logic_type, type_cleanup) = self.compile_literal_or_variable( + LiteralOrVariable::Literal(logic_type.node), + scope, + )?; + let (var, var_cleanup) = self.compile_operand(*var, scope)?; + + self.write_output(format!( + "ss {} {} {} {}", + dev_name, slot_index, logic_type, var + ))?; + + cleanup!(name_cleanup, index_cleanup, type_cleanup, var_cleanup); + + Ok(None) + } } } diff --git a/rust_compiler/libs/compiler/src/variable_manager.rs b/rust_compiler/libs/compiler/src/variable_manager.rs index 77a5c24..faf8a1a 100644 --- a/rust_compiler/libs/compiler/src/variable_manager.rs +++ b/rust_compiler/libs/compiler/src/variable_manager.rs @@ -52,7 +52,7 @@ pub enum LocationRequest { Stack, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum VariableLocation<'a> { /// Represents a temporary register (r1 - r7) Temporary(u8), @@ -66,7 +66,6 @@ pub enum VariableLocation<'a> { Device(Cow<'a, str>), } -// FIX: Added 'b lifetime for the parent reference pub struct VariableScope<'a, 'b> { temporary_vars: VecDeque, persistant_vars: VecDeque, @@ -75,7 +74,6 @@ pub struct VariableScope<'a, 'b> { parent: Option<&'b VariableScope<'a, 'b>>, } -// FIX: Updated Default impl to include 'b impl<'a, 'b> Default for VariableScope<'a, 'b> { fn default() -> Self { Self { @@ -88,7 +86,6 @@ impl<'a, 'b> Default for VariableScope<'a, 'b> { } } -// FIX: Updated impl block to include 'b impl<'a, 'b> VariableScope<'a, 'b> { #[allow(dead_code)] pub const TEMP_REGISTER_COUNT: u8 = 7; @@ -112,7 +109,6 @@ impl<'a, 'b> VariableScope<'a, 'b> { }) } - // FIX: parent is now &'b VariableScope<'a, 'b> pub fn scoped(parent: &'b VariableScope<'a, 'b>) -> Self { Self { parent: Option::Some(parent), diff --git a/rust_compiler/libs/helpers/src/syscall.rs b/rust_compiler/libs/helpers/src/syscall.rs index e859c75..5ec38c5 100644 --- a/rust_compiler/libs/helpers/src/syscall.rs +++ b/rust_compiler/libs/helpers/src/syscall.rs @@ -9,9 +9,11 @@ macro_rules! with_syscalls { "load", "loadBatched", "loadBatchedNamed", + "loadSlot", "set", "setBatched", "setBatchedNamed", + "setSlot", "acos", "asin", "atan", @@ -32,9 +34,11 @@ macro_rules! with_syscalls { "l", "lb", "lbn", + "ls", "s", "sb", - "sbn" + "sbn", + "ss" ); }; } diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index 8a99e9c..663e2fe 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -295,6 +295,7 @@ impl<'a> Parser<'a> { self, TokenType::Symbol(s) if s.is_operator() || s.is_comparison() || s.is_logical() || matches!(s, Symbol::Assign) ) { + println!("{lhs}"); return Ok(Some(self.infix(lhs)?)); } else if self_matches_current!( self, @@ -1518,18 +1519,23 @@ impl<'a> Parser<'a> { } fn syscall(&mut self) -> Result, Error<'a>> { - fn check_length<'a>( - span: Span, - arguments: &[Spanned>], - length: usize, - ) -> Result<(), Error<'a>> { - if arguments.len() != length { + let invocation = self.invocation()?; + + let check_length = |len: usize| -> Result<(), Error> { + if invocation.arguments.len() != len { return Err(Error::InvalidSyntax( - span, - format!("Expected {} arguments", length), + self.current_span(), + format!("Expected {} arguments", len), )); } Ok(()) + }; + + macro_rules! args { + ($count:expr) => {{ + check_length($count)?; + invocation.arguments.into_iter() + }}; } macro_rules! literal_or_variable { @@ -1581,23 +1587,19 @@ impl<'a> Parser<'a> { }; } - let invocation = self.invocation()?; - match invocation.name.node.as_ref() { // System SysCalls "yield" => { - check_length(self.current_span(), &invocation.arguments, 0)?; + check_length(0)?; Ok(SysCall::System(sys_call::System::Yield)) } "sleep" => { - check_length(self.current_span(), &invocation.arguments, 1)?; - let mut arg = invocation.arguments.into_iter(); - let expr = arg.next().ok_or(Error::UnexpectedEOF)?; + let mut args = args!(1); + let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(System::Sleep(boxed!(expr)))) } "hash" => { - check_length(self.current_span(), &invocation.arguments, 1)?; - let mut args = invocation.arguments.into_iter(); + let mut args = args!(1); let lit_str = literal_or_variable!(args.next()); let Spanned { @@ -1617,8 +1619,7 @@ impl<'a> Parser<'a> { }))) } "load" | "l" => { - check_length(self.current_span(), &invocation.arguments, 2)?; - let mut args = invocation.arguments.into_iter(); + let mut args = args!(2); let tmp = args.next(); let device = literal_or_variable!(tmp); @@ -1662,8 +1663,7 @@ impl<'a> Parser<'a> { ))) } "loadBatched" | "lb" => { - check_length(self.current_span(), &invocation.arguments, 3)?; - let mut args = invocation.arguments.into_iter(); + let mut args = args!(3); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); @@ -1680,8 +1680,7 @@ impl<'a> Parser<'a> { ))) } "loadBatchedNamed" | "lbn" => { - check_length(self.current_span(), &invocation.arguments, 4)?; - let mut args = invocation.arguments.into_iter(); + let mut args = args!(4); let tmp = args.next(); let dev_hash = literal_or_variable!(tmp); @@ -1699,8 +1698,7 @@ impl<'a> Parser<'a> { ))) } "set" | "s" => { - check_length(self.current_span(), &invocation.arguments, 3)?; - let mut args = invocation.arguments.into_iter(); + let mut args = args!(3); let tmp = args.next(); let device = literal_or_variable!(tmp); @@ -1720,8 +1718,7 @@ impl<'a> Parser<'a> { ))) } "setBatched" | "sb" => { - check_length(self.current_span(), &invocation.arguments, 3)?; - let mut args = invocation.arguments.into_iter(); + let mut args = args!(3); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); @@ -1739,8 +1736,7 @@ impl<'a> Parser<'a> { ))) } "setBatchedNamed" | "sbn" => { - check_length(self.current_span(), &invocation.arguments, 4)?; - let mut args = invocation.arguments.into_iter(); + let mut args = args!(4); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); @@ -1760,30 +1756,110 @@ impl<'a> Parser<'a> { expr, ))) } + "loadSlot" | "ls" => { + let mut args = args!(3); + let next = args.next(); + let dev_name = literal_or_variable!(next); + let next = args.next(); + let slot_index = get_arg!(Literal, literal_or_variable!(next)); + if !matches!( + slot_index, + Spanned { + node: Literal::Number(_), + .. + }, + ) { + return Err(Error::InvalidSyntax( + slot_index.span, + "Expected a number".to_string(), + )); + } + let next = args.next(); + let slot_logic = get_arg!(Literal, literal_or_variable!(next)); + if !matches!( + slot_logic, + Spanned { + node: Literal::String(_), + .. + } + ) { + return Err(Error::InvalidSyntax( + slot_logic.span, + "Expected a String".into(), + )); + } + + Ok(SysCall::System(System::LoadSlot( + dev_name, slot_index, slot_logic, + ))) + } + "setSlot" | "ss" => { + let mut args = args!(4); + let next = args.next(); + let dev_name = literal_or_variable!(next); + let next = args.next(); + let slot_index = get_arg!(Literal, literal_or_variable!(next)); + if !matches!( + slot_index, + Spanned { + node: Literal::Number(_), + .. + } + ) { + return Err(Error::InvalidSyntax( + slot_index.span, + "Expected a number".into(), + )); + } + let next = args.next(); + let slot_logic = get_arg!(Literal, literal_or_variable!(next)); + if !matches!( + slot_logic, + Spanned { + node: Literal::String(_), + .. + } + ) { + return Err(Error::InvalidSyntax( + slot_logic.span, + "Expected a string".into(), + )); + } + let next = args.next(); + let expr = next.ok_or(Error::UnexpectedEOF)?; + + Ok(SysCall::System(System::SetSlot( + dev_name, + slot_index, + slot_logic, + Box::new(expr), + ))) + } + // Math SysCalls "acos" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Acos(boxed!(tmp)))) } "asin" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Asin(boxed!(tmp)))) } "atan" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Atan(boxed!(expr)))) } "atan2" => { - check_length(self.current_span(), &invocation.arguments, 2)?; + check_length(2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; @@ -1791,42 +1867,42 @@ impl<'a> Parser<'a> { Ok(SysCall::Math(Math::Atan2(boxed!(arg1), boxed!(arg2)))) } "abs" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Abs(boxed!(expr)))) } "ceil" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Ceil(boxed!(arg)))) } "cos" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Cos(boxed!(arg)))) } "floor" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Floor(boxed!(arg)))) } "log" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Log(boxed!(arg)))) } "max" => { - check_length(self.current_span(), &invocation.arguments, 2)?; + check_length(2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; @@ -1834,7 +1910,7 @@ impl<'a> Parser<'a> { Ok(SysCall::Math(Math::Max(boxed!(arg1), boxed!(arg2)))) } "min" => { - check_length(self.current_span(), &invocation.arguments, 2)?; + check_length(2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; @@ -1842,32 +1918,32 @@ impl<'a> Parser<'a> { Ok(SysCall::Math(Math::Min(boxed!(arg1), boxed!(arg2)))) } "rand" => { - check_length(self.current_span(), &invocation.arguments, 0)?; + check_length(0)?; Ok(SysCall::Math(Math::Rand)) } "sin" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Sin(boxed!(arg)))) } "sqrt" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Sqrt(boxed!(arg)))) } "tan" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Tan(boxed!(arg)))) } "trunc" => { - check_length(self.current_span(), &invocation.arguments, 1)?; + check_length(1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().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 e90e837..00a48ef 100644 --- a/rust_compiler/libs/parser/src/sys_call.rs +++ b/rust_compiler/libs/parser/src/sys_call.rs @@ -214,6 +214,30 @@ documented! { Spanned>, Box>>, ), + /// Loads slot LogicSlotType from device into a variable + /// + /// ## IC10 + /// `ls r0 d0 2 Occupied` + /// ## Slang + /// `let isOccupied = loadSlot(deviceHash, 2, "Occupied");` + /// `let isOccupied = ls(deviceHash, 2, "Occupied");` + LoadSlot( + Spanned>, + Spanned>, + Spanned> + ), + /// Stores a value of LogicType on a device by the index value + /// ## IC10 + /// `ss d0 0 "Open" 1` + /// ## Slang + /// `setSlot(deviceHash, 0, "Open", true);` + /// `ss(deviceHash, 0, "Open", true);` + SetSlot( + Spanned>, + Spanned>, + Spanned>, + Box>> + ) } } @@ -235,6 +259,8 @@ impl<'a> std::fmt::Display for System<'a> { System::SetOnDeviceBatchedNamed(a, b, c, d) => { write!(f, "setOnDeviceBatchedNamed({}, {}, {}, {})", a, b, c, d) } + System::LoadSlot(a, b, c) => write!(f, "loadSlot({}, {}, {})", a, b, c), + System::SetSlot(a, b, c, d) => write!(f, "setSlot({}, {}, {}, {})", a, b, c, d), } } } diff --git a/rust_compiler/libs/tokenizer/src/token.rs b/rust_compiler/libs/tokenizer/src/token.rs index bfda737..7127d95 100644 --- a/rust_compiler/libs/tokenizer/src/token.rs +++ b/rust_compiler/libs/tokenizer/src/token.rs @@ -39,7 +39,7 @@ impl From for Diagnostic { ..Default::default() } } - _ => todo!(), + _ => Diagnostic::default(), } } }