From c3986ab4d92b54980700c01242df585de0b44f9f Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Tue, 9 Dec 2025 16:05:40 -0700 Subject: [PATCH] VariableManager lifetime errors --- rust_compiler/libs/compiler/src/v1.rs | 401 ++++++++++-------- .../libs/compiler/src/variable_manager.rs | 56 ++- rust_compiler/libs/parser/src/lib.rs | 56 +-- 3 files changed, 281 insertions(+), 232 deletions(-) diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index 3822a9c..482a1d7 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -12,6 +12,7 @@ use parser::{ }, }; use std::{ + borrow::Cow, collections::HashMap, io::{BufWriter, Write}, }; @@ -36,7 +37,10 @@ macro_rules! debug { }; } -fn extract_literal(literal: Literal, allow_strings: bool) -> Result { +fn extract_literal<'a>( + literal: Literal<'a>, + allow_strings: bool, +) -> Result, Error<'a>> { if !allow_strings && matches!(literal, Literal::String(_)) { return Err(Error::Unknown( "Literal strings are not allowed in this context".to_string(), @@ -45,45 +49,45 @@ fn extract_literal(literal: Literal, allow_strings: bool) -> Result s, - Literal::Number(n) => n.to_string(), - Literal::Boolean(b) => if b { "1" } else { "0" }.into(), + Literal::Number(n) => Cow::from(n.to_string()), + Literal::Boolean(b) => Cow::from(if b { "1" } else { "0" }), }) } #[derive(Error, Debug)] -pub enum Error { - #[error(transparent)] - Parse(#[from] parser::Error), +pub enum Error<'a> { + #[error("{0}")] + Parse(parser::Error<'a>), - #[error(transparent)] - Scope(#[from] variable_manager::Error), + #[error("{0}")] + Scope(variable_manager::Error<'a>), #[error("IO Error: {0}")] IO(String), #[error("`{0}` has already been defined.")] - DuplicateIdentifier(String, Span), + DuplicateIdentifier(Cow<'a, str>, Span), #[error("`{0}` is not found in the current scope.")] - UnknownIdentifier(String, Span), + UnknownIdentifier(Cow<'a, str>, Span), #[error("`{0}` is not valid.")] - InvalidDevice(String, Span), + InvalidDevice(Cow<'a, str>, Span), #[error("Incorrent number of arguments passed into `{0}`")] - AgrumentMismatch(String, Span), + AgrumentMismatch(Cow<'a, str>, Span), #[error("Attempted to re-assign a value to const variable `{0}`")] - ConstAssignment(String, Span), + ConstAssignment(Cow<'a, str>, Span), #[error("Attempted to re-assign a value to a device const `{0}`")] - DeviceAssignment(String, Span), + DeviceAssignment(Cow<'a, str>, Span), #[error("{0}")] Unknown(String, Option), } -impl From for lsp_types::Diagnostic { +impl<'a> From> for lsp_types::Diagnostic { fn from(value: Error) -> Self { use Error::*; use lsp_types::*; @@ -116,8 +120,20 @@ impl From for lsp_types::Diagnostic { } } +impl<'a> From> for Error<'a> { + fn from(value: parser::Error<'a>) -> Self { + Self::Parse(value) + } +} + +impl<'a> From> for Error<'a> { + fn from(value: variable_manager::Error<'a>) -> Self { + Self::Scope(value) + } +} + // Map io::Error to Error manually since we can't clone io::Error -impl From for Error { +impl<'a> From for Error<'a> { fn from(err: std::io::Error) -> Self { Error::IO(err.to_string()) } @@ -129,26 +145,26 @@ pub struct CompilerConfig { pub debug: bool, } -struct CompilationResult { - location: VariableLocation, +struct CompilationResult<'a> { + location: VariableLocation<'a>, /// If Some, this is the name of the temporary variable that holds the result. /// It must be freed by the caller when done. - temp_name: Option, + temp_name: Option>, } pub struct Compiler<'a, W: std::io::Write> { pub parser: ASTParser<'a>, - function_locations: HashMap, - function_metadata: HashMap>, - devices: HashMap, + function_locations: HashMap, usize>, + function_metadata: HashMap, Vec>>, + devices: HashMap, Cow<'a, str>>, output: &'a mut BufWriter, current_line: usize, declared_main: bool, config: CompilerConfig, temp_counter: usize, label_counter: usize, - loop_stack: Vec<(String, String)>, // Stores (start_label, end_label) - pub errors: Vec, + loop_stack: Vec<(Cow<'a, str>, Cow<'a, str>)>, // Stores (start_label, end_label) + pub errors: Vec>, } impl<'a, W: std::io::Write> Compiler<'a, W> { @@ -173,7 +189,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } } - pub fn compile(mut self) -> Vec { + pub fn compile(mut self) -> Vec> { let expr = self.parser.parse_all(); // Copy errors from parser @@ -211,15 +227,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { return self.errors; } + let mut scope = VariableScope::default(); + // We ignore the result of the root expression (usually a block) - if let Err(e) = self.expression(spanned_root, &mut VariableScope::default()) { + if let Err(e) = self.expression(spanned_root, &mut scope) { self.errors.push(e); } self.errors } - fn write_output(&mut self, output: impl Into) -> Result<(), Error> { + fn write_output(&mut self, output: impl Into) -> Result<(), Error<'a>> { self.output.write_all(output.into().as_bytes())?; self.output.write_all(b"\n")?; self.current_line += 1; @@ -227,21 +245,21 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Ok(()) } - fn next_temp_name(&mut self) -> String { + fn next_temp_name(&mut self) -> Cow<'a, str> { self.temp_counter += 1; - format!("__binary_temp_{}", self.temp_counter) + Cow::from(format!("__binary_temp_{}", self.temp_counter)) } - fn next_label_name(&mut self) -> String { + fn next_label_name(&mut self) -> Cow<'a, str> { self.label_counter += 1; - format!("L{}", self.label_counter) + Cow::from(format!("L{}", self.label_counter)) } - fn expression<'v>( + fn expression( &mut self, - expr: Spanned, - scope: &mut VariableScope<'v>, - ) -> Result, Error> { + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result>, Error<'a>> { match expr.node { Expression::Function(expr_func) => { self.expression_function(expr_func, scope)?; @@ -300,11 +318,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // Invocation returns result in r15 (RETURN_REGISTER). // If used as an expression, we must move it to a temp to avoid overwrite. let temp_name = self.next_temp_name(); - let temp_loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?; + let temp_loc = + scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?; self.emit_variable_assignment( - &temp_name, + temp_name.clone(), &temp_loc, - format!("r{}", VariableScope::RETURN_REGISTER), + Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)), )?; Ok(Some(CompilationResult { location: temp_loc, @@ -322,8 +341,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Expression::Literal(spanned_lit) => match spanned_lit.node { Literal::Number(num) => { let temp_name = self.next_temp_name(); - let loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?; - self.emit_variable_assignment(&temp_name, &loc, num.to_string())?; + let loc = scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?; + self.emit_variable_assignment( + temp_name.clone(), + &loc, + Cow::from(num.to_string()), + )?; Ok(Some(CompilationResult { location: loc, temp_name: Some(temp_name), @@ -332,8 +355,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Literal::Boolean(b) => { let val = if b { "1" } else { "0" }; let temp_name = self.next_temp_name(); - let loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?; - self.emit_variable_assignment(&temp_name, &loc, val)?; + let loc = scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?; + self.emit_variable_assignment(temp_name.clone(), &loc, Cow::from(val))?; Ok(Some(CompilationResult { location: loc, temp_name: Some(temp_name), @@ -374,7 +397,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // 2. Allocate a temp register for the result let result_name = self.next_temp_name(); - let loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?; + let loc = scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?; let reg = self.resolve_register(&loc)?; // 3. Emit load instruction: l rX device member @@ -406,7 +429,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // Compile negation as 0 - inner let (inner_str, cleanup) = self.compile_operand(*inner_expr, scope)?; let result_name = self.next_temp_name(); - let result_loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?; + let result_loc = + scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?; let result_reg = self.resolve_register(&result_loc)?; self.write_output(format!("sub {result_reg} 0 {inner_str}"))?; @@ -432,11 +456,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { /// Resolves an expression to a device identifier string for use in instructions like `s` or `l`. /// Returns (device_string, optional_cleanup_temp_name). - fn resolve_device<'v>( + fn resolve_device( &mut self, - expr: Spanned, - scope: &mut VariableScope<'v>, - ) -> Result<(String, Option), Error> { + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result<(Cow<'a, str>, Option>), Error<'a>> { // If it's a direct variable reference, check if it's a known device alias first if let Expression::Variable(ref name) = expr.node && let Some(device_id) = self.devices.get(&name.node) @@ -450,10 +474,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { fn emit_variable_assignment( &mut self, - var_name: &str, - location: &VariableLocation, - source_value: impl Into, - ) -> Result<(), Error> { + var_name: Cow<'a, str>, + location: &VariableLocation<'a>, + source_value: Cow<'a, str>, + ) -> Result<(), Error<'a>> { let debug_tag = if self.config.debug { format!(" #{var_name}") } else { @@ -462,10 +486,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { match location { VariableLocation::Temporary(reg) | VariableLocation::Persistant(reg) => { - self.write_output(format!("move r{reg} {}{debug_tag}", source_value.into()))?; + self.write_output(format!("move r{reg} {}{debug_tag}", source_value))?; } VariableLocation::Stack(_) => { - self.write_output(format!("push {}{debug_tag}", source_value.into()))?; + self.write_output(format!("push {}{debug_tag}", source_value))?; } VariableLocation::Constant(_) => { return Err(Error::Unknown( @@ -488,12 +512,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Ok(()) } - fn expression_declaration<'v>( + fn expression_declaration( &mut self, - var_name: Spanned, - expr: Spanned, - scope: &mut VariableScope<'v>, - ) -> Result, Error> { + var_name: Spanned>, + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result>, Error<'a>> { let name_str = var_name.node; let name_span = var_name.span; @@ -502,8 +526,13 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { && let Expression::Literal(spanned_lit) = &box_expr.node && let Literal::Number(neg_num) = &spanned_lit.node { - let loc = scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; - self.emit_variable_assignment(&name_str, &loc, format!("-{neg_num}"))?; + let loc = + scope.add_variable(name_str.clone(), LocationRequest::Persist, Some(name_span))?; + self.emit_variable_assignment( + name_str.clone(), + &loc, + Cow::from(format!("-{neg_num}")), + )?; return Ok(Some(CompilationResult { location: loc, temp_name: None, @@ -519,7 +548,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Some(name_span), )?; - self.emit_variable_assignment(&name_str, &var_location, num)?; + self.emit_variable_assignment( + name_str.clone(), + &var_location, + Cow::from(num.to_string()), + )?; (var_location, None) } Literal::Boolean(b) => { @@ -530,7 +563,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Some(name_span), )?; - self.emit_variable_assignment(&name_str, &var_location, val)?; + self.emit_variable_assignment(name_str, &var_location, Cow::from(val))?; (var_location, None) } _ => return Ok(None), @@ -538,12 +571,15 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Expression::Invocation(invoke_expr) => { self.expression_function_invocation(invoke_expr, scope)?; - let loc = - scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; + let loc = scope.add_variable( + name_str.clone(), + LocationRequest::Persist, + Some(name_span), + )?; self.emit_variable_assignment( - &name_str, + name_str, &loc, - format!("r{}", VariableScope::RETURN_REGISTER), + Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)), )?; (loc, None) } @@ -564,11 +600,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { }; let loc = - scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; + scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?; self.emit_variable_assignment( - &name_str, + name_str, &loc, - format!("r{}", VariableScope::RETURN_REGISTER), + Cow::from(format!("r{}", VariableScope::RETURN_REGISTER)), )?; (loc, None) @@ -577,19 +613,19 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Expression::Binary(bin_expr) => { let result = self.expression_binary(bin_expr, scope)?; let var_loc = - scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; + scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?; if let CompilationResult { location: VariableLocation::Constant(Literal::Number(num)), .. } = result { - self.emit_variable_assignment(&name_str, &var_loc, num)?; + self.emit_variable_assignment(name_str, &var_loc, Cow::from(num.to_string()))?; (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)?; + self.emit_variable_assignment(name_str, &var_loc, result_reg)?; // Free the temp result if let Some(name) = result.temp_name { @@ -601,11 +637,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Expression::Logical(log_expr) => { let result = self.expression_logical(log_expr, scope)?; let var_loc = - scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; + scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?; // 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)?; + self.emit_variable_assignment(name_str, &var_loc, result_reg)?; // Free the temp result if let Some(name) = result.temp_name { @@ -626,7 +662,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { }; let var_loc = - scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; + scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?; // Handle loading from stack if necessary let src_str = match src_loc { @@ -646,7 +682,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } VariableLocation::Constant(_) | VariableLocation::Device(_) => unreachable!(), }; - self.emit_variable_assignment(&name_str, &var_loc, src_str)?; + self.emit_variable_assignment(name_str, &var_loc, Cow::from(src_str))?; (var_loc, None) } Expression::Priority(inner) => { @@ -678,10 +714,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { }; let var_loc = - scope.add_variable(&name_str, LocationRequest::Persist, Some(name_span))?; + scope.add_variable(name_str, LocationRequest::Persist, Some(name_span))?; let result_reg = self.resolve_register(&comp_res.location)?; - self.emit_variable_assignment(&name_str, &var_loc, result_reg)?; + self.emit_variable_assignment(name_str, &var_loc, result_reg)?; if let Some(temp) = comp_res.temp_name { scope.free_temp(temp, None)?; @@ -703,11 +739,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { })) } - fn expression_const_declaration<'v>( + fn expression_const_declaration( &mut self, - expr: ConstDeclarationExpression, - scope: &mut VariableScope<'v>, - ) -> Result { + expr: ConstDeclarationExpression<'a>, + scope: &mut VariableScope<'a>, + ) -> Result, Error<'a>> { let ConstDeclarationExpression { name: const_name, value: const_value, @@ -738,11 +774,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { }) } - fn expression_assignment<'v>( + fn expression_assignment( &mut self, - expr: AssignmentExpression, - scope: &mut VariableScope<'v>, - ) -> Result<(), Error> { + expr: AssignmentExpression<'a>, + scope: &mut VariableScope<'a>, + ) -> Result<(), Error<'a>> { let AssignmentExpression { assignee, expression, @@ -828,9 +864,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { fn expression_function_invocation( &mut self, - invoke_expr: Spanned, - stack: &mut VariableScope, - ) -> Result<(), Error> { + invoke_expr: Spanned>, + stack: &mut VariableScope<'a>, + ) -> Result<(), Error<'a>> { let InvocationExpression { name, arguments } = invoke_expr.node; if !self.function_locations.contains_key(&name.node) { @@ -845,12 +881,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { let Some(args) = self.function_metadata.get(&name.node) else { // Should be covered by check above - return Err(Error::UnknownIdentifier(name.node.clone(), name.span)); + return Err(Error::UnknownIdentifier(name.node, name.span)); }; if args.len() != arguments.len() { self.errors - .push(Error::AgrumentMismatch(name.node.clone(), name.span)); + .push(Error::AgrumentMismatch(name.node, name.span)); // Proceed anyway? The assembly will likely crash or act weird. // Best to skip generation of this call to prevent bad IC10 return Ok(()); @@ -859,7 +895,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // backup all used registers to the stack let active_registers = stack.registers().cloned().collect::>(); for register in &active_registers { - stack.add_variable(format!("temp_{register}"), LocationRequest::Stack, None)?; + stack.add_variable( + Cow::from(format!("temp_{register}")), + LocationRequest::Stack, + None, + )?; self.write_output(format!("push r{register}"))?; } for arg in arguments { @@ -876,15 +916,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { _ => {} }, Expression::Variable(var_name) => { - let loc = - match stack.get_location_of(var_name.node.clone(), Some(var_name.span)) { - Ok(l) => l, - Err(_) => { - self.errors - .push(Error::UnknownIdentifier(var_name.node, var_name.span)); - VariableLocation::Temporary(0) - } - }; + let loc = match stack.get_location_of(&var_name.node, Some(var_name.span)) { + Ok(l) => l, + Err(_) => { + self.errors + .push(Error::UnknownIdentifier(var_name.node, var_name.span)); + VariableLocation::Temporary(0) + } + }; match loc { VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => { @@ -975,7 +1014,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { for register in active_registers { let VariableLocation::Stack(stack_offset) = stack - .get_location_of(format!("temp_{register}"), None) + .get_location_of(&Cow::from(format!("temp_{register}")), None) .map_err(Error::Scope)? else { // This shouldn't happen if we just added it @@ -1001,7 +1040,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Ok(()) } - fn expression_device(&mut self, expr: DeviceDeclarationExpression) -> Result<(), Error> { + fn expression_device( + &mut self, + expr: DeviceDeclarationExpression<'a>, + ) -> Result<(), Error<'a>> { if self.devices.contains_key(&expr.name.node) { self.errors.push(Error::DuplicateIdentifier( expr.name.node.clone(), @@ -1017,11 +1059,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Ok(()) } - fn expression_if<'v>( + fn expression_if( &mut self, - expr: IfExpression, - scope: &mut VariableScope<'v>, - ) -> Result<(), Error> { + expr: IfExpression<'a>, + scope: &mut VariableScope<'a>, + ) -> Result<(), Error<'a>> { let end_label = self.next_label_name(); let else_label = if expr.else_branch.is_some() { self.next_label_name() @@ -1064,11 +1106,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Ok(()) } - fn expression_loop<'v>( + fn expression_loop( &mut self, - expr: LoopExpression, - scope: &mut VariableScope<'v>, - ) -> Result<(), Error> { + expr: LoopExpression<'a>, + scope: &mut VariableScope<'a>, + ) -> Result<(), Error<'a>> { let start_label = self.next_label_name(); let end_label = self.next_label_name(); @@ -1090,11 +1132,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Ok(()) } - fn expression_while<'v>( + fn expression_while( &mut self, - expr: WhileExpression, - scope: &mut VariableScope<'v>, - ) -> Result<(), Error> { + expr: WhileExpression<'a>, + scope: &mut VariableScope<'a>, + ) -> Result<(), Error<'a>> { let start_label = self.next_label_name(); let end_label = self.next_label_name(); @@ -1126,7 +1168,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Ok(()) } - fn expression_break(&mut self) -> Result<(), Error> { + fn expression_break(&mut self) -> Result<(), Error<'a>> { if let Some((_, end_label)) = self.loop_stack.last() { self.write_output(format!("j {end_label}"))?; Ok(()) @@ -1138,7 +1180,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } } - fn expression_continue(&mut self) -> Result<(), Error> { + fn expression_continue(&mut self) -> Result<(), Error<'a>> { if let Some((start_label, _)) = self.loop_stack.last() { self.write_output(format!("j {start_label}"))?; Ok(()) @@ -1153,9 +1195,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { /// Helper to resolve a location to a register string (e.g., "r0"). /// Note: This does not handle Stack locations automatically, as they require /// instruction emission to load. Use `compile_operand` for general handling. - fn resolve_register(&self, loc: &VariableLocation) -> Result { + fn resolve_register(&self, loc: &VariableLocation) -> Result, Error<'a>> { match loc { - VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => Ok(format!("r{r}")), + VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => { + Ok(Cow::from(format!("r{r}"))) + } VariableLocation::Constant(_) => Err(Error::Unknown( "Cannot resolve a constant value to register".into(), None, @@ -1177,19 +1221,19 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { /// so the caller can free it. fn compile_operand( &mut self, - expr: Spanned, - scope: &mut VariableScope, - ) -> Result<(String, Option), Error> { + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result<(Cow<'a, str>, Option>), Error<'a>> { // Optimization for literals if let Expression::Literal(spanned_lit) = &expr.node { if let Literal::Number(n) = spanned_lit.node { - return Ok((n.to_string(), None)); + return Ok((Cow::from(n.to_string()), None)); } if let Literal::Boolean(b) = spanned_lit.node { - return Ok((if b { "1".to_string() } else { "0".to_string() }, None)); + return Ok((Cow::from(if b { "1" } else { "0" }), None)); } if let Literal::String(ref s) = spanned_lit.node { - return Ok((s.to_string(), None)); + return Ok((s.clone(), None)); } } @@ -1199,7 +1243,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { && let Expression::Literal(spanned_lit) = &inner.node && let Literal::Number(n) = spanned_lit.node { - return Ok((format!("-{}", n), None)); + return Ok((Cow::from(format!("-{}", n)), None)); } let result_opt = self.expression(expr, scope)?; @@ -1208,23 +1252,24 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Some(r) => r, None => { // Expression failed or returned void. Recover with dummy. - return Ok(("r0".to_string(), None)); + return Ok((Cow::from("r0"), None)); } }; match result.location { VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => { - Ok((format!("r{r}"), result.temp_name)) + Ok((Cow::from(format!("r{r}")), result.temp_name)) } VariableLocation::Constant(lit) => match lit { - Literal::Number(n) => Ok((n.to_string(), None)), - Literal::Boolean(b) => Ok((if b { "1" } else { "0" }.to_string(), None)), + Literal::Number(n) => Ok((Cow::from(n.to_string()), None)), + Literal::Boolean(b) => Ok((Cow::from(if b { "1" } else { "0" }), None)), Literal::String(s) => Ok((s, None)), }, VariableLocation::Stack(offset) => { // If it's on the stack, we must load it into a temp to use it as an operand let temp_name = self.next_temp_name(); - let temp_loc = scope.add_variable(&temp_name, LocationRequest::Temp, None)?; + let temp_loc = + scope.add_variable(temp_name.clone(), LocationRequest::Temp, None)?; let temp_reg = self.resolve_register(&temp_loc)?; self.write_output(format!( @@ -1247,9 +1292,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { fn compile_literal_or_variable( &mut self, - val: LiteralOrVariable, - scope: &mut VariableScope, - ) -> Result<(String, Option), Error> { + val: LiteralOrVariable<'a>, + scope: &mut VariableScope<'a>, + ) -> Result<(Cow<'a, str>, Option>), Error<'a>> { let dummy_span = Span { start_line: 0, start_col: 0, @@ -1273,12 +1318,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { ) } - fn expression_binary<'v>( + fn expression_binary( &mut self, - expr: Spanned, - scope: &mut VariableScope<'v>, - ) -> Result { - fn fold_binary_expression(expr: &BinaryExpression) -> Option { + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result, Error<'a>> { + fn fold_binary_expression<'a>(expr: &BinaryExpression<'a>) -> Option { let (lhs, rhs) = match &expr { BinaryExpression::Add(l, r) | BinaryExpression::Subtract(l, r) @@ -1298,7 +1343,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } } - fn fold_expression(expr: &Expression) -> Option { + fn fold_expression<'a>(expr: &Expression<'a>) -> Option { match expr { // 1. Base Case: It's already a number Expression::Literal(lit) => match lit.node { @@ -1346,7 +1391,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // Allocate result register let result_name = self.next_temp_name(); - let result_loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?; + let result_loc = scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?; let result_reg = self.resolve_register(&result_loc)?; // Emit instruction: op result lhs rhs @@ -1366,17 +1411,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { }) } - fn expression_logical<'v>( + fn expression_logical( &mut self, - expr: Spanned, - scope: &mut VariableScope<'v>, - ) -> Result { + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result, Error<'a>> { match expr.node { 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, None)?; + let result_loc = + scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?; let result_reg = self.resolve_register(&result_loc)?; // seq rX rY 0 => if rY == 0 set rX = 1 else rX = 0 @@ -1411,7 +1457,8 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // Allocate result register let result_name = self.next_temp_name(); - let result_loc = scope.add_variable(&result_name, LocationRequest::Temp, None)?; + let result_loc = + scope.add_variable(result_name.clone(), LocationRequest::Temp, None)?; let result_reg = self.resolve_register(&result_loc)?; // Emit instruction: op result lhs rhs @@ -1433,11 +1480,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } } - fn expression_block<'v>( + fn expression_block( &mut self, - mut expr: BlockExpression, - parent_scope: &mut VariableScope<'v>, - ) -> Result<(), Error> { + mut expr: BlockExpression<'a>, + parent_scope: &mut VariableScope<'a>, + ) -> Result<(), Error<'a>> { // First, sort the expressions to ensure functions are hoisted expr.0.sort_by(|a, b| { if matches!( @@ -1499,17 +1546,21 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } /// Takes the result of the expression and stores it in VariableScope::RETURN_REGISTER - fn expression_return<'v>( + fn expression_return( &mut self, - expr: Spanned, - scope: &mut VariableScope<'v>, - ) -> Result { + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result, Error<'a>> { if let Expression::Negation(neg_expr) = &expr.node && let Expression::Literal(spanned_lit) = &neg_expr.node && let Literal::Number(neg_num) = &spanned_lit.node { let loc = VariableLocation::Persistant(VariableScope::RETURN_REGISTER); - self.emit_variable_assignment("returnValue", &loc, format!("-{neg_num}"))?; + self.emit_variable_assignment( + Cow::from("returnValue"), + &loc, + Cow::from(format!("-{neg_num}")), + )?; return Ok(loc); }; @@ -1562,17 +1613,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { Expression::Literal(spanned_lit) => match spanned_lit.node { Literal::Number(num) => { self.emit_variable_assignment( - "returnValue", + Cow::from("returnValue"), &VariableLocation::Persistant(VariableScope::RETURN_REGISTER), - num, + Cow::from(num.to_string()), )?; } Literal::Boolean(b) => { let val = if b { "1" } else { "0" }; self.emit_variable_assignment( - "returnValue", + Cow::from("returnValue"), &VariableLocation::Persistant(VariableScope::RETURN_REGISTER), - val, + Cow::from(val.to_string()), )?; } _ => {} @@ -1631,12 +1682,12 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // syscalls that return values will be stored in the VariableScope::RETURN_REGISTER // register - fn expression_syscall_system<'v>( + fn expression_syscall_system( &mut self, - expr: System, + expr: System<'a>, span: Span, - scope: &mut VariableScope<'v>, - ) -> Result, Error> { + scope: &mut VariableScope<'a>, + ) -> Result>, Error<'a>> { macro_rules! cleanup { ($($to_clean:expr),*) => { $( @@ -1707,7 +1758,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { .devices .get(&device_name) .cloned() - .unwrap_or("d0".to_string()); + .unwrap_or(Cow::from("d0")); let Spanned { node: Literal::String(logic_type), @@ -1799,7 +1850,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { .devices .get(&device_name) .cloned() - .unwrap_or("d0".to_string()); + .unwrap_or(Cow::from("d0")); let Spanned { node: Literal::String(logic_type), @@ -1889,11 +1940,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { } } - fn expression_syscall_math<'v>( + fn expression_syscall_math( &mut self, - expr: Math, - scope: &mut VariableScope<'v>, - ) -> Result, Error> { + expr: Math<'a>, + scope: &mut VariableScope<'a>, + ) -> Result>, Error<'a>> { macro_rules! cleanup { ($($to_clean:expr),*) => { $( @@ -2085,11 +2136,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { /// Compile a function declaration. /// Calees are responsible for backing up any registers they wish to use. - fn expression_function<'v>( + fn expression_function( &mut self, - expr: Spanned, - scope: &mut VariableScope<'v>, - ) -> Result<(), Error> { + expr: Spanned>, + scope: &mut VariableScope<'a>, + ) -> Result<(), Error<'a>> { let FunctionExpression { name, arguments, diff --git a/rust_compiler/libs/compiler/src/variable_manager.rs b/rust_compiler/libs/compiler/src/variable_manager.rs index 123ac09..fd2e463 100644 --- a/rust_compiler/libs/compiler/src/variable_manager.rs +++ b/rust_compiler/libs/compiler/src/variable_manager.rs @@ -5,25 +5,28 @@ use lsp_types::{Diagnostic, DiagnosticSeverity}; use parser::tree_node::{Literal, Span}; -use std::collections::{HashMap, VecDeque}; +use std::{ + borrow::Cow, + collections::{HashMap, VecDeque}, +}; use thiserror::Error; const TEMP: [u8; 7] = [1, 2, 3, 4, 5, 6, 7]; const PERSIST: [u8; 7] = [8, 9, 10, 11, 12, 13, 14]; #[derive(Error, Debug)] -pub enum Error { +pub enum Error<'a> { #[error("{0} already exists.")] - DuplicateVariable(String, Option), + DuplicateVariable(Cow<'a, str>, Option), #[error("{0} does not exist.")] - UnknownVariable(String, Option), + UnknownVariable(Cow<'a, str>, Option), #[error("{0}")] - Unknown(String, Option), + Unknown(Cow<'a, str>, Option), } -impl From for lsp_types::Diagnostic { +impl<'a> From> for lsp_types::Diagnostic { fn from(value: Error) -> Self { match value { Error::DuplicateVariable(_, span) @@ -50,7 +53,7 @@ pub enum LocationRequest { } #[derive(Clone)] -pub enum VariableLocation { +pub enum VariableLocation<'a> { /// Represents a temporary register (r1 - r7) Temporary(u8), /// Represents a persistant register (r8 - r14) @@ -58,15 +61,15 @@ pub enum VariableLocation { /// Represents a a stack offset (current stack - offset = variable loc) Stack(u16), /// Represents a constant value and should be directly substituted as such. - Constant(Literal), + Constant(Literal<'a>), /// Represents a device pin. This will contain the exact `d0-d5` string - Device(String), + Device(Cow<'a, str>), } pub struct VariableScope<'a> { temporary_vars: VecDeque, persistant_vars: VecDeque, - var_lookup_table: HashMap, + var_lookup_table: HashMap, VariableLocation<'a>>, stack_offset: u16, parent: Option<&'a VariableScope<'a>>, } @@ -123,12 +126,11 @@ impl<'a> VariableScope<'a> { /// to the stack. pub fn add_variable( &mut self, - var_name: impl Into, + var_name: Cow<'a, str>, location: LocationRequest, span: Option, - ) -> Result { - let var_name = var_name.into(); - if self.var_lookup_table.contains_key(var_name.as_str()) { + ) -> Result, Error<'a>> { + if self.var_lookup_table.contains_key(&var_name) { return Err(Error::DuplicateVariable(var_name, span)); } let var_location = match location { @@ -163,11 +165,10 @@ impl<'a> VariableScope<'a> { pub fn define_const( &mut self, - var_name: impl Into, - value: Literal, + var_name: Cow<'a, str>, + value: Literal<'a>, span: Option, - ) -> Result { - let var_name = var_name.into(); + ) -> Result, Error<'a>> { if self.var_lookup_table.contains_key(&var_name) { return Err(Error::DuplicateVariable(var_name, span)); } @@ -180,13 +181,11 @@ impl<'a> VariableScope<'a> { pub fn get_location_of( &self, - var_name: impl Into, + var_name: &Cow<'a, str>, span: Option, - ) -> Result { - let var_name = var_name.into(); - + ) -> Result, Error<'a>> { // 1. Check this scope - if let Some(var) = self.var_lookup_table.get(var_name.as_str()) { + if let Some(var) = self.var_lookup_table.get(var_name) { if let VariableLocation::Stack(inserted_at_offset) = var { // Return offset relative to CURRENT sp return Ok(VariableLocation::Stack( @@ -207,7 +206,7 @@ impl<'a> VariableScope<'a> { return Ok(loc); } - Err(Error::UnknownVariable(var_name, span)) + Err(Error::UnknownVariable(Cow::from(var_name.to_owned()), span)) } pub fn has_parent(&self) -> bool { @@ -217,11 +216,10 @@ impl<'a> VariableScope<'a> { #[allow(dead_code)] pub fn free_temp( &mut self, - var_name: impl Into, + var_name: Cow<'a, str>, span: Option, - ) -> Result<(), Error> { - let var_name = var_name.into(); - let Some(location) = self.var_lookup_table.remove(var_name.as_str()) else { + ) -> Result<(), Error<'a>> { + let Some(location) = self.var_lookup_table.remove(&var_name) else { return Err(Error::UnknownVariable(var_name, span)); }; @@ -231,7 +229,7 @@ impl<'a> VariableScope<'a> { } VariableLocation::Persistant(_) => { return Err(Error::UnknownVariable( - String::from("Attempted to free a `let` variable."), + Cow::from("Attempted to free a `let` variable."), span, )); } diff --git a/rust_compiler/libs/parser/src/lib.rs b/rust_compiler/libs/parser/src/lib.rs index 2c4bc02..8a99e9c 100644 --- a/rust_compiler/libs/parser/src/lib.rs +++ b/rust_compiler/libs/parser/src/lib.rs @@ -1518,14 +1518,14 @@ impl<'a> Parser<'a> { } fn syscall(&mut self) -> Result, Error<'a>> { - fn check_length<'a, 't: 'a>( - parser: &'t Parser, + fn check_length<'a>( + span: Span, arguments: &[Spanned>], length: usize, ) -> Result<(), Error<'a>> { if arguments.len() != length { return Err(Error::InvalidSyntax( - parser.current_span(), + span, format!("Expected {} arguments", length), )); } @@ -1586,17 +1586,17 @@ impl<'a> Parser<'a> { match invocation.name.node.as_ref() { // System SysCalls "yield" => { - check_length(self, &invocation.arguments, 0)?; + check_length(self.current_span(), &invocation.arguments, 0)?; Ok(SysCall::System(sys_call::System::Yield)) } "sleep" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut arg = invocation.arguments.into_iter(); let expr = arg.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::System(System::Sleep(boxed!(expr)))) } "hash" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let lit_str = literal_or_variable!(args.next()); @@ -1617,7 +1617,7 @@ impl<'a> Parser<'a> { }))) } "load" | "l" => { - check_length(self, &invocation.arguments, 2)?; + check_length(self.current_span(), &invocation.arguments, 2)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next(); @@ -1662,7 +1662,7 @@ impl<'a> Parser<'a> { ))) } "loadBatched" | "lb" => { - check_length(self, &invocation.arguments, 3)?; + check_length(self.current_span(), &invocation.arguments, 3)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); @@ -1680,7 +1680,7 @@ impl<'a> Parser<'a> { ))) } "loadBatchedNamed" | "lbn" => { - check_length(self, &invocation.arguments, 4)?; + check_length(self.current_span(), &invocation.arguments, 4)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next(); let dev_hash = literal_or_variable!(tmp); @@ -1699,7 +1699,7 @@ impl<'a> Parser<'a> { ))) } "set" | "s" => { - check_length(self, &invocation.arguments, 3)?; + check_length(self.current_span(), &invocation.arguments, 3)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next(); let device = literal_or_variable!(tmp); @@ -1720,7 +1720,7 @@ impl<'a> Parser<'a> { ))) } "setBatched" | "sb" => { - check_length(self, &invocation.arguments, 3)?; + check_length(self.current_span(), &invocation.arguments, 3)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); @@ -1739,7 +1739,7 @@ impl<'a> Parser<'a> { ))) } "setBatchedNamed" | "sbn" => { - check_length(self, &invocation.arguments, 4)?; + check_length(self.current_span(), &invocation.arguments, 4)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next(); let device_hash = literal_or_variable!(tmp); @@ -1762,28 +1762,28 @@ impl<'a> Parser<'a> { } // Math SysCalls "acos" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Acos(boxed!(tmp)))) } "asin" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let tmp = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Asin(boxed!(tmp)))) } "atan" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Atan(boxed!(expr)))) } "atan2" => { - check_length(self, &invocation.arguments, 2)?; + check_length(self.current_span(), &invocation.arguments, 2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; @@ -1791,42 +1791,42 @@ impl<'a> Parser<'a> { Ok(SysCall::Math(Math::Atan2(boxed!(arg1), boxed!(arg2)))) } "abs" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let expr = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Abs(boxed!(expr)))) } "ceil" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Ceil(boxed!(arg)))) } "cos" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Cos(boxed!(arg)))) } "floor" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Floor(boxed!(arg)))) } "log" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Log(boxed!(arg)))) } "max" => { - check_length(self, &invocation.arguments, 2)?; + check_length(self.current_span(), &invocation.arguments, 2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; @@ -1834,7 +1834,7 @@ impl<'a> Parser<'a> { Ok(SysCall::Math(Math::Max(boxed!(arg1), boxed!(arg2)))) } "min" => { - check_length(self, &invocation.arguments, 2)?; + check_length(self.current_span(), &invocation.arguments, 2)?; let mut args = invocation.arguments.into_iter(); let arg1 = args.next().ok_or(Error::UnexpectedEOF)?; let arg2 = args.next().ok_or(Error::UnexpectedEOF)?; @@ -1842,32 +1842,32 @@ impl<'a> Parser<'a> { Ok(SysCall::Math(Math::Min(boxed!(arg1), boxed!(arg2)))) } "rand" => { - check_length(self, &invocation.arguments, 0)?; + check_length(self.current_span(), &invocation.arguments, 0)?; Ok(SysCall::Math(Math::Rand)) } "sin" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Sin(boxed!(arg)))) } "sqrt" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Sqrt(boxed!(arg)))) } "tan" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?; Ok(SysCall::Math(Math::Tan(boxed!(arg)))) } "trunc" => { - check_length(self, &invocation.arguments, 1)?; + check_length(self.current_span(), &invocation.arguments, 1)?; let mut args = invocation.arguments.into_iter(); let arg = args.next().ok_or(Error::UnexpectedEOF)?;