diff --git a/rust_compiler/libs/compiler/src/test/syscall.rs b/rust_compiler/libs/compiler/src/test/syscall.rs index 22ce165..f894eb3 100644 --- a/rust_compiler/libs/compiler/src/test/syscall.rs +++ b/rust_compiler/libs/compiler/src/test/syscall.rs @@ -287,3 +287,68 @@ fn test_load_reagent() -> anyhow::Result<()> { Ok(()) } +#[test] +fn test_clr() -> anyhow::Result<()> { + let compiled = compile! { + check + " + device stackDevice = \"d0\"; + clr(stackDevice); + let deviceRef = 5; + clr(deviceRef); + " + }; + + assert!( + compiled.errors.is_empty(), + "Expected no errors, got: {:?}", + compiled.errors + ); + + assert_eq!( + compiled.output, + indoc! { + " + j main + main: + clr d0 + move r8 5 + clr r8 + " + } + ); + + Ok(()) +} +#[test] +fn test_rmap() -> anyhow::Result<()> { + let compiled = compile! { + check + " + device printer = \"d0\"; + let reagentHash = 12345; + let itemHash = rmap(printer, reagentHash); + " + }; + + assert!( + compiled.errors.is_empty(), + "Expected no errors, got: {:?}", + compiled.errors + ); + + assert_eq!( + compiled.output, + indoc! { + " + j main + main: + move r8 12345 + rmap r15 d0 r8 + move r9 r15 + " + } + ); + + Ok(()) +} diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index 6d7f523..9a0934f 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -2286,7 +2286,10 @@ impl<'a> Compiler<'a> { expr: Spanned>, scope: &mut VariableScope<'a, '_>, ) -> Result, Error<'a>> { - fn fold_binary_expression<'a>(expr: &BinaryExpression<'a>) -> Option { + fn fold_binary_expression<'a>( + expr: &BinaryExpression<'a>, + scope: &VariableScope<'a, '_>, + ) -> Option { fn number_to_i64(n: Number) -> Option { match n { Number::Integer(i, _) => i64::try_from(i).ok(), @@ -2315,7 +2318,7 @@ impl<'a> Compiler<'a> { | BinaryExpression::LeftShift(l, r) | BinaryExpression::RightShiftArithmetic(l, r) | BinaryExpression::RightShiftLogical(l, r) => { - (fold_expression(l)?, fold_expression(r)?) + (fold_expression(l, scope)?, fold_expression(r, scope)?) } }; @@ -2359,7 +2362,10 @@ impl<'a> Compiler<'a> { } } - fn fold_expression<'a>(expr: &Expression<'a>) -> Option { + fn fold_expression<'a>( + expr: &Expression<'a>, + scope: &VariableScope<'a, '_>, + ) -> Option { match expr { // 1. Base Case: It's already a number Expression::Literal(lit) => match lit.node { @@ -2368,18 +2374,28 @@ impl<'a> Compiler<'a> { }, // 2. Handle Parentheses: Just recurse deeper - Expression::Priority(inner) => fold_expression(&inner.node), + Expression::Priority(inner) => fold_expression(&inner.node, scope), // 3. Handle Negation: Recurse, then negate Expression::Negation(inner) => { - let val = fold_expression(&inner.node)?; + let val = fold_expression(&inner.node, scope)?; Some(-val) // Requires impl Neg for Number } // 4. Handle Binary Ops: Recurse BOTH sides, then combine - Expression::Binary(bin) => fold_binary_expression(&bin.node), + Expression::Binary(bin) => fold_binary_expression(&bin.node, scope), - // 5. Handle hash() syscall - evaluates to a constant at compile time + // 5. Handle Variable Reference: Check if it's a const + Expression::Variable(var_id) => { + if let Ok(var_loc) = scope.get_location_of(var_id, None) { + if let VariableLocation::Constant(Literal::Number(num)) = var_loc { + return Some(num); + } + } + None + } + + // 6. Handle hash() syscall - evaluates to a constant at compile time Expression::Syscall(Spanned { node: SysCall::System(System::Hash(Spanned { @@ -2391,7 +2407,7 @@ impl<'a> Compiler<'a> { return Some(Number::Integer(crc_hash_signed(str_to_hash), Unit::None)); } - // 6. Handle hash() macro as invocation - evaluates to a constant at compile time + // 7. Handle hash() macro as invocation - evaluates to a constant at compile time Expression::Invocation(inv) => { if inv.node.name.node == "hash" && inv.node.arguments.len() == 1 { if let Expression::Literal(Spanned { @@ -2406,12 +2422,12 @@ impl<'a> Compiler<'a> { None } - // 7. Anything else cannot be compile-time folded + // 8. Anything else cannot be compile-time folded _ => None, } } - if let Some(const_lit) = fold_binary_expression(&expr.node) { + if let Some(const_lit) = fold_binary_expression(&expr.node, scope) { return Ok(CompileLocation { location: VariableLocation::Constant(Literal::Number(const_lit)), temp_name: None, @@ -2925,6 +2941,13 @@ impl<'a> Compiler<'a> { cleanup!(var_cleanup); Ok(None) } + System::Clr(device) => { + let (op, var_cleanup) = self.compile_operand(*device, scope)?; + self.write_instruction(Instruction::Clr(op), Some(span))?; + + cleanup!(var_cleanup); + Ok(None) + } System::Hash(hash_arg) => { let Spanned { node: Literal::String(str_lit), @@ -3244,6 +3267,42 @@ impl<'a> Compiler<'a> { cleanup!(reagent_cleanup, reagent_hash_cleanup, device_cleanup); + Ok(Some(CompileLocation { + location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER), + temp_name: None, + })) + } + System::Rmap(device, reagent_hash) => { + let Spanned { + node: LiteralOrVariable::Variable(device_spanned), + .. + } = device + else { + return Err(Error::AgrumentMismatch( + "Arg1 expected to be a variable".into(), + span, + )); + }; + + let (device, device_cleanup) = self.compile_literal_or_variable( + LiteralOrVariable::Variable(device_spanned), + scope, + )?; + + let (reagent_hash, reagent_hash_cleanup) = + self.compile_operand(*reagent_hash, scope)?; + + self.write_instruction( + Instruction::Rmap( + Operand::Register(VariableScope::RETURN_REGISTER), + device, + reagent_hash, + ), + Some(span), + )?; + + cleanup!(reagent_hash_cleanup, device_cleanup); + Ok(Some(CompileLocation { location: VariableLocation::Persistant(VariableScope::RETURN_REGISTER), temp_name: None, diff --git a/rust_compiler/libs/helpers/src/syscall.rs b/rust_compiler/libs/helpers/src/syscall.rs index e329129..b01f615 100644 --- a/rust_compiler/libs/helpers/src/syscall.rs +++ b/rust_compiler/libs/helpers/src/syscall.rs @@ -5,12 +5,14 @@ macro_rules! with_syscalls { // Big names "yield", "sleep", + "clr", "hash", "load", "loadBatched", "loadBatchedNamed", "loadSlot", "loadReagent", + "rmap", "set", "setBatched", "setBatchedNamed", diff --git a/rust_compiler/libs/il/src/lib.rs b/rust_compiler/libs/il/src/lib.rs index 7a0554d..5520d7e 100644 --- a/rust_compiler/libs/il/src/lib.rs +++ b/rust_compiler/libs/il/src/lib.rs @@ -195,6 +195,9 @@ pub enum Instruction<'a> { /// `lr register device reagentMode int` LoadReagent(Operand<'a>, Operand<'a>, Operand<'a>, Operand<'a>), + /// `rmap register device reagentHash` - Resolve Reagent to Item Hash + Rmap(Operand<'a>, Operand<'a>, Operand<'a>), + /// `j label` - Unconditional Jump Jump(Operand<'a>), /// `jal label` - Jump and Link (Function Call) @@ -267,6 +270,8 @@ pub enum Instruction<'a> { Yield, /// `sleep val` - Sleep for seconds Sleep(Operand<'a>), + /// `clr val` - Clear stack memory on device + Clr(Operand<'a>), /// `alias name target` - Define Alias (Usually handled by compiler, but good for IR) Alias(Cow<'a, str>, Operand<'a>), @@ -328,6 +333,9 @@ impl<'a> fmt::Display for Instruction<'a> { Instruction::LoadReagent(reg, device, reagent_mode, reagent_hash) => { write!(f, "lr {} {} {} {}", reg, device, reagent_mode, reagent_hash) } + Instruction::Rmap(reg, device, reagent_hash) => { + write!(f, "rmap {} {} {}", reg, device, reagent_hash) + } Instruction::Jump(lbl) => write!(f, "j {}", lbl), Instruction::JumpAndLink(lbl) => write!(f, "jal {}", lbl), Instruction::JumpRelative(off) => write!(f, "jr {}", off), @@ -363,6 +371,7 @@ impl<'a> fmt::Display for Instruction<'a> { } Instruction::Yield => write!(f, "yield"), Instruction::Sleep(val) => write!(f, "sleep {}", val), + Instruction::Clr(val) => write!(f, "clr {}", val), Instruction::Alias(name, target) => write!(f, "alias {} {}", name, target), Instruction::Define(name, val) => write!(f, "define {} {}", name, val), Instruction::LabelDef(lbl) => write!(f, "{}:", lbl), diff --git a/rust_compiler/libs/integration_tests/src/bitwise_tests.rs b/rust_compiler/libs/integration_tests/src/bitwise_tests.rs index eada160..cd6df21 100644 --- a/rust_compiler/libs/integration_tests/src/bitwise_tests.rs +++ b/rust_compiler/libs/integration_tests/src/bitwise_tests.rs @@ -90,4 +90,23 @@ mod bitwise_tests { let output = compile_with_and_without_optimization(source); insta::assert_snapshot!(output); } + + #[test] + fn test_bitwise_with_const() { + let source = indoc! {r#" + device sorterOutput = "d0"; + + const ingotSortClass = 19; + const equals = 0; + + loop { + yield(); + clr(sorterOutput); + sorterOutput[0] = (ingotSortClass << 16) | (equals << 8) | 3; + } + "#}; + + let output = compile_with_and_without_optimization(source); + insta::assert_snapshot!(output); + } } diff --git a/rust_compiler/libs/integration_tests/src/snapshots/integration_tests__bitwise_tests__bitwise_tests__bitwise_with_const.snap b/rust_compiler/libs/integration_tests/src/snapshots/integration_tests__bitwise_tests__bitwise_tests__bitwise_with_const.snap new file mode 100644 index 0000000..897a733 --- /dev/null +++ b/rust_compiler/libs/integration_tests/src/snapshots/integration_tests__bitwise_tests__bitwise_tests__bitwise_with_const.snap @@ -0,0 +1,21 @@ +--- +source: libs/integration_tests/src/bitwise_tests.rs +expression: output +--- +## Unoptimized Output + +j main +main: +__internal_L1: +yield +clr d0 +put d0 0 1245187 +j __internal_L1 +__internal_L2: + +## Optimized Output + +yield +clr d0 +put d0 0 1245187 +j 0 diff --git a/rust_compiler/libs/optimizer/src/helpers.rs b/rust_compiler/libs/optimizer/src/helpers.rs index 6bb8d23..efe5350 100644 --- a/rust_compiler/libs/optimizer/src/helpers.rs +++ b/rust_compiler/libs/optimizer/src/helpers.rs @@ -43,6 +43,7 @@ pub fn get_destination_reg(instr: &Instruction) -> Option { | Instruction::Tan(Operand::Register(r), _) | Instruction::Trunc(Operand::Register(r), _) | Instruction::LoadReagent(Operand::Register(r), _, _, _) + | Instruction::Rmap(Operand::Register(r), _, _) | Instruction::Pop(Operand::Register(r)) => Some(*r), _ => None, } @@ -107,6 +108,7 @@ pub fn set_destination_reg<'a>(instr: &Instruction<'a>, new_reg: u8) -> Option Some(Instruction::Sqrt(r, a.clone())), Instruction::Tan(_, a) => Some(Instruction::Tan(r, a.clone())), Instruction::Trunc(_, a) => Some(Instruction::Trunc(r, a.clone())), + Instruction::Rmap(_, a, b) => Some(Instruction::Rmap(r, a.clone(), b.clone())), _ => None, } } @@ -136,6 +138,7 @@ pub fn reg_is_read(instr: &Instruction, reg: u8) -> bool { | Instruction::BranchLe(a, b, _) => check(a) || check(b), Instruction::BranchEqZero(a, _) | Instruction::BranchNeZero(a, _) => check(a), Instruction::LoadReagent(_, device, _, item_hash) => check(device) || check(item_hash), + Instruction::Rmap(_, device, reagent_hash) => check(device) || check(reagent_hash), Instruction::LoadSlot(_, dev, slot, _) => check(dev) || check(slot), Instruction::LoadBatch(_, dev, _, mode) => check(dev) || check(mode), Instruction::LoadBatchNamed(_, d_hash, n_hash, _, mode) => { diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index 435319f..3c3b3c0 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -1960,6 +1960,11 @@ impl<'a> Parser<'a> { let expr = args.next().ok_or_else(|| self.unexpected_eof())?; Ok(SysCall::System(System::Sleep(boxed!(expr)))) } + "clr" => { + let mut args = args!(1); + let expr = args.next().ok_or_else(|| self.unexpected_eof())?; + Ok(SysCall::System(System::Clr(boxed!(expr)))) + } "hash" => { let mut args = args!(1); let lit_str = literal_or_variable!(args.next()); @@ -2191,6 +2196,17 @@ impl<'a> Parser<'a> { Box::new(reagent_hash), ))) } + "rmap" => { + let mut args = args!(2); + let next = args.next(); + let device = literal_or_variable!(next); + let reagent_hash = args.next().ok_or_else(|| self.unexpected_eof())?; + + Ok(SysCall::System(System::Rmap( + device, + Box::new(reagent_hash), + ))) + } // Math SysCalls "acos" => { diff --git a/rust_compiler/libs/parser/src/sys_call.rs b/rust_compiler/libs/parser/src/sys_call.rs index 67e03a9..69edce6 100644 --- a/rust_compiler/libs/parser/src/sys_call.rs +++ b/rust_compiler/libs/parser/src/sys_call.rs @@ -142,6 +142,12 @@ documented! { /// ## Slang /// `sleep(number|var);` Sleep(Box>>), + /// Clears stack memory on the provided device. + /// ## IC10 + /// `clr d?` + /// ## Slang + /// `clr(device);` + Clr(Box>>), /// Gets the in-game hash for a specific prefab name. NOTE! This call is COMPLETELY /// optimized away unless you bind it to a `let` variable. If you use a `const` variable /// however, the hash is correctly computed at compile time and substitued automatically. @@ -249,6 +255,17 @@ documented! { Spanned>, Spanned>, Box>> + ), + /// Maps a reagent hash to the item hash that fulfills it on a device + /// + /// ## IC10 + /// `rmap r? d? reagentHash(r?|num)` + /// ## Slang + /// `let itemHash = rmap(device, reagentHash);` + /// `let itemHash = rmap(device, reagentHashValue);` + Rmap( + Spanned>, + Box>> ) } } @@ -258,6 +275,7 @@ impl<'a> std::fmt::Display for System<'a> { match self { System::Yield => write!(f, "yield()"), System::Sleep(a) => write!(f, "sleep({})", a), + System::Clr(a) => write!(f, "clr({})", a), System::Hash(a) => write!(f, "hash({})", a), System::LoadFromDevice(a, b) => write!(f, "loadFromDevice({}, {})", a, b), System::LoadBatch(a, b, c) => write!(f, "loadBatch({}, {}, {})", a, b, c), @@ -274,6 +292,7 @@ impl<'a> std::fmt::Display for System<'a> { System::LoadSlot(a, b, c) => write!(f, "loadSlot({}, {}, {})", a, b, c), System::SetSlot(a, b, c, d) => write!(f, "setSlot({}, {}, {}, {})", a, b, c, d), System::LoadReagent(a, b, c) => write!(f, "loadReagent({}, {}, {})", a, b, c), + System::Rmap(a, b) => write!(f, "rmap({}, {})", a, b), } } }