From d34ce32d978861b9aaa8ec6f113f1045552092c1 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Thu, 4 Dec 2025 21:18:57 -0700 Subject: [PATCH] v1 constant expressions --- rust_compiler/libs/compiler/src/v1.rs | 93 +++++++++++++++++-- .../libs/compiler/src/variable_manager.rs | 22 ++++- 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index f97e99b..6633554 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -4,10 +4,10 @@ use parser::{ Parser as ASTParser, sys_call::{SysCall, System}, tree_node::{ - AssignmentExpression, BinaryExpression, BlockExpression, DeviceDeclarationExpression, - Expression, FunctionExpression, IfExpression, InvocationExpression, Literal, - LiteralOrVariable, LogicalExpression, LoopExpression, MemberAccessExpression, Span, - Spanned, WhileExpression, + AssignmentExpression, BinaryExpression, BlockExpression, ConstDeclarationExpression, + DeviceDeclarationExpression, Expression, FunctionExpression, IfExpression, + InvocationExpression, Literal, LiteralOrVariable, LogicalExpression, LoopExpression, + MemberAccessExpression, Span, Spanned, WhileExpression, }, }; use quick_error::quick_error; @@ -34,6 +34,20 @@ macro_rules! debug { }; } +fn extract_literal(literal: Literal, allow_strings: bool) -> Result { + if !allow_strings && matches!(literal, Literal::String(_)) { + return Err(Error::Unknown( + "Literal strings are not allowed in this context".to_string(), + None, + )); + } + Ok(match literal { + Literal::String(s) => s, + Literal::Number(n) => n.to_string(), + Literal::Boolean(b) => if b { "1" } else { "0" }.into(), + }) +} + quick_error! { #[derive(Debug)] pub enum Error { @@ -58,6 +72,9 @@ quick_error! { AgrumentMismatch(func_name: String, span: Span) { display("Incorrect number of arguments passed into `{func_name}`") } + ConstAssignment(ident: String, span: Span) { + display("Attempted to re-assign a value to const variable `{ident}`") + } Unknown(reason: String, span: Option) { display("{reason}") } @@ -84,6 +101,7 @@ impl From for lsp_types::Diagnostic { DuplicateIdentifier(_, span) | UnknownIdentifier(_, span) | InvalidDevice(_, span) + | ConstAssignment(_, span) | AgrumentMismatch(_, span) => Diagnostic { range: span.into(), message: value.to_string(), @@ -267,6 +285,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { // decl_expr is Box> self.expression_declaration(var_name, *decl_expr, scope) } + Expression::ConstDeclaration(const_decl_expr) => { + self.expression_const_declaration(const_decl_expr.node, scope)?; + Ok(None) + } Expression::Assignment(assign_expr) => { self.expression_assignment(assign_expr.node, scope)?; Ok(None) @@ -435,6 +457,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { VariableLocation::Stack(_) => { self.write_output(format!("push {}{debug_tag}", source_value.into()))?; } + VariableLocation::Constant(_) => { + return Err(Error::Unknown( + r#"Attempted to emit a variable assignent for a constant value. + This is a Compiler bug and should be reported to the developer."# + .into(), + None, + )); + } } Ok(()) @@ -581,6 +611,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { ))?; format!("r{}", VariableScope::TEMP_STACK_REGISTER) } + VariableLocation::Constant(_) => unreachable!(), }; self.emit_variable_assignment(&name_str, &var_loc, src_str)?; (var_loc, None) @@ -638,6 +669,22 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { })) } + fn expression_const_declaration<'v>( + &mut self, + expr: ConstDeclarationExpression, + scope: &mut VariableScope<'v>, + ) -> Result { + let ConstDeclarationExpression { + name: const_name, + value: const_value, + } = expr; + + Ok(CompilationResult { + location: scope.define_const(const_name.node, const_value.node)?, + temp_name: None, + }) + } + fn expression_assignment<'v>( &mut self, expr: AssignmentExpression, @@ -685,6 +732,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { VariableScope::TEMP_STACK_REGISTER ))?; } + VariableLocation::Constant(_) => { + return Err(Error::ConstAssignment(identifier.node, identifier.span)); + } } if let Some(name) = cleanup { @@ -782,6 +832,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => { self.write_output(format!("push r{reg}"))?; } + VariableLocation::Constant(lit) => { + self.write_output(format!("push {}", extract_literal(lit, false)?))?; + } VariableLocation::Stack(stack_offset) => { self.write_output(format!( "sub r{0} sp {stack_offset}", @@ -1041,6 +1094,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { fn resolve_register(&self, loc: &VariableLocation) -> Result { match loc { VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => Ok(format!("r{r}")), + VariableLocation::Constant(_) => Err(Error::Unknown( + "Cannot resolve a constant value to register".into(), + None, + )), VariableLocation::Stack(_) => Err(Error::Unknown( "Cannot resolve Stack location directly to register string without context".into(), None, @@ -1090,6 +1147,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => { Ok((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::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(); @@ -1256,11 +1318,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { ) -> Result<(), Error> { // First, sort the expressions to ensure functions are hoisted expr.0.sort_by(|a, b| { - if matches!(b.node, Expression::Function(_)) - && matches!(a.node, Expression::Function(_)) - { + if matches!( + b.node, + Expression::Function(_) | Expression::ConstDeclaration(_) + ) && matches!( + a.node, + Expression::Function(_) | Expression::ConstDeclaration(_) + ) { std::cmp::Ordering::Equal - } else if matches!(a.node, Expression::Function(_)) { + } else if matches!( + a.node, + Expression::Function(_) | Expression::ConstDeclaration(_) + ) { std::cmp::Ordering::Less } else { std::cmp::Ordering::Greater @@ -1332,6 +1401,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> { debug!(self, "#returnValue") ))?; } + VariableLocation::Constant(lit) => { + let str = extract_literal(lit, false)?; + self.write_output(format!( + "move r{} {str} {}", + VariableScope::RETURN_REGISTER, + debug!(self, "#returnValue") + ))? + } VariableLocation::Stack(offset) => { self.write_output(format!( "sub r{} sp {offset}", diff --git a/rust_compiler/libs/compiler/src/variable_manager.rs b/rust_compiler/libs/compiler/src/variable_manager.rs index c22a39f..15f0aea 100644 --- a/rust_compiler/libs/compiler/src/variable_manager.rs +++ b/rust_compiler/libs/compiler/src/variable_manager.rs @@ -3,6 +3,7 @@ // r1 - r7 : Temporary Variables // r8 - r14 : Persistant Variables +use parser::tree_node::Literal; use quick_error::quick_error; use std::collections::{HashMap, VecDeque}; @@ -43,11 +44,14 @@ pub enum VariableLocation { Persistant(u8), /// 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), } pub struct VariableScope<'a> { temporary_vars: VecDeque, persistant_vars: VecDeque, + constant_vars: HashMap, var_lookup_table: HashMap, stack_offset: u16, parent: Option<&'a VariableScope<'a>>, @@ -61,6 +65,7 @@ impl<'a> Default for VariableScope<'a> { persistant_vars: PERSIST.to_vec().into(), temporary_vars: TEMP.to_vec().into(), var_lookup_table: HashMap::new(), + constant_vars: HashMap::new(), } } } @@ -93,6 +98,7 @@ impl<'a> VariableScope<'a> { parent: Option::Some(parent), temporary_vars: parent.temporary_vars.clone(), persistant_vars: parent.persistant_vars.clone(), + constant_vars: parent.constant_vars.clone(), ..Default::default() } } @@ -142,6 +148,20 @@ impl<'a> VariableScope<'a> { Ok(var_location) } + pub fn define_const( + &mut self, + var_name: impl Into, + value: Literal, + ) -> Result { + let var_name = var_name.into(); + if self.constant_vars.contains_key(&var_name) { + return Err(Error::DuplicateVariable(var_name)); + } + + self.constant_vars.insert(var_name, value.clone()); + Ok(VariableLocation::Constant(value)) + } + pub fn get_location_of(&self, var_name: impl Into) -> Result { let var_name = var_name.into(); @@ -190,7 +210,7 @@ impl<'a> VariableScope<'a> { "Attempted to free a `let` variable.", ))); } - VariableLocation::Stack(_) => {} + _ => {} }; Ok(())