From a60e9d7dce3438c67f159ed8c7b7df516fa71b73 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Fri, 5 Dec 2025 23:25:23 -0700 Subject: [PATCH] fold nested literal binary expressions --- rust_compiler/Cargo.lock | 6 +- rust_compiler/libs/compiler/Cargo.toml | 3 +- .../compiler/src/test/binary_expression.rs | 12 +-- .../compiler/src/test/logic_expression.rs | 5 +- .../libs/compiler/src/test/syscall.rs | 30 +----- rust_compiler/libs/compiler/src/v1.rs | 91 +++++++++++++++---- rust_compiler/libs/helpers/Cargo.toml | 1 + .../libs/helpers/src/helper_funcs.rs | 11 +++ rust_compiler/libs/helpers/src/lib.rs | 2 + rust_compiler/libs/tokenizer/src/token.rs | 75 +++++++++++++++ 10 files changed, 176 insertions(+), 60 deletions(-) create mode 100644 rust_compiler/libs/helpers/src/helper_funcs.rs diff --git a/rust_compiler/Cargo.lock b/rust_compiler/Cargo.lock index d6d7361..664669d 100644 --- a/rust_compiler/Cargo.lock +++ b/rust_compiler/Cargo.lock @@ -252,12 +252,13 @@ name = "compiler" version = "0.1.0" dependencies = [ "anyhow", - "crc32fast", + "helpers", "indoc", "lsp-types", "parser", "pretty_assertions", "quick-error", + "rust_decimal", "tokenizer", ] @@ -373,6 +374,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "helpers" version = "0.1.0" +dependencies = [ + "crc32fast", +] [[package]] name = "indexmap" diff --git a/rust_compiler/libs/compiler/Cargo.toml b/rust_compiler/libs/compiler/Cargo.toml index 940ba19..a21718c 100644 --- a/rust_compiler/libs/compiler/Cargo.toml +++ b/rust_compiler/libs/compiler/Cargo.toml @@ -7,8 +7,9 @@ edition = "2024" quick-error = { workspace = true } parser = { path = "../parser" } tokenizer = { path = "../tokenizer" } +helpers = { path = "../helpers" } lsp-types = { workspace = true } -crc32fast = { workspace = true } +rust_decimal = { workspace = true } [dev-dependencies] anyhow = { version = "1.0" } diff --git a/rust_compiler/libs/compiler/src/test/binary_expression.rs b/rust_compiler/libs/compiler/src/test/binary_expression.rs index 8f890d4..8254eaa 100644 --- a/rust_compiler/libs/compiler/src/test/binary_expression.rs +++ b/rust_compiler/libs/compiler/src/test/binary_expression.rs @@ -17,8 +17,7 @@ fn simple_binary_expression() -> anyhow::Result<()> { " j main main: - add r1 1 2 - move r8 r1 #i + move r8 3 #i " } ); @@ -72,7 +71,7 @@ fn nested_binary_expressions() -> anyhow::Result<()> { } #[test] -fn stress_test_negation_with_stack_spillover() -> anyhow::Result<()> { +fn stress_test_constant_folding() -> anyhow::Result<()> { let compiled = compile! { debug " @@ -86,12 +85,7 @@ fn stress_test_negation_with_stack_spillover() -> anyhow::Result<()> { " j main main: - add r1 -1 -2 - add r2 -5 -6 - mul r3 -4 r2 - add r4 -3 r3 - mul r5 r1 r4 - move r8 r5 #negationHell + move r8 -123 #negationHell " } ); diff --git a/rust_compiler/libs/compiler/src/test/logic_expression.rs b/rust_compiler/libs/compiler/src/test/logic_expression.rs index 55bdda6..1f65d4b 100644 --- a/rust_compiler/libs/compiler/src/test/logic_expression.rs +++ b/rust_compiler/libs/compiler/src/test/logic_expression.rs @@ -112,9 +112,8 @@ fn test_math_with_logic() -> anyhow::Result<()> { " j main main: - add r1 1 2 - sgt r2 r1 1 - move r8 r2 #logic + sgt r1 3 1 + move r8 r1 #logic " } ); diff --git a/rust_compiler/libs/compiler/src/test/syscall.rs b/rust_compiler/libs/compiler/src/test/syscall.rs index 7433880..ec323e5 100644 --- a/rust_compiler/libs/compiler/src/test/syscall.rs +++ b/rust_compiler/libs/compiler/src/test/syscall.rs @@ -88,7 +88,7 @@ fn test_set_on_device_batched() -> anyhow::Result<()> { let compiled = compile! { debug r#" - let doorHash = hash("Door"); + const doorHash = hash("Door"); setOnDeviceBatched(doorHash, "Lock", true); "# }; @@ -99,9 +99,7 @@ fn test_set_on_device_batched() -> anyhow::Result<()> { r#" j main main: - move r15 HASH("Door") #hash_ret - move r8 r15 #doorHash - sb r8 Lock 1 + sb 718797587 Lock 1 "# } ); @@ -133,27 +131,3 @@ fn test_load_from_device() -> anyhow::Result<()> { Ok(()) } - -#[test] -fn test_hash() -> anyhow::Result<()> { - let compiled = compile! { - debug - r#" - let nameHash = hash("testValue"); - "# - }; - - assert_eq!( - compiled, - indoc! { - r#" - j main - main: - move r15 HASH("testValue") #hash_ret - move r8 r15 #nameHash - "# - } - ); - - Ok(()) -} diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index 7f5b553..236e20f 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -1,6 +1,6 @@ #![allow(clippy::result_large_err)] use crate::variable_manager::{self, LocationRequest, VariableLocation, VariableScope}; -use crc32fast::hash as crc32_hash; +use helpers::prelude::*; use parser::{ Parser as ASTParser, sys_call::{SysCall, System}, @@ -559,15 +559,24 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { let result = self.expression_binary(bin_expr, scope)?; let var_loc = scope.add_variable(&name_str, LocationRequest::Persist)?; - // Move result from temp to new persistent variable - let result_reg = self.resolve_register(&result.location)?; - self.emit_variable_assignment(&name_str, &var_loc, result_reg)?; + if let CompilationResult { + location: VariableLocation::Constant(Literal::Number(num)), + .. + } = result + { + self.emit_variable_assignment(&name_str, &var_loc, num)?; + (var_loc, None) + } else { + // Move result from temp to new persistent variable + let result_reg = self.resolve_register(&result.location)?; + self.emit_variable_assignment(&name_str, &var_loc, result_reg)?; - // Free the temp result - if let Some(name) = result.temp_name { - scope.free_temp(name)?; + // Free the temp result + if let Some(name) = result.temp_name { + scope.free_temp(name)?; + } + (var_loc, None) } - (var_loc, None) } Expression::Logical(log_expr) => { let result = self.expression_logical(log_expr, scope)?; @@ -686,14 +695,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { LiteralOr::Or(Spanned { node: SysCall::System(System::Hash(Literal::String(str_to_hash))), .. - }) => { - let hash = crc32_hash(str_to_hash.as_bytes()); - - // in stationeers, crc32 is a SIGNED int. - let hash_value_i32 = i32::from_le_bytes(hash.to_le_bytes()); - - Literal::Number(Number::Integer(hash_value_i32 as i128)) - } + }) => Literal::Number(Number::Integer(crc_hash_signed(&str_to_hash))), LiteralOr::Or(Spanned { span, .. }) => { return Err(Error::Unknown( "hash only supports string literals in this context.".into(), @@ -1232,6 +1234,58 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { expr: Spanned, scope: &mut VariableScope<'v>, ) -> Result { + fn fold_binary_expression(expr: &BinaryExpression) -> Option { + let (lhs, rhs) = match &expr { + BinaryExpression::Add(l, r) + | BinaryExpression::Subtract(l, r) + | BinaryExpression::Multiply(l, r) + | BinaryExpression::Divide(l, r) + | BinaryExpression::Exponent(l, r) + | BinaryExpression::Modulo(l, r) => (fold_expression(l)?, fold_expression(r)?), + }; + + match expr { + BinaryExpression::Add(..) => Some(lhs + rhs), + BinaryExpression::Subtract(..) => Some(lhs - rhs), + BinaryExpression::Multiply(..) => Some(lhs * rhs), + BinaryExpression::Divide(..) => Some(lhs / rhs), // Watch out for div by zero panics! + BinaryExpression::Modulo(..) => Some(lhs % rhs), + _ => None, // Handle Exponent separately or implement pow + } + } + + fn fold_expression(expr: &Expression) -> Option { + match expr { + // 1. Base Case: It's already a number + Expression::Literal(lit) => match lit.node { + Literal::Number(n) => Some(n), + _ => None, + }, + + // 2. Handle Parentheses: Just recurse deeper + Expression::Priority(inner) => fold_expression(&inner.node), + + // 3. Handle Negation: Recurse, then negate + Expression::Negation(inner) => { + let val = fold_expression(&inner.node)?; + Some(-val) // Requires impl Neg for Number + } + + // 4. Handle Binary Ops: Recurse BOTH sides, then combine + Expression::Binary(bin) => fold_binary_expression(&bin.node), + + // 5. Anything else (Variables, Function Calls) cannot be compile-time folded + _ => None, + } + } + + if let Some(const_lit) = fold_binary_expression(&expr.node) { + return Ok(CompilationResult { + location: VariableLocation::Constant(Literal::Number(const_lit)), + temp_name: None, + }); + }; + let (op_str, left_expr, right_expr) = match expr.node { BinaryExpression::Add(l, r) => ("add", l, r), BinaryExpression::Multiply(l, r) => ("mul", l, r), @@ -1553,8 +1607,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { )); }; - let loc = VariableLocation::Persistant(VariableScope::RETURN_REGISTER); - self.emit_variable_assignment("hash_ret", &loc, format!(r#"HASH("{}")"#, str_lit))?; + let loc = VariableLocation::Constant(Literal::Number(Number::Integer( + crc_hash_signed(&str_lit), + ))); Ok(Some(CompilationResult { location: loc, diff --git a/rust_compiler/libs/helpers/Cargo.toml b/rust_compiler/libs/helpers/Cargo.toml index cb9df2c..97e7548 100644 --- a/rust_compiler/libs/helpers/Cargo.toml +++ b/rust_compiler/libs/helpers/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +crc32fast = { workspace = true } diff --git a/rust_compiler/libs/helpers/src/helper_funcs.rs b/rust_compiler/libs/helpers/src/helper_funcs.rs new file mode 100644 index 0000000..3e68894 --- /dev/null +++ b/rust_compiler/libs/helpers/src/helper_funcs.rs @@ -0,0 +1,11 @@ +use crc32fast::hash as crc32_hash; +/// This function takes an input which is meant to be hashed via the CRC32 algorithm, but it then +/// converts the generated UNSIGNED number into it's SIGNED counterpart. +pub fn crc_hash_signed(input: &str) -> i128 { + let hash = crc32_hash(input.as_bytes()); + + // in stationeers, crc32 is a SIGNED int. + let hash_value_i32 = i32::from_le_bytes(hash.to_le_bytes()); + + hash_value_i32 as i128 +} diff --git a/rust_compiler/libs/helpers/src/lib.rs b/rust_compiler/libs/helpers/src/lib.rs index 40b9b5a..680897e 100644 --- a/rust_compiler/libs/helpers/src/lib.rs +++ b/rust_compiler/libs/helpers/src/lib.rs @@ -1,3 +1,4 @@ +mod helper_funcs; mod macros; mod syscall; @@ -11,5 +12,6 @@ pub trait Documentation { } pub mod prelude { + pub use super::helper_funcs::*; pub use super::{Documentation, documented, with_syscalls}; } diff --git a/rust_compiler/libs/tokenizer/src/token.rs b/rust_compiler/libs/tokenizer/src/token.rs index 7637bfe..9745ecd 100644 --- a/rust_compiler/libs/tokenizer/src/token.rs +++ b/rust_compiler/libs/tokenizer/src/token.rs @@ -173,6 +173,81 @@ pub enum Number { Decimal(Decimal), } +impl From for Decimal { + fn from(value: Number) -> Self { + match value { + Number::Decimal(d) => d, + Number::Integer(i) => Decimal::from(i), + } + } +} + +impl std::ops::Neg for Number { + type Output = Number; + + fn neg(self) -> Self::Output { + match self { + Self::Integer(i) => Self::Integer(-i), + Self::Decimal(d) => Self::Decimal(-d), + } + } +} + +impl std::ops::Add for Number { + type Output = Number; + + fn add(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Self::Integer(l), Self::Integer(r)) => Number::Integer(l + r), + (Self::Decimal(l), Self::Decimal(r)) => Number::Decimal(l + r), + (Self::Integer(l), Self::Decimal(r)) => Number::Decimal(Decimal::from(l) + r), + (Self::Decimal(l), Self::Integer(r)) => Number::Decimal(l + Decimal::from(r)), + } + } +} + +impl std::ops::Sub for Number { + type Output = Number; + + fn sub(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Self::Integer(l), Self::Integer(r)) => Self::Integer(l - r), + (Self::Decimal(l), Self::Integer(r)) => Self::Decimal(l - Decimal::from(r)), + (Self::Integer(l), Self::Decimal(r)) => Self::Decimal(Decimal::from(l) - r), + (Self::Decimal(l), Self::Decimal(r)) => Self::Decimal(l - r), + } + } +} + +impl std::ops::Mul for Number { + type Output = Number; + + fn mul(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (Number::Integer(l), Number::Integer(r)) => Number::Integer(l * r), + (Number::Integer(l), Number::Decimal(r)) => Number::Decimal(Decimal::from(l) * r), + (Number::Decimal(l), Number::Integer(r)) => Number::Decimal(l * Decimal::from(r)), + (Number::Decimal(l), Number::Decimal(r)) => Number::Decimal(l * r), + } + } +} + +impl std::ops::Div for Number { + type Output = Number; + + fn div(self, rhs: Self) -> Self::Output { + Number::Decimal(Decimal::from(self) / Decimal::from(rhs)) + } +} + +impl std::ops::Rem for Number { + type Output = Number; + + fn rem(self, rhs: Self) -> Self::Output { + Number::Decimal(Decimal::from(self) % Decimal::from(rhs)) + } +} + impl std::fmt::Display for Number { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self {