Basic support for logical expressions

This commit is contained in:
2025-11-25 00:26:17 -07:00
parent 4e87b57961
commit dd433e1746
5 changed files with 392 additions and 42 deletions

View File

@@ -0,0 +1,119 @@
use crate::compile;
use indoc::indoc;
use pretty_assertions::assert_eq;
#[test]
fn test_comparison_expressions() -> anyhow::Result<()> {
let compiled = compile! {
debug
"
let isGreater = 10 > 5;
let isLess = 5 < 10;
let isEqual = 5 == 5;
let isNotEqual = 5 != 10;
let isGreaterOrEqual = 10 >= 10;
let isLessOrEqual = 5 <= 5;
"
};
assert_eq!(
compiled,
indoc! {
"
j main
main:
sgt r1 10 5
move r8 r1 #isGreater
slt r2 5 10
move r9 r2 #isLess
seq r3 5 5
move r10 r3 #isEqual
sne r4 5 10
move r11 r4 #isNotEqual
sge r5 10 10
move r12 r5 #isGreaterOrEqual
sle r6 5 5
move r13 r6 #isLessOrEqual
"
}
);
Ok(())
}
#[test]
fn test_logical_and_or_not() -> anyhow::Result<()> {
let compiled = compile! {
debug
"
let logic1 = 1 && 1;
let logic2 = 1 || 0;
let logic3 = !1;
"
};
assert_eq!(
compiled,
indoc! {
"
j main
main:
and r1 1 1
move r8 r1 #logic1
or r2 1 0
move r9 r2 #logic2
seq r3 1 0
move r10 r3 #logic3
"
}
);
Ok(())
}
#[test]
fn test_complex_logic() -> anyhow::Result<()> {
let compiled = compile! {
debug
"
let logic = (10 > 5) && (5 < 10);
"
};
assert_eq!(
compiled,
indoc! {
"
j main
main:
sgt r1 10 5
slt r2 5 10
and r3 r1 r2
move r8 r3 #logic
"
}
);
Ok(())
}
#[test]
fn test_math_with_logic() -> anyhow::Result<()> {
let compiled = compile! {
debug
"
let logic = (1 + 2) > 1;
"
};
assert_eq!(
compiled,
indoc! {
"
"
}
);
Ok(())
}

View File

@@ -44,3 +44,4 @@ mod binary_expression;
mod declaration_function_invocation;
mod declaration_literal;
mod function_declaration;
mod logic_expression;

View File

@@ -3,7 +3,7 @@ use parser::{
Parser as ASTParser,
tree_node::{
BinaryExpression, BlockExpression, DeviceDeclarationExpression, Expression,
FunctionExpression, InvocationExpression, Literal,
FunctionExpression, InvocationExpression, Literal, LogicalExpression,
},
};
use quick_error::quick_error;
@@ -165,6 +165,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
let result = self.expression_binary(bin_expr, scope)?;
Ok(Some(result))
}
Expression::Logical(log_expr) => {
let result = self.expression_logical(log_expr, scope)?;
Ok(Some(result))
}
Expression::Literal(Literal::Number(num)) => {
let temp_name = self.next_temp_name();
let loc = scope.add_variable(&temp_name, LocationRequest::Temp)?;
@@ -280,6 +284,20 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
}
var_loc
}
Expression::Logical(log_expr) => {
let result = self.expression_logical(log_expr, scope)?;
let var_loc = scope.add_variable(&var_name, LocationRequest::Persist)?;
// Move result from temp to new persistent variable
let result_reg = self.resolve_register(&result.location)?;
self.emit_variable_assignment(&var_name, &var_loc, result_reg)?;
// Free the temp result
if let Some(name) = result.temp_name {
scope.free_temp(name)?;
}
var_loc
}
Expression::Variable(name) => {
let src_loc = scope.get_location_of(&name)?;
let var_loc = scope.add_variable(&var_name, LocationRequest::Persist)?;
@@ -374,6 +392,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
stack.free_temp(name)?;
}
}
Expression::Logical(log_expr) => {
// Compile the logical expression to a temp register
let result = self.expression_logical(log_expr, stack)?;
let reg_str = self.resolve_register(&result.location)?;
self.write_output(format!("push {reg_str}"))?;
if let Some(name) = result.temp_name {
stack.free_temp(name)?;
}
}
_ => {
return Err(Error::Unknown(format!(
"Attempted to call `{}` with an unsupported argument type",
@@ -524,6 +551,73 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
})
}
fn expression_logical<'v>(
&mut self,
expr: LogicalExpression,
scope: &mut VariableScope<'v>,
) -> Result<CompilationResult, Error> {
match expr {
LogicalExpression::Not(inner) => {
let (inner_str, cleanup) = self.compile_operand(*inner, scope)?;
let result_name = self.next_temp_name();
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp)?;
let result_reg = self.resolve_register(&result_loc)?;
// seq rX rY 0 => if rY == 0 set rX = 1 else rX = 0
self.write_output(format!("seq {result_reg} {inner_str} 0"))?;
if let Some(name) = cleanup {
scope.free_temp(name)?;
}
Ok(CompilationResult {
location: result_loc,
temp_name: Some(result_name),
})
}
_ => {
let (op_str, left_expr, right_expr) = match expr {
LogicalExpression::And(l, r) => ("and", l, r),
LogicalExpression::Or(l, r) => ("or", l, r),
LogicalExpression::Equal(l, r) => ("seq", l, r),
LogicalExpression::NotEqual(l, r) => ("sne", l, r),
LogicalExpression::GreaterThan(l, r) => ("sgt", l, r),
LogicalExpression::GreaterThanOrEqual(l, r) => ("sge", l, r),
LogicalExpression::LessThan(l, r) => ("slt", l, r),
LogicalExpression::LessThanOrEqual(l, r) => ("sle", l, r),
LogicalExpression::Not(_) => unreachable!(),
};
// Compile LHS
let (lhs_str, lhs_cleanup) = self.compile_operand(*left_expr, scope)?;
// Compile RHS
let (rhs_str, rhs_cleanup) = self.compile_operand(*right_expr, scope)?;
// Allocate result register
let result_name = self.next_temp_name();
let result_loc = scope.add_variable(&result_name, LocationRequest::Temp)?;
let result_reg = self.resolve_register(&result_loc)?;
// Emit instruction: op result lhs rhs
self.write_output(format!("{op_str} {result_reg} {lhs_str} {rhs_str}"))?;
// Clean up operand temps
if let Some(name) = lhs_cleanup {
scope.free_temp(name)?;
}
if let Some(name) = rhs_cleanup {
scope.free_temp(name)?;
}
Ok(CompilationResult {
location: result_loc,
temp_name: Some(result_name),
})
}
}
}
fn expression_block<'v>(
&mut self,
mut expr: BlockExpression,
@@ -623,6 +717,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
scope.free_temp(name)?;
}
}
Expression::Logical(log_expr) => {
let result = self.expression_logical(log_expr, scope)?;
let result_reg = self.resolve_register(&result.location)?;
self.write_output(format!(
"move r{} {}",
VariableScope::RETURN_REGISTER,
result_reg
))?;
if let Some(name) = result.temp_name {
scope.free_temp(name)?;
}
}
_ => {
return Err(Error::Unknown(format!(
"Unsupported `return` statement: {:?}",
@@ -748,3 +854,4 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
}