v1 constant expressions

This commit is contained in:
2025-12-04 21:18:57 -07:00
parent 4b6c7eb63c
commit d34ce32d97
2 changed files with 106 additions and 9 deletions

View File

@@ -4,10 +4,10 @@ use parser::{
Parser as ASTParser, Parser as ASTParser,
sys_call::{SysCall, System}, sys_call::{SysCall, System},
tree_node::{ tree_node::{
AssignmentExpression, BinaryExpression, BlockExpression, DeviceDeclarationExpression, AssignmentExpression, BinaryExpression, BlockExpression, ConstDeclarationExpression,
Expression, FunctionExpression, IfExpression, InvocationExpression, Literal, DeviceDeclarationExpression, Expression, FunctionExpression, IfExpression,
LiteralOrVariable, LogicalExpression, LoopExpression, MemberAccessExpression, Span, InvocationExpression, Literal, LiteralOrVariable, LogicalExpression, LoopExpression,
Spanned, WhileExpression, MemberAccessExpression, Span, Spanned, WhileExpression,
}, },
}; };
use quick_error::quick_error; use quick_error::quick_error;
@@ -34,6 +34,20 @@ macro_rules! debug {
}; };
} }
fn extract_literal(literal: Literal, allow_strings: bool) -> Result<String, Error> {
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! { quick_error! {
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
@@ -58,6 +72,9 @@ quick_error! {
AgrumentMismatch(func_name: String, span: Span) { AgrumentMismatch(func_name: String, span: Span) {
display("Incorrect number of arguments passed into `{func_name}`") 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<Span>) { Unknown(reason: String, span: Option<Span>) {
display("{reason}") display("{reason}")
} }
@@ -84,6 +101,7 @@ impl From<Error> for lsp_types::Diagnostic {
DuplicateIdentifier(_, span) DuplicateIdentifier(_, span)
| UnknownIdentifier(_, span) | UnknownIdentifier(_, span)
| InvalidDevice(_, span) | InvalidDevice(_, span)
| ConstAssignment(_, span)
| AgrumentMismatch(_, span) => Diagnostic { | AgrumentMismatch(_, span) => Diagnostic {
range: span.into(), range: span.into(),
message: value.to_string(), message: value.to_string(),
@@ -267,6 +285,10 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
// decl_expr is Box<Spanned<Expression>> // decl_expr is Box<Spanned<Expression>>
self.expression_declaration(var_name, *decl_expr, scope) 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) => { Expression::Assignment(assign_expr) => {
self.expression_assignment(assign_expr.node, scope)?; self.expression_assignment(assign_expr.node, scope)?;
Ok(None) Ok(None)
@@ -435,6 +457,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
VariableLocation::Stack(_) => { VariableLocation::Stack(_) => {
self.write_output(format!("push {}{debug_tag}", source_value.into()))?; 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(()) Ok(())
@@ -581,6 +611,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
))?; ))?;
format!("r{}", VariableScope::TEMP_STACK_REGISTER) format!("r{}", VariableScope::TEMP_STACK_REGISTER)
} }
VariableLocation::Constant(_) => unreachable!(),
}; };
self.emit_variable_assignment(&name_str, &var_loc, src_str)?; self.emit_variable_assignment(&name_str, &var_loc, src_str)?;
(var_loc, None) (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<CompilationResult, Error> {
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>( fn expression_assignment<'v>(
&mut self, &mut self,
expr: AssignmentExpression, expr: AssignmentExpression,
@@ -685,6 +732,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
VariableScope::TEMP_STACK_REGISTER VariableScope::TEMP_STACK_REGISTER
))?; ))?;
} }
VariableLocation::Constant(_) => {
return Err(Error::ConstAssignment(identifier.node, identifier.span));
}
} }
if let Some(name) = cleanup { if let Some(name) = cleanup {
@@ -782,6 +832,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => { VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => {
self.write_output(format!("push r{reg}"))?; self.write_output(format!("push r{reg}"))?;
} }
VariableLocation::Constant(lit) => {
self.write_output(format!("push {}", extract_literal(lit, false)?))?;
}
VariableLocation::Stack(stack_offset) => { VariableLocation::Stack(stack_offset) => {
self.write_output(format!( self.write_output(format!(
"sub r{0} sp {stack_offset}", "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<String, Error> { fn resolve_register(&self, loc: &VariableLocation) -> Result<String, Error> {
match loc { match loc {
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => Ok(format!("r{r}")), 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( VariableLocation::Stack(_) => Err(Error::Unknown(
"Cannot resolve Stack location directly to register string without context".into(), "Cannot resolve Stack location directly to register string without context".into(),
None, None,
@@ -1090,6 +1147,11 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => { VariableLocation::Temporary(r) | VariableLocation::Persistant(r) => {
Ok((format!("r{r}"), result.temp_name)) 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) => { VariableLocation::Stack(offset) => {
// If it's on the stack, we must load it into a temp to use it as an operand // 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_name = self.next_temp_name();
@@ -1256,11 +1318,18 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
) -> Result<(), Error> { ) -> Result<(), Error> {
// First, sort the expressions to ensure functions are hoisted // First, sort the expressions to ensure functions are hoisted
expr.0.sort_by(|a, b| { expr.0.sort_by(|a, b| {
if matches!(b.node, Expression::Function(_)) if matches!(
&& matches!(a.node, Expression::Function(_)) b.node,
{ Expression::Function(_) | Expression::ConstDeclaration(_)
) && matches!(
a.node,
Expression::Function(_) | Expression::ConstDeclaration(_)
) {
std::cmp::Ordering::Equal std::cmp::Ordering::Equal
} else if matches!(a.node, Expression::Function(_)) { } else if matches!(
a.node,
Expression::Function(_) | Expression::ConstDeclaration(_)
) {
std::cmp::Ordering::Less std::cmp::Ordering::Less
} else { } else {
std::cmp::Ordering::Greater std::cmp::Ordering::Greater
@@ -1332,6 +1401,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
debug!(self, "#returnValue") 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) => { VariableLocation::Stack(offset) => {
self.write_output(format!( self.write_output(format!(
"sub r{} sp {offset}", "sub r{} sp {offset}",

View File

@@ -3,6 +3,7 @@
// r1 - r7 : Temporary Variables // r1 - r7 : Temporary Variables
// r8 - r14 : Persistant Variables // r8 - r14 : Persistant Variables
use parser::tree_node::Literal;
use quick_error::quick_error; use quick_error::quick_error;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
@@ -43,11 +44,14 @@ pub enum VariableLocation {
Persistant(u8), Persistant(u8),
/// Represents a a stack offset (current stack - offset = variable loc) /// Represents a a stack offset (current stack - offset = variable loc)
Stack(u16), Stack(u16),
/// Represents a constant value and should be directly substituted as such.
Constant(Literal),
} }
pub struct VariableScope<'a> { pub struct VariableScope<'a> {
temporary_vars: VecDeque<u8>, temporary_vars: VecDeque<u8>,
persistant_vars: VecDeque<u8>, persistant_vars: VecDeque<u8>,
constant_vars: HashMap<String, Literal>,
var_lookup_table: HashMap<String, VariableLocation>, var_lookup_table: HashMap<String, VariableLocation>,
stack_offset: u16, stack_offset: u16,
parent: Option<&'a VariableScope<'a>>, parent: Option<&'a VariableScope<'a>>,
@@ -61,6 +65,7 @@ impl<'a> Default for VariableScope<'a> {
persistant_vars: PERSIST.to_vec().into(), persistant_vars: PERSIST.to_vec().into(),
temporary_vars: TEMP.to_vec().into(), temporary_vars: TEMP.to_vec().into(),
var_lookup_table: HashMap::new(), var_lookup_table: HashMap::new(),
constant_vars: HashMap::new(),
} }
} }
} }
@@ -93,6 +98,7 @@ impl<'a> VariableScope<'a> {
parent: Option::Some(parent), parent: Option::Some(parent),
temporary_vars: parent.temporary_vars.clone(), temporary_vars: parent.temporary_vars.clone(),
persistant_vars: parent.persistant_vars.clone(), persistant_vars: parent.persistant_vars.clone(),
constant_vars: parent.constant_vars.clone(),
..Default::default() ..Default::default()
} }
} }
@@ -142,6 +148,20 @@ impl<'a> VariableScope<'a> {
Ok(var_location) Ok(var_location)
} }
pub fn define_const(
&mut self,
var_name: impl Into<String>,
value: Literal,
) -> Result<VariableLocation, Error> {
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<String>) -> Result<VariableLocation, Error> { pub fn get_location_of(&self, var_name: impl Into<String>) -> Result<VariableLocation, Error> {
let var_name = var_name.into(); let var_name = var_name.into();
@@ -190,7 +210,7 @@ impl<'a> VariableScope<'a> {
"Attempted to free a `let` variable.", "Attempted to free a `let` variable.",
))); )));
} }
VariableLocation::Stack(_) => {} _ => {}
}; };
Ok(()) Ok(())