diff --git a/rust_compiler/libs/compiler/src/test/mod.rs b/rust_compiler/libs/compiler/src/test/mod.rs index 011e834..68e35e9 100644 --- a/rust_compiler/libs/compiler/src/test/mod.rs +++ b/rust_compiler/libs/compiler/src/test/mod.rs @@ -12,18 +12,16 @@ macro_rules! compile { let mut writer = std::io::BufWriter::new(Vec::new()); let compiler = ::Compiler::new( parser::Parser::new(tokenizer::Tokenizer::from(String::from($source))), - &mut writer, None, ); - compiler.compile(); + let res = compiler.compile(); + res.instructions.write(&mut writer)?; output!(writer) }}; (result $source:expr) => {{ - let mut writer = std::io::BufWriter::new(Vec::new()); let compiler = crate::Compiler::new( parser::Parser::new(tokenizer::Tokenizer::from($source)), - &mut writer, Some(crate::CompilerConfig { debug: true }), ); compiler.compile().errors @@ -33,10 +31,10 @@ macro_rules! compile { let mut writer = std::io::BufWriter::new(Vec::new()); let compiler = crate::Compiler::new( parser::Parser::new(tokenizer::Tokenizer::from($source)), - &mut writer, Some(crate::CompilerConfig { debug: true }), ); - compiler.compile(); + let res = compiler.compile(); + res.instructions.write(&mut writer)?; output!(writer) }}; } diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index c54fac0..e5647fb 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -1,7 +1,7 @@ #![allow(clippy::result_large_err)] use crate::variable_manager::{self, LocationRequest, VariableLocation, VariableScope}; use helpers::{Span, prelude::*}; -use il::{Instruction, InstructionNode, Operand}; +use il::{Instruction, InstructionNode, Instructions, Operand}; use parser::{ Parser as ASTParser, sys_call::{Math, SysCall, System}, @@ -13,11 +13,7 @@ use parser::{ }, }; use rust_decimal::Decimal; -use std::{ - borrow::Cow, - collections::HashMap, - io::{BufWriter, Write}, -}; +use std::{borrow::Cow, collections::HashMap}; use thiserror::Error; use tokenizer::token::Number; @@ -139,24 +135,22 @@ struct CompileLocation<'a> { pub struct CompilationResult<'a> { pub errors: Vec>, - pub source_map: HashMap>, - pub instructions: Vec>, + pub instructions: Instructions<'a>, } -pub struct Compiler<'a, 'w, W: std::io::Write> { +pub struct Compiler<'a> { pub parser: ASTParser<'a>, function_locations: HashMap, usize>, function_metadata: HashMap, Vec>>, devices: HashMap, Cow<'a, str>>, - output: &'w mut BufWriter, // This holds the IL code which will be used in the // optimizer - pub instructions: Vec>, + pub instructions: Instructions<'a>, current_line: usize, declared_main: bool, - config: CompilerConfig, + _config: CompilerConfig, temp_counter: usize, label_counter: usize, loop_stack: Vec<(Cow<'a, str>, Cow<'a, str>)>, // Stores (start_label, end_label) @@ -167,22 +161,17 @@ pub struct Compiler<'a, 'w, W: std::io::Write> { pub errors: Vec>, } -impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { - pub fn new( - parser: ASTParser<'a>, - writer: &'w mut BufWriter, - config: Option, - ) -> Self { +impl<'a> Compiler<'a> { + pub fn new(parser: ASTParser<'a>, config: Option) -> Self { Self { parser, function_locations: HashMap::new(), function_metadata: HashMap::new(), devices: HashMap::new(), - output: writer, - instructions: Vec::new(), + instructions: Instructions::default(), current_line: 1, declared_main: false, - config: config.unwrap_or_default(), + _config: config.unwrap_or_default(), temp_counter: 0, label_counter: 0, loop_stack: Vec::new(), @@ -205,7 +194,6 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { Ok(Some(expr)) => expr, Ok(None) => { return CompilationResult { - source_map: self.source_map, errors: self.errors, instructions: self.instructions, }; @@ -215,7 +203,6 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { self.errors.push(Error::Parse(e)); return CompilationResult { errors: self.errors, - source_map: self.source_map, instructions: self.instructions, }; } @@ -241,7 +228,6 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { self.errors.push(e); return CompilationResult { errors: self.errors, - source_map: self.source_map, instructions: self.instructions, }; } @@ -255,7 +241,6 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { CompilationResult { errors: self.errors, - source_map: self.source_map, instructions: self.instructions, } } @@ -266,8 +251,6 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { instr: Instruction<'a>, span: Option, ) -> Result<(), Error<'a>> { - self.output.write_all(format!("{}", instr).as_bytes())?; - self.output.write_all(b"\n")?; self.current_line += 1; self.instructions.push(InstructionNode::new(instr, span)); @@ -781,7 +764,6 @@ impl<'a, 'w, W: std::io::Write> Compiler<'a, 'w, W> { } Expression::Ternary(ternary) => { let res = self.expression_ternary(ternary.node, scope)?; - println!("{res:?}"); let var_loc = scope.add_variable( name_str.clone(), LocationRequest::Persist, diff --git a/rust_compiler/libs/il/src/lib.rs b/rust_compiler/libs/il/src/lib.rs index e8a33f9..fae551c 100644 --- a/rust_compiler/libs/il/src/lib.rs +++ b/rust_compiler/libs/il/src/lib.rs @@ -1,13 +1,77 @@ use helpers::Span; use rust_decimal::Decimal; use std::borrow::Cow; +use std::collections::HashMap; use std::fmt; +use std::io::{BufWriter, Write}; +use std::ops::{Deref, DerefMut}; + +#[derive(Default)] +pub struct Instructions<'a>(Vec>); + +impl<'a> Deref for Instructions<'a> { + type Target = Vec>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> DerefMut for Instructions<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'a> Instructions<'a> { + pub fn new(instructions: Vec>) -> Self { + Self(instructions) + } + pub fn into_inner(self) -> Vec> { + self.0 + } + pub fn write(self, writer: &mut BufWriter) -> Result<(), std::io::Error> { + for node in self.0 { + writer.write_all(node.to_string().as_bytes())?; + writer.write_all(b"\n")?; + } + + writer.flush()?; + Ok(()) + } + pub fn source_map(&self) -> HashMap { + let mut map = HashMap::new(); + + for (line_num, node) in self.0.iter().enumerate() { + if let Some(span) = node.span { + map.insert(line_num, span); + } + } + + map + } +} + +impl<'a> std::fmt::Display for Instructions<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for node in &self.0 { + writeln!(f, "{node}")?; + } + Ok(()) + } +} pub struct InstructionNode<'a> { pub instruction: Instruction<'a>, pub span: Option, } +impl<'a> std::fmt::Display for InstructionNode<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.instruction) + } +} + impl<'a> InstructionNode<'a> { pub fn new(instr: Instruction<'a>, span: Option) -> Self { Self { diff --git a/rust_compiler/libs/optimizer/src/lib.rs b/rust_compiler/libs/optimizer/src/lib.rs index aaac6a4..2204488 100644 --- a/rust_compiler/libs/optimizer/src/lib.rs +++ b/rust_compiler/libs/optimizer/src/lib.rs @@ -1,4 +1,4 @@ -use il::{Instruction, InstructionNode, Operand}; +use il::{Instruction, InstructionNode, Instructions, Operand}; use rust_decimal::Decimal; use std::collections::{HashMap, HashSet}; @@ -6,7 +6,8 @@ mod leaf_function; use leaf_function::find_leaf_functions; /// Entry point for the optimizer. -pub fn optimize<'a>(mut instructions: Vec>) -> Vec> { +pub fn optimize<'a>(instructions: Instructions<'a>) -> Instructions<'a> { + let mut instructions = instructions.into_inner(); let mut changed = true; let mut pass_count = 0; const MAX_PASSES: usize = 10; @@ -49,13 +50,35 @@ pub fn optimize<'a>(mut instructions: Vec>) -> Vec bool { + for instruction in instructions.iter().take(end_idx).skip(start_idx) { + match instruction.instruction { + Instruction::Push(_) | Instruction::Pop(_) => return true, + // Check for explicit SP modification + Instruction::Add(Operand::StackPointer, _, _) + | Instruction::Sub(Operand::StackPointer, _, _) + | Instruction::Mul(Operand::StackPointer, _, _) + | Instruction::Div(Operand::StackPointer, _, _) + | Instruction::Move(Operand::StackPointer, _) => return true, + _ => {} + } + } + false } /// Pass: Leaf Function Optimization /// If a function makes no calls (is a leaf), it doesn't need to save/restore `ra`. fn optimize_leaf_functions<'a>( - mut input: Vec>, + input: Vec>, ) -> (Vec>, bool) { let leaves = find_leaf_functions(&input); if leaves.is_empty() { @@ -64,40 +87,44 @@ fn optimize_leaf_functions<'a>( let mut changed = false; let mut to_remove = HashSet::new(); - let mut current_function: Option = None; - // Map of FunctionName -> The stack offset where RA was stored. - // We need this to adjust other stack accesses (arguments vs locals). + // We map function names to the INDEX of the instruction that restores RA. + // We use this to validate the function body later. + let mut func_restore_indices = HashMap::new(); let mut func_ra_offsets = HashMap::new(); + let mut current_function: Option = None; + let mut function_start_indices = HashMap::new(); + // First scan: Identify instructions to remove and capture RA offsets for (i, node) in input.iter().enumerate() { match &node.instruction { Instruction::LabelDef(label) => { current_function = Some(label.to_string()); + function_start_indices.insert(label.to_string(), i); } Instruction::Push(Operand::ReturnAddress) => { - if let Some(func) = ¤t_function { - if leaves.contains(func) { - to_remove.insert(i); - changed = true; - } + if let Some(func) = ¤t_function + && leaves.contains(func) + { + to_remove.insert(i); } } Instruction::Get(Operand::ReturnAddress, _, Operand::Register(_)) => { // This is the restore instruction: `get ra db r0` - if let Some(func) = ¤t_function { - if leaves.contains(func) { - to_remove.insert(i); - // Look back for the address calc: `sub r0 sp OFFSET` - if i > 0 { - if let Instruction::Sub(_, Operand::StackPointer, Operand::Number(n)) = - &input[i - 1].instruction - { - func_ra_offsets.insert(func.clone(), *n); - to_remove.insert(i - 1); - } - } + if let Some(func) = ¤t_function + && leaves.contains(func) + { + to_remove.insert(i); + func_restore_indices.insert(func.clone(), i); + + // Look back for the address calc: `sub r0 sp OFFSET` + if i > 0 + && let Instruction::Sub(_, Operand::StackPointer, Operand::Number(n)) = + &input[i - 1].instruction + { + func_ra_offsets.insert(func.clone(), *n); + to_remove.insert(i - 1); } } } @@ -105,54 +132,80 @@ fn optimize_leaf_functions<'a>( } } + // Safety Check: Verify that functions marked for optimization don't have complex stack ops. + // If they do, unmark them. + let mut safe_functions = HashSet::new(); + + for (func, start_idx) in &function_start_indices { + if let Some(restore_idx) = func_restore_indices.get(func) { + // Check instructions between start and restore using the helper function. + // We need to skip the `push ra` we just marked for removal, otherwise the helper + // will flag it as a complex op (Push). + // `start_idx` is the LabelDef. `start_idx + 1` is typically `push ra`. + + let check_start = if to_remove.contains(&(start_idx + 1)) { + start_idx + 2 + } else { + start_idx + 1 + }; + + // `restore_idx` points to the `get ra` instruction. The helper scans up to `end_idx` exclusive, + // so we don't need to worry about the restore instruction itself. + if !function_has_complex_stack_ops(&input, check_start, *restore_idx) { + safe_functions.insert(func.clone()); + changed = true; + } + } + } + if !changed { return (input, false); } - // Second scan: Rebuild with adjustments + // Second scan: Rebuild with adjustments, but only for SAFE functions let mut output = Vec::with_capacity(input.len()); let mut processing_function: Option = None; for (i, mut node) in input.into_iter().enumerate() { - if to_remove.contains(&i) { - continue; + if to_remove.contains(&i) + && let Some(func) = &processing_function + && safe_functions.contains(func) + { + continue; // SKIP (Remove) } if let Instruction::LabelDef(l) = &node.instruction { processing_function = Some(l.to_string()); } - // Apply Stack Adjustments if we are inside a leaf function that we optimized - if let Some(func) = &processing_function { - if let Some(ra_offset) = func_ra_offsets.get(func) { - // If this is the stack cleanup `sub sp sp N`, decrement N by 1 (since we removed push ra) - if let Instruction::Sub( - Operand::StackPointer, - Operand::StackPointer, - Operand::Number(n), - ) = &mut node.instruction - { - let new_n = *n - Decimal::from(1); - if new_n.is_zero() { - continue; // Remove instruction if 0 - } - *n = new_n; + // Apply Stack Adjustments + if let Some(func) = &processing_function + && safe_functions.contains(func) + && let Some(ra_offset) = func_ra_offsets.get(func) + { + // 1. Stack Cleanup Adjustment + if let Instruction::Sub( + Operand::StackPointer, + Operand::StackPointer, + Operand::Number(n), + ) = &mut node.instruction + { + // Decrease cleanup amount by 1 (for the removed RA) + let new_n = *n - Decimal::from(1); + if new_n.is_zero() { + continue; } + *n = new_n; + } - // Adjust stack variable accesses relative to the removed RA. - // Compiler layout: [Args] [RA] [Locals/Temps] - // Stack grows up (increment sp on push). - // Access is `sp - offset`. - // Deeper items (Args) have LARGER offsets than RA. - // Shallower items (Locals) have SMALLER offsets than RA. - // Since RA is gone, items deeper than RA (Args) effectively shift "down" (index - 1). - if let Instruction::Sub(_, Operand::StackPointer, Operand::Number(n)) = - &mut node.instruction - { - if *n > *ra_offset { - *n -= Decimal::from(1); - } - } + // 2. Stack Variable Offset Adjustment + // Since we verified the function is "Simple" (no nested stack mods), + // we can safely assume offsets > ra_offset need shifting. + if let Instruction::Sub(_, Operand::StackPointer, Operand::Number(n)) = + &mut node.instruction + && *n > *ra_offset + { + *n -= Decimal::from(1); } } @@ -173,16 +226,11 @@ fn analyze_clobbers(instructions: &[InstructionNode]) -> HashMap HashMap( - mut input: Vec>, + input: Vec>, ) -> (Vec>, bool) { let clobbers = analyze_clobbers(&input); let mut changed = false; let mut to_remove = HashSet::new(); - let mut stack_adjustments = HashMap::new(); // Index of `sub sp sp N` -> amount to subtract + let mut stack_adjustments = HashMap::new(); let mut i = 0; while i < input.len() { if let Instruction::JumpAndLink(Operand::Label(target)) = &input[i].instruction { let target_key = target.to_string(); - // If we don't have info on the function (e.g. extern or complex), skip + if let Some(func_clobbers) = clobbers.get(&target_key) { // 1. Identify Pushes immediately preceding the JAL let mut pushes = Vec::new(); // (index, register) @@ -221,54 +269,67 @@ fn optimize_function_calls<'a>( } // 2. Identify Restores immediately following the JAL - // Compiler emits: sub r0 sp Offset, get Reg db r0. let mut restores = Vec::new(); // (index_of_get, register, index_of_sub) let mut scan_fwd = i + 1; while scan_fwd < input.len() { - // Skip over the 'sub r0 sp X' address calculation lines + // Skip 'sub r0 sp X' if let Instruction::Sub(Operand::Register(0), Operand::StackPointer, _) = &input[scan_fwd].instruction { // Check next instruction for the Get - if scan_fwd + 1 < input.len() { - if let Instruction::Get(Operand::Register(r), _, Operand::Register(0)) = + if scan_fwd + 1 < input.len() + && let Instruction::Get(Operand::Register(r), _, Operand::Register(0)) = &input[scan_fwd + 1].instruction - { - restores.push((scan_fwd + 1, *r, scan_fwd)); - scan_fwd += 2; - continue; - } + { + restores.push((scan_fwd + 1, *r, scan_fwd)); + scan_fwd += 2; + continue; } } break; } - // 3. Check for Stack Cleanup `sub sp sp N` + // 3. Stack Cleanup let cleanup_idx = scan_fwd; - let has_cleanup = if cleanup_idx < input.len() { - if let Instruction::Sub( - Operand::StackPointer, - Operand::StackPointer, - Operand::Number(_), - ) = &input[cleanup_idx].instruction - { - true - } else { - false - } + matches!( + input[cleanup_idx].instruction, + Instruction::Sub( + Operand::StackPointer, + Operand::StackPointer, + Operand::Number(_) + ) + ) } else { false }; - // "All or Nothing" strategy for the safe subset: + // SAFEGUARD: Check Counts! + // If we pushed r8 twice but only restored it once, we have an argument. + // We must ensure the number of pushes for each register MATCHES the number of restores. + let mut push_counts = HashMap::new(); + for (_, r) in &pushes { + *push_counts.entry(*r).or_insert(0) += 1; + } + + let mut restore_counts = HashMap::new(); + for (_, r, _) in &restores { + *restore_counts.entry(*r).or_insert(0) += 1; + } + + let counts_match = push_counts + .iter() + .all(|(reg, count)| restore_counts.get(reg).unwrap_or(&0) == count); + // Also check reverse to ensure we didn't restore something we didn't push (unlikely but possible) + let counts_match_reverse = restore_counts + .iter() + .all(|(reg, count)| push_counts.get(reg).unwrap_or(&0) == count); + + // Clobber Check let all_pushes_safe = pushes.iter().all(|(_, r)| !func_clobbers.contains(r)); - let push_set: HashSet = pushes.iter().map(|(_, r)| *r).collect(); - let restore_set: HashSet = restores.iter().map(|(_, r, _)| *r).collect(); - - if all_pushes_safe && has_cleanup && push_set == restore_set { - // We can remove ALL saves/restores for this call! + if all_pushes_safe && has_cleanup && counts_match && counts_match_reverse { + // We can remove ALL found pushes/restores safely for (p_idx, _) in pushes { to_remove.insert(p_idx); } @@ -278,7 +339,7 @@ fn optimize_function_calls<'a>( } // Reduce stack cleanup amount - let num_removed = push_set.len() as i64; + let num_removed = push_counts.values().sum::() as i64; stack_adjustments.insert(cleanup_idx, num_removed); changed = true; } @@ -295,15 +356,14 @@ fn optimize_function_calls<'a>( } // Apply stack adjustment - if let Some(reduction) = stack_adjustments.get(&idx) { - if let Instruction::Sub(dst, a, Operand::Number(n)) = &node.instruction { - let new_n = n - Decimal::from(*reduction); - if new_n.is_zero() { - continue; // Remove the sub entirely if 0 - } - node.instruction = - Instruction::Sub(dst.clone(), a.clone(), Operand::Number(new_n)); + if let Some(reduction) = stack_adjustments.get(&idx) + && let Instruction::Sub(dst, a, Operand::Number(n)) = &node.instruction + { + let new_n = n - Decimal::from(*reduction); + if new_n.is_zero() { + continue; // Remove the sub entirely if 0 } + node.instruction = Instruction::Sub(dst.clone(), a.clone(), Operand::Number(new_n)); } clean.push(node); @@ -357,10 +417,16 @@ fn register_forwarding<'a>( break; } // If the temp is redefined, then the old value is dead, so we are safe. - if let Some(redef) = get_destination_reg(&node.instruction) { - if redef == temp_reg { - break; - } + if let Some(redef) = get_destination_reg(&node.instruction) + && redef == temp_reg + { + break; + } + + // Reg15 is a return register. + + if temp_reg == 15 { + break; } // If we hit a label/jump, we assume liveness might leak (conservative safety) if matches!( @@ -436,17 +502,17 @@ fn resolve_labels<'a>(input: Vec>) -> Vec { + Instruction::BranchEq(_, _, op) + | Instruction::BranchNe(_, _, op) + | Instruction::BranchGt(_, _, op) + | Instruction::BranchLt(_, _, op) + | Instruction::BranchGe(_, _, op) + | Instruction::BranchLe(_, _, op) => { if let Some(num) = get_line(op) { *op = num; } } - Instruction::BranchEqZero(a, op) | Instruction::BranchNeZero(a, op) => { + Instruction::BranchEqZero(_, op) | Instruction::BranchNeZero(_, op) => { if let Some(num) = get_line(op) { *op = num; } @@ -634,8 +700,7 @@ fn reg_is_read(instr: &Instruction, reg: u8) -> bool { } } -// --- Constant Propagation & Dead Code (Same as before) --- - +/// --- Constant Propagation & Dead Code --- fn constant_propagation<'a>(input: Vec>) -> (Vec>, bool) { let mut output = Vec::with_capacity(input.len()); let mut changed = false; @@ -648,13 +713,8 @@ fn constant_propagation<'a>(input: Vec>) -> (Vec { - if let Some(val) = resolve_value(src, ®isters) { - Some(Instruction::Move(dst.clone(), Operand::Number(val))) - } else { - None - } - } + Instruction::Move(dst, src) => resolve_value(src, ®isters) + .map(|val| Instruction::Move(dst.clone(), Operand::Number(val))), Instruction::Add(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| x + y), Instruction::Sub(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| x - y), Instruction::Mul(dst, a, b) => try_fold_math(dst, a, b, ®isters, |x, y| x * y), @@ -718,11 +778,11 @@ fn constant_propagation<'a>(input: Vec>) -> (Vec(input: Vec>) -> (Vec( changed = true; continue; } - match node.instruction { - Instruction::Jump(_) | Instruction::Jump(Operand::ReturnAddress) => dead = true, - _ => {} + if let Instruction::Jump(_) = node.instruction { + dead = true } output.push(node); } diff --git a/rust_compiler/src/ffi/mod.rs b/rust_compiler/src/ffi/mod.rs index f302a53..76195a3 100644 --- a/rust_compiler/src/ffi/mod.rs +++ b/rust_compiler/src/ffi/mod.rs @@ -1,9 +1,8 @@ use compiler::{CompilationResult, Compiler}; use helpers::{Documentation, Span}; -use optimizer::optimize; use parser::{sys_call::SysCall, Parser}; use safer_ffi::prelude::*; -use std::io::{BufWriter, Write}; +use std::io::BufWriter; use tokenizer::{ token::{Token, TokenType}, Tokenizer, @@ -128,33 +127,32 @@ pub fn free_docs_vec(v: safer_ffi::Vec) { pub fn compile_from_string(input: safer_ffi::slice::Ref<'_, u16>) -> FfiCompilationResult { let res = std::panic::catch_unwind(|| { let input = String::from_utf16_lossy(input.as_slice()); - let mut tmp = BufWriter::new(Vec::new()); let tokenizer = Tokenizer::from(input.as_str()); let parser = Parser::new(tokenizer); - let compiler = Compiler::new(parser, &mut tmp, None); + let compiler = Compiler::new(parser, None); let res = compiler.compile(); if !res.errors.is_empty() { - return (safer_ffi::String::EMPTY, res.source_map); + return (safer_ffi::String::EMPTY, res.instructions.source_map()); } let mut writer = BufWriter::new(Vec::new()); - for instruction in optimize(res.instructions) { - _ = writer.write_all(instruction.instruction.to_string().as_bytes()); - _ = writer.write_all(b"\n"); - } + // writing into a Vec. This should not fail. + let optimized = optimizer::optimize(res.instructions); + let map = optimized.source_map(); + _ = optimized.write(&mut writer); let Ok(compiled_vec) = writer.into_inner() else { - return (safer_ffi::String::EMPTY, res.source_map); + return (safer_ffi::String::EMPTY, map); }; // Safety: I know the compiler only outputs valid utf8 ( safer_ffi::String::from(unsafe { String::from_utf8_unchecked(compiled_vec) }), - res.source_map, + map, ) }); @@ -162,13 +160,9 @@ pub fn compile_from_string(input: safer_ffi::slice::Ref<'_, u16>) -> FfiCompilat FfiCompilationResult { source_map: source_map .into_iter() - .flat_map(|(k, v)| { - v.into_iter() - .map(|span| FfiSourceMapEntry { - span: span.into(), - line_number: k as u32, - }) - .collect::>() + .map(|(line_num, span)| FfiSourceMapEntry { + span: span.into(), + line_number: line_num as u32, }) .collect::>() .into(), @@ -244,9 +238,8 @@ pub fn diagnose_source(input: safer_ffi::slice::Ref<'_, u16>) -> safer_ffi::Vec< let res = std::panic::catch_unwind(|| { let input = String::from_utf16_lossy(input.as_slice()); - let mut writer = BufWriter::new(Vec::new()); let tokenizer = Tokenizer::from(input.as_str()); - let compiler = Compiler::new(Parser::new(tokenizer), &mut writer, None); + let compiler = Compiler::new(Parser::new(tokenizer), None); let CompilationResult { errors: diagnosis, .. diff --git a/rust_compiler/src/main.rs b/rust_compiler/src/main.rs index b6ddb9d..da60b84 100644 --- a/rust_compiler/src/main.rs +++ b/rust_compiler/src/main.rs @@ -88,9 +88,7 @@ fn run_logic<'a>() -> Result<(), Error<'a>> { None => BufWriter::new(Box::new(std::io::stdout())), }; - let mut tmp = BufWriter::new(vec![]); - - let compiler = Compiler::new(parser, &mut tmp, None); + let compiler = Compiler::new(parser, None); let CompilationResult { errors, @@ -109,11 +107,7 @@ fn run_logic<'a>() -> Result<(), Error<'a>> { } } - let instructions = optimizer::optimize(instructions); - - for instruction in instructions { - writer.write_all(format!("{}\n", instruction.instruction).as_bytes())?; - } + optimizer::optimize(instructions).write(&mut writer)?; writer.flush()?; diff --git a/spilling.slang b/spilling.slang new file mode 100644 index 0000000..515d4f5 --- /dev/null +++ b/spilling.slang @@ -0,0 +1,43 @@ +device self = "db"; +device gasSensor = "d0"; +device atmosAnal = "d1"; +device atmosValve = "d2"; +device atmosTank = "d3"; +device atmosInlet = "d4"; + +atmosInlet.Lock = true; +atmosInlet.Mode = 1; +atmosValve.On = false; +atmosValve.Lock = true; + +let isPumping = false; +let tempPressure = 0; + +loop { + yield(); + let temp = gasSensor.Temperature; + let pres = atmosAnal.Pressure; + let liqV = atmosAnal.VolumeOfLiquid; + let tempVol = atmosAnal.Volume; + + let stress = 5_000 * liqV / tempVol; + + tempPressure = isPumping ? 1_000 : 10_000; + + let shouldTurnOnInlet = ( + temp > 0c && + pres < tempPressure && + stress < 50 + ); + + isPumping = ( + !shouldTurnOnInlet && + atmosTank.Pressure < 35_000 && + atmosAnal.RatioPollutant == 0 && + atmosAnal.RatioLiquidPollutant == 0 && + atmosAnal.Pressure > 1_000 + ); + + atmosValve.On = isPumping; + atmosInlet.On = shouldTurnOnInlet; +}