Fixed compiler bug that causes args.next to be invoked more than the 1 expected time

This commit is contained in:
2025-12-06 02:09:12 -07:00
parent 7fb153572d
commit 5d92586dba
4 changed files with 107 additions and 52 deletions

View File

@@ -63,7 +63,7 @@ fn test_set_on_device() -> anyhow::Result<()> {
device airConditioner = "d0"; device airConditioner = "d0";
let internalTemp = 20c; 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 debug
r#" r#"
const doorHash = hash("Door"); 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] #[test]
fn test_set_on_device_batched_named() -> anyhow::Result<()> { fn test_set_on_device_batched_named() -> anyhow::Result<()> {
let compiled = compile! { let compiled = compile! {
result debug
r#" r#"
device dev = "d0"; device dev = "d0";
const devName = hash("test"); const devName = hash("test");
let myVar = lbn(dev, devName, "On", 12); sbn(dev, devName, "On", 12);
"# "#
}; };
println!("{compiled:?}"); assert_eq!(
compiled,
assert!(compiled.is_empty()); indoc! {
"
// assert_eq!( j main
// compiled, main:
// indoc! { sbn d0 -662733300 On 12
// " "
// j main }
// main: );
// lbn r8 d0
// "
// }
// );
Ok(()) Ok(())
} }
@@ -143,7 +139,7 @@ fn test_load_from_device() -> anyhow::Result<()> {
r#" r#"
device airCon = "d0"; device airCon = "d0";
let setting = loadFromDevice(airCon, "On"); let setting = load(airCon, "On");
"# "#
}; };

View File

@@ -77,6 +77,9 @@ quick_error! {
ConstAssignment(ident: String, span: Span) { ConstAssignment(ident: String, span: Span) {
display("Attempted to re-assign a value to const variable `{ident}`") 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<Span>) { Unknown(reason: String, span: Option<Span>) {
display("{reason}") display("{reason}")
} }
@@ -104,6 +107,7 @@ impl From<Error> for lsp_types::Diagnostic {
| UnknownIdentifier(_, span) | UnknownIdentifier(_, span)
| InvalidDevice(_, span) | InvalidDevice(_, span)
| ConstAssignment(_, span) | ConstAssignment(_, span)
| DeviceAssignment(_, span)
| AgrumentMismatch(_, span) => Diagnostic { | AgrumentMismatch(_, span) => Diagnostic {
range: span.into(), range: span.into(),
message: value.to_string(), message: value.to_string(),
@@ -348,6 +352,13 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
temp_name: None, // User variable, do not free temp_name: None, // User variable, do not free
})), })),
Err(_) => { Err(_) => {
// 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 self.errors
.push(Error::UnknownIdentifier(name.node.clone(), name.span)); .push(Error::UnknownIdentifier(name.node.clone(), name.span));
Ok(Some(CompilationResult { Ok(Some(CompilationResult {
@@ -357,6 +368,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
} }
} }
} }
}
Expression::MemberAccess(access) => { Expression::MemberAccess(access) => {
// "load" behavior (e.g. `let x = d0.On`) // "load" behavior (e.g. `let x = d0.On`)
let MemberAccessExpression { object, member } = access.node; let MemberAccessExpression { object, member } = access.node;
@@ -467,6 +479,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
None, 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(()) Ok(())
@@ -622,7 +642,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
))?; ))?;
format!("r{}", VariableScope::TEMP_STACK_REGISTER) format!("r{}", VariableScope::TEMP_STACK_REGISTER)
} }
VariableLocation::Constant(_) => unreachable!(), VariableLocation::Constant(_) | VariableLocation::Device(_) => unreachable!(),
}; };
self.emit_variable_assignment(&name_str, &var_loc, src_str)?; self.emit_variable_assignment(&name_str, &var_loc, src_str)?;
(var_loc, None) (var_loc, None)
@@ -765,6 +785,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
VariableLocation::Constant(_) => { VariableLocation::Constant(_) => {
return Err(Error::ConstAssignment(identifier.node, identifier.span)); return Err(Error::ConstAssignment(identifier.node, identifier.span));
} }
VariableLocation::Device(_) => {
return Err(Error::DeviceAssignment(identifier.node, identifier.span));
}
} }
if let Some(name) = cleanup { if let Some(name) = cleanup {
@@ -879,6 +902,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
VariableScope::TEMP_STACK_REGISTER 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) => { 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(), "Cannot resolve a constant value to register".into(),
None, None,
)), )),
VariableLocation::Device(_) => Err(Error::Unknown(
"Cannot resolve a device to a register".into(),
None,
)),
VariableLocation::Stack(_) => Err(Error::Unknown( VariableLocation::Stack(_) => Err(Error::Unknown(
"Cannot resolve Stack location directly to register string without context".into(), "Cannot resolve Stack location directly to register string without context".into(),
None, None,
@@ -1205,6 +1238,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// We return the NEW temp name to be freed. // We return the NEW temp name to be freed.
Ok((temp_reg, Some(temp_name))) 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 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(_) => { Err(_) => {
self.errors.push(Error::UnknownIdentifier( 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 (value, value_cleanup) = self.compile_operand(*val_expr, scope)?;
let (device_hash, device_hash_cleanup) = let (device_hash, device_hash_cleanup) =
self.compile_literal_or_variable(device_hash.node, scope)?; self.compile_literal_or_variable(device_hash.node, scope)?;
let (name_hash, name_hash_cleanup) = let (name_hash, name_hash_cleanup) =
self.compile_literal_or_variable(name_hash.node, scope)?; self.compile_literal_or_variable(name_hash.node, scope)?;
let (logic_type, logic_type_cleanup) = self.compile_literal_or_variable( let (logic_type, logic_type_cleanup) = self.compile_literal_or_variable(
LiteralOrVariable::Literal(logic_type.node), LiteralOrVariable::Literal(logic_type.node),
scope, scope,
)?; )?;
self.write_output(format!( self.write_output(format!(
"snb {} {} {} {}", "sbn {} {} {} {}",
device_hash, name_hash, logic_type, value device_hash, name_hash, logic_type, value
))?; ))?;
@@ -1725,7 +1767,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
logic_type_cleanup logic_type_cleanup
); );
todo!() Ok(None)
} }
System::LoadFromDevice(device, logic_type) => { System::LoadFromDevice(device, logic_type) => {
let Spanned { let Spanned {

View File

@@ -46,6 +46,8 @@ pub enum VariableLocation {
Stack(u16), Stack(u16),
/// Represents a constant value and should be directly substituted as such. /// Represents a constant value and should be directly substituted as such.
Constant(Literal), Constant(Literal),
/// Represents a device pin. This will contain the exact `d0-d5` string
Device(String),
} }
pub struct VariableScope<'a> { pub struct VariableScope<'a> {

View File

@@ -1586,7 +1586,7 @@ impl<'a> Parser<'a> {
macro_rules! literal_or_variable { macro_rules! literal_or_variable {
($iter:expr) => { ($iter:expr) => {
match $iter { match &$iter {
Some(expr) => { Some(expr) => {
let span = expr.span; let span = expr.span;
match &expr.node { match &expr.node {
@@ -1599,9 +1599,9 @@ impl<'a> Parser<'a> {
node: LiteralOrVariable::Variable(ident.clone()), node: LiteralOrVariable::Variable(ident.clone()),
}, },
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::InvalidSyntax(
self.current_span(), expr.span,
self.current_token.clone().ok_or(Error::UnexpectedEOF)?, "Expected a literal or variable".to_string(),
)); ));
} }
} }
@@ -1625,8 +1625,8 @@ impl<'a> Parser<'a> {
}, },
_ => { _ => {
return Err(Error::InvalidSyntax( return Err(Error::InvalidSyntax(
self.current_span(), $arg.span,
String::from("Expected a variable"), format!("Expected a {}", stringify!($matcher).to_lowercase()),
)) ))
} }
} }
@@ -1656,9 +1656,9 @@ impl<'a> Parser<'a> {
span, span,
} = lit_str } = lit_str
else { else {
return Err(Error::UnexpectedToken( return Err(Error::InvalidSyntax(
self.current_span(), lit_str.span,
self.current_token.clone().ok_or(Error::UnexpectedEOF)?, "Expected a string literal".to_string(),
)); ));
}; };
@@ -1671,7 +1671,8 @@ impl<'a> Parser<'a> {
check_length(self, &invocation.arguments, 2)?; check_length(self, &invocation.arguments, 2)?;
let mut args = invocation.arguments.into_iter(); 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 next_arg = args.next();
let variable = match next_arg { let variable = match next_arg {
@@ -1682,16 +1683,16 @@ impl<'a> Parser<'a> {
span: spanned_lit.span, span: spanned_lit.span,
}, },
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::InvalidSyntax(
self.current_span(), spanned_lit.span,
self.current_token.clone().ok_or(Error::UnexpectedEOF)?, "Expected a string literal".to_string(),
)); ));
} }
}, },
_ => { _ => {
return Err(Error::UnexpectedToken( return Err(Error::InvalidSyntax(
self.current_span(), expr.span,
self.current_token.clone().ok_or(Error::UnexpectedEOF)?, "Expected a string literal".to_string(),
)); ));
} }
}, },
@@ -1739,8 +1740,12 @@ impl<'a> Parser<'a> {
"set" | "s" => { "set" | "s" => {
check_length(self, &invocation.arguments, 3)?; check_length(self, &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let device = literal_or_variable!(args.next()); let tmp = args.next();
let logic_type = get_arg!(Literal, literal_or_variable!(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)?; let variable = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::System(sys_call::System::SetOnDevice( Ok(SysCall::System(sys_call::System::SetOnDevice(
device, device,
@@ -1754,8 +1759,11 @@ impl<'a> Parser<'a> {
"setBatched" | "sb" => { "setBatched" | "sb" => {
check_length(self, &invocation.arguments, 3)?; check_length(self, &invocation.arguments, 3)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let device_hash = literal_or_variable!(args.next()); let tmp = args.next();
let logic_type = get_arg!(Literal, literal_or_variable!(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)?; let variable = args.next().ok_or(Error::UnexpectedEOF)?;
Ok(SysCall::System(sys_call::System::SetOnDeviceBatched( Ok(SysCall::System(sys_call::System::SetOnDeviceBatched(
@@ -1770,10 +1778,17 @@ impl<'a> Parser<'a> {
"setBatchedNamed" | "sbn" => { "setBatchedNamed" | "sbn" => {
check_length(self, &invocation.arguments, 4)?; check_length(self, &invocation.arguments, 4)?;
let mut args = invocation.arguments.into_iter(); let mut args = invocation.arguments.into_iter();
let device_hash = literal_or_variable!(args.next()); let tmp = args.next();
let name_hash = literal_or_variable!(args.next()); let device_hash = literal_or_variable!(tmp);
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 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( Ok(SysCall::System(System::SetOnDeviceBatchedNamed(
device_hash, device_hash,