From 5d92586dba18811b5545a2587275bb11c0cc06f1 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Sat, 6 Dec 2025 02:09:12 -0700 Subject: [PATCH] Fixed compiler bug that causes args.next to be invoked more than the 1 expected time --- .../libs/compiler/src/test/syscall.rs | 34 +++++----- rust_compiler/libs/compiler/src/v1.rs | 60 +++++++++++++++--- .../libs/compiler/src/variable_manager.rs | 2 + rust_compiler/libs/parser/src/lib.rs | 63 ++++++++++++------- 4 files changed, 107 insertions(+), 52 deletions(-) diff --git a/rust_compiler/libs/compiler/src/test/syscall.rs b/rust_compiler/libs/compiler/src/test/syscall.rs index a36e672..9455cb1 100644 --- a/rust_compiler/libs/compiler/src/test/syscall.rs +++ b/rust_compiler/libs/compiler/src/test/syscall.rs @@ -63,7 +63,7 @@ fn test_set_on_device() -> anyhow::Result<()> { device airConditioner = "d0"; let internalTemp = 20c; - setOnDevice(airConditioner, "On", internalTemp > 25c); + set(airConditioner, "On", internalTemp > 25c); "# }; @@ -89,7 +89,7 @@ fn test_set_on_device_batched() -> anyhow::Result<()> { debug r#" const doorHash = hash("Door"); - setOnDeviceBatched(doorHash, "Lock", true); + setBatched(doorHash, "Lock", true); "# }; @@ -109,29 +109,25 @@ fn test_set_on_device_batched() -> anyhow::Result<()> { #[test] fn test_set_on_device_batched_named() -> anyhow::Result<()> { let compiled = compile! { - result + debug r#" device dev = "d0"; const devName = hash("test"); - let myVar = lbn(dev, devName, "On", 12); + sbn(dev, devName, "On", 12); "# }; - println!("{compiled:?}"); - - assert!(compiled.is_empty()); - - // assert_eq!( - // compiled, - // indoc! { - // " - // j main - // main: - // lbn r8 d0 - // " - // } - // ); + assert_eq!( + compiled, + indoc! { + " + j main + main: + sbn d0 -662733300 On 12 + " + } + ); Ok(()) } @@ -143,7 +139,7 @@ fn test_load_from_device() -> anyhow::Result<()> { r#" device airCon = "d0"; - let setting = loadFromDevice(airCon, "On"); + let setting = load(airCon, "On"); "# }; diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index 0a69f7f..7b4dffa 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -77,6 +77,9 @@ quick_error! { ConstAssignment(ident: String, span: Span) { display("Attempted to re-assign a value to const variable `{ident}`") } + DeviceAssignment(ident: String, span: Span) { + display("Attempted to re-assign a value to a device const `{ident}`") + } Unknown(reason: String, span: Option) { display("{reason}") } @@ -104,6 +107,7 @@ impl From for lsp_types::Diagnostic { | UnknownIdentifier(_, span) | InvalidDevice(_, span) | ConstAssignment(_, span) + | DeviceAssignment(_, span) | AgrumentMismatch(_, span) => Diagnostic { range: span.into(), message: value.to_string(), @@ -348,12 +352,20 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { temp_name: None, // User variable, do not free })), Err(_) => { - self.errors - .push(Error::UnknownIdentifier(name.node.clone(), name.span)); - Ok(Some(CompilationResult { - location: VariableLocation::Temporary(0), - temp_name: None, - })) + // fallback, check devices + if let Some(device) = self.devices.get(&name.node) { + Ok(Some(CompilationResult { + location: VariableLocation::Device(device.clone()), + temp_name: None, + })) + } else { + self.errors + .push(Error::UnknownIdentifier(name.node.clone(), name.span)); + Ok(Some(CompilationResult { + location: VariableLocation::Temporary(0), + temp_name: None, + })) + } } } } @@ -467,6 +479,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { None, )); } + VariableLocation::Device(_) => { + return Err(Error::Unknown( + r#"Attempted to emit a variable assignent for device. + This is a Compiler bug and should be reported to the developer."# + .into(), + None, + )); + } } Ok(()) @@ -622,7 +642,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { ))?; format!("r{}", VariableScope::TEMP_STACK_REGISTER) } - VariableLocation::Constant(_) => unreachable!(), + VariableLocation::Constant(_) | VariableLocation::Device(_) => unreachable!(), }; self.emit_variable_assignment(&name_str, &var_loc, src_str)?; (var_loc, None) @@ -765,6 +785,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { VariableLocation::Constant(_) => { return Err(Error::ConstAssignment(identifier.node, identifier.span)); } + VariableLocation::Device(_) => { + return Err(Error::DeviceAssignment(identifier.node, identifier.span)); + } } if let Some(name) = cleanup { @@ -879,6 +902,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { VariableScope::TEMP_STACK_REGISTER ))?; } + VariableLocation::Device(_) => { + return Err(Error::Unknown( + r#"Attempted to pass a device contant into a function argument. These values can be used without scope."#.into(), + Some(arg.span), + )); + } } } Expression::Binary(bin_expr) => { @@ -1128,6 +1157,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { "Cannot resolve a constant value to register".into(), None, )), + VariableLocation::Device(_) => Err(Error::Unknown( + "Cannot resolve a device to a register".into(), + None, + )), VariableLocation::Stack(_) => Err(Error::Unknown( "Cannot resolve Stack location directly to register string without context".into(), None, @@ -1205,6 +1238,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // We return the NEW temp name to be freed. Ok((temp_reg, Some(temp_name))) } + VariableLocation::Device(d) => Ok((d, None)), } } @@ -1505,6 +1539,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { VariableScope::TEMP_STACK_REGISTER ))?; } + VariableLocation::Device(_) => { + return Err(Error::Unknown( + "You can not return a device from a function.".into(), + Some(var_name.span), + )); + } }, Err(_) => { self.errors.push(Error::UnknownIdentifier( @@ -1706,15 +1746,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { 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 {} {} {} {}", + "sbn {} {} {} {}", device_hash, name_hash, logic_type, value ))?; @@ -1725,7 +1767,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { logic_type_cleanup ); - todo!() + Ok(None) } System::LoadFromDevice(device, logic_type) => { let Spanned { diff --git a/rust_compiler/libs/compiler/src/variable_manager.rs b/rust_compiler/libs/compiler/src/variable_manager.rs index 61dde65..0c5696c 100644 --- a/rust_compiler/libs/compiler/src/variable_manager.rs +++ b/rust_compiler/libs/compiler/src/variable_manager.rs @@ -46,6 +46,8 @@ pub enum VariableLocation { Stack(u16), /// Represents a constant value and should be directly substituted as such. Constant(Literal), + /// Represents a device pin. This will contain the exact `d0-d5` string + Device(String), } pub struct VariableScope<'a> { diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index 76c4e04..6a8a587 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -1586,7 +1586,7 @@ impl<'a> Parser<'a> { macro_rules! literal_or_variable { ($iter:expr) => { - match $iter { + match &$iter { Some(expr) => { let span = expr.span; match &expr.node { @@ -1599,9 +1599,9 @@ impl<'a> Parser<'a> { node: LiteralOrVariable::Variable(ident.clone()), }, _ => { - return Err(Error::UnexpectedToken( - self.current_span(), - self.current_token.clone().ok_or(Error::UnexpectedEOF)?, + return Err(Error::InvalidSyntax( + expr.span, + "Expected a literal or variable".to_string(), )); } } @@ -1625,8 +1625,8 @@ impl<'a> Parser<'a> { }, _ => { return Err(Error::InvalidSyntax( - self.current_span(), - String::from("Expected a variable"), + $arg.span, + format!("Expected a {}", stringify!($matcher).to_lowercase()), )) } } @@ -1656,9 +1656,9 @@ impl<'a> Parser<'a> { span, } = lit_str else { - return Err(Error::UnexpectedToken( - self.current_span(), - self.current_token.clone().ok_or(Error::UnexpectedEOF)?, + return Err(Error::InvalidSyntax( + lit_str.span, + "Expected a string literal".to_string(), )); }; @@ -1671,7 +1671,8 @@ impl<'a> Parser<'a> { check_length(self, &invocation.arguments, 2)?; let mut args = invocation.arguments.into_iter(); - let device = literal_or_variable!(args.next()); + let tmp = args.next(); + let device = literal_or_variable!(tmp); let next_arg = args.next(); let variable = match next_arg { @@ -1682,16 +1683,16 @@ impl<'a> Parser<'a> { span: spanned_lit.span, }, _ => { - return Err(Error::UnexpectedToken( - self.current_span(), - self.current_token.clone().ok_or(Error::UnexpectedEOF)?, + return Err(Error::InvalidSyntax( + spanned_lit.span, + "Expected a string literal".to_string(), )); } }, _ => { - return Err(Error::UnexpectedToken( - self.current_span(), - self.current_token.clone().ok_or(Error::UnexpectedEOF)?, + return Err(Error::InvalidSyntax( + expr.span, + "Expected a string literal".to_string(), )); } }, @@ -1739,8 +1740,12 @@ impl<'a> Parser<'a> { "set" | "s" => { check_length(self, &invocation.arguments, 3)?; let mut args = invocation.arguments.into_iter(); - let device = literal_or_variable!(args.next()); - let logic_type = get_arg!(Literal, literal_or_variable!(args.next())); + let tmp = args.next(); + let device = literal_or_variable!(tmp); + + let tmp = args.next(); + let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); + let variable = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(sys_call::System::SetOnDevice( device, @@ -1754,8 +1759,11 @@ impl<'a> Parser<'a> { "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 tmp = args.next(); + let device_hash = literal_or_variable!(tmp); + + let tmp = args.next(); + let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); let variable = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(sys_call::System::SetOnDeviceBatched( @@ -1770,10 +1778,17 @@ impl<'a> Parser<'a> { "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)?); + let tmp = args.next(); + let device_hash = literal_or_variable!(tmp); + + let tmp = args.next(); + let name_hash = literal_or_variable!(tmp); + + let tmp = args.next(); + let logic_type = get_arg!(Literal, literal_or_variable!(tmp)); + + let tmp = args.next(); + let expr = Box::new(tmp.ok_or(Error::UnexpectedEOF)?); Ok(SysCall::System(System::SetOnDeviceBatchedNamed( device_hash,