Fixed exponent expression not being in right-first order of operations

This commit is contained in:
2025-11-17 21:20:32 -07:00
parent f0a3bbe739
commit 5a78e25469
7 changed files with 297 additions and 8 deletions

12
Cargo.lock generated
View File

@@ -245,8 +245,11 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
name = "compiler" name = "compiler"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"indoc",
"parser", "parser",
"quick-error", "quick-error",
"tokenizer",
] ]
[[package]] [[package]]
@@ -344,6 +347,15 @@ dependencies = [
"hashbrown 0.16.0", "hashbrown 0.16.0",
] ]
[[package]]
name = "indoc"
version = "2.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "inventory" name = "inventory"
version = "0.3.21" version = "0.3.21"

View File

@@ -6,3 +6,8 @@ edition = "2024"
[dependencies] [dependencies]
quick-error = { workspace = true } quick-error = { workspace = true }
parser = { path = "../parser" } parser = { path = "../parser" }
tokenizer = { path = "../tokenizer" }
[dev-dependencies]
anyhow = { version = "1.0" }
indoc = { version = "2.0" }

View File

@@ -1,5 +1,7 @@
#[cfg(test)] #[cfg(test)]
mod test; mod test;
mod v2;
mod variable_manager;
use parser::Parser as ASTParser; use parser::Parser as ASTParser;
use parser::sys_call::SysCall; use parser::sys_call::SysCall;

View File

@@ -0,0 +1,67 @@
use super::v2::Compiler;
use crate::v2::CompilerConfig;
use indoc::indoc;
use parser::Parser;
use std::io::BufWriter;
use tokenizer::Tokenizer;
macro_rules! output {
($input:expr) => {
String::from_utf8($input.into_inner()?)?
};
}
macro_rules! compile {
($source:expr) => {{
let mut writer = BufWriter::new(Vec::new());
let compiler = Compiler::new(
Parser::new(Tokenizer::from(String::from($source))),
&mut writer,
None,
);
compiler.compile()?;
output!(writer)
}};
(debug $source:expr) => {{
let mut writer = BufWriter::new(Vec::new());
let compiler = Compiler::new(
Parser::new(Tokenizer::from(String::from($source))),
&mut writer,
Some(CompilerConfig {
debug: true,
..Default::default()
}),
);
compiler.compile()?;
output!(writer)
}};
}
#[test]
fn test_function_declaration_with_register_params() -> anyhow::Result<()> {
let compiled = compile!(debug r#"
// This is a test function declaration with no body
fn doSomething(arg1, arg2) {
};
"#);
assert_eq!(
compiled,
indoc! {"
j main
doSomething:
push ra
push r4
move r4 r0 #arg1
push r5
move r5 r1 #arg2
pop r5
pop r4
pop ra
j ra
"}
);
Ok(())
}

170
libs/compiler/src/v2.rs Normal file
View File

@@ -0,0 +1,170 @@
use parser::{
Parser as ASTParser,
tree_node::{BlockExpression, Expression, FunctionExpression},
};
use quick_error::quick_error;
use std::{
collections::HashMap,
io::{BufWriter, Write},
};
use crate::variable_manager::VariableScope;
quick_error! {
#[derive(Debug)]
pub enum CompilerError {
ParseError(error: parser::ParseError) {
from()
}
IoError(error: std::io::Error) {
from()
}
DuplicateFunction(func_name: String) {
display("{func_name} has already been defined")
}
}
}
#[derive(Default)]
pub struct CompilerConfig {
pub debug: bool,
}
pub struct Compiler<'a, W: std::io::Write> {
parser: ASTParser,
/// Max stack size for the program is by default 512.
variable_scope: Vec<HashMap<String, i32>>,
function_locations: HashMap<String, usize>,
devices: HashMap<String, String>,
output: &'a mut BufWriter<W>,
current_line: usize,
declared_main: bool,
config: CompilerConfig,
}
impl<'a, W: std::io::Write> Compiler<'a, W> {
pub fn new(
parser: ASTParser,
writer: &'a mut BufWriter<W>,
config: Option<CompilerConfig>,
) -> Self {
Self {
parser,
variable_scope: Vec::new(),
function_locations: HashMap::new(),
devices: HashMap::new(),
output: writer,
current_line: 1,
declared_main: false,
config: config.unwrap_or_default(),
}
}
pub fn compile(mut self) -> Result<(), CompilerError> {
let expr = self.parser.parse_all()?;
let Some(expr) = expr else { return Ok(()) };
self.write_output("j main")?;
self.expression(expr, &mut VariableScope::default())
}
fn write_output(&mut self, output: impl Into<String>) -> Result<(), CompilerError> {
self.output.write_all(output.into().as_bytes())?;
self.output.write_all(b"\n")?;
self.current_line += 1;
Ok(())
}
fn expression<'v>(
&mut self,
expr: Expression,
scope: &mut VariableScope<'v>,
) -> Result<(), CompilerError> {
match expr {
Expression::Function(expr_func) => self.expression_function(expr_func, scope)?,
Expression::Block(expr_block) => {
self.expression_block(expr_block, &mut VariableScope::scoped(&scope))?
}
_ => todo!(),
};
Ok(())
}
fn expression_block<'v>(
&mut self,
mut expr: BlockExpression,
scope: &mut VariableScope<'v>,
) -> Result<(), CompilerError> {
// First, sort the expressions to ensure functions are hoisted
expr.0.sort_by(|a, b| {
if matches!(b, Expression::Function(_)) && matches!(a, Expression::Function(_)) {
std::cmp::Ordering::Equal
} else if matches!(a, Expression::Function(_)) {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
});
for expr in expr.0 {
if !self.declared_main && !matches!(expr, Expression::Function(_)) {
self.write_output("main:")?;
self.declared_main = true;
}
self.expression(expr, scope)?;
}
Ok(())
}
fn expression_function<'v>(
&mut self,
expr: FunctionExpression,
scope: &mut VariableScope<'v>,
) -> Result<(), CompilerError> {
let FunctionExpression {
name,
arguments,
body,
} = expr;
if self.function_locations.contains_key(&name) {
return Err(CompilerError::DuplicateFunction(name));
}
self.function_locations
.insert(name.clone(), self.current_line);
self.write_output(format!("{}:", name))?;
self.write_output("push ra")?;
let mut block_scope = VariableScope::scoped(&scope);
for (index, var_name) in arguments.iter().enumerate() {
self.write_output(format!("push r{}", index + 4))?;
self.write_output(format!(
"move r{} r{} {}",
index + 4,
index,
if self.config.debug {
format!("#{}", var_name)
} else {
"".into()
}
))?;
}
self.expression_block(body, &mut block_scope)?;
for (indx, _) in arguments.iter().enumerate().rev() {
self.write_output(format!("pop r{}", indx + 4))?;
}
self.write_output("pop ra")?;
self.write_output("j ra")?;
Ok(())
}
}

View File

@@ -0,0 +1,33 @@
// r0 - r3 : function arguments
// r4 - r9 : temporary variables
// r10 - r15 : persistant variables
use std::collections::HashMap;
enum VarType {
/// Represents a parameter register (r0 - r3)
Func(u8),
/// Represents a temporary register (r4 - r8)
Temp(u8),
/// Represents a variable register (r9 - r15)
Persist(u8),
/// Represents a stack pointer offset for a given variable
Stack(u16),
}
#[derive(Default)]
pub struct VariableScope<'a> {
stack: Vec<HashMap<&'a str, usize>>,
temp: HashMap<&'a str, u8>,
persist: HashMap<&'a str, u8>,
parent: Option<&'a VariableScope<'a>>,
}
impl<'a> VariableScope<'a> {
pub fn scoped(parent: &'a VariableScope<'a>) -> Self {
Self {
parent: Option::Some(parent),
..Default::default()
}
}
}

View File

@@ -361,25 +361,22 @@ impl Parser {
// Every time we find a valid operator, we pop 2 off the expressions and add one back. // Every time we find a valid operator, we pop 2 off the expressions and add one back.
// This means that we need to keep track of the current iteration to ensure we are // This means that we need to keep track of the current iteration to ensure we are
// removing the correct expressions from the vector // removing the correct expressions from the vector
let mut current_iteration = 0;
// Loop through operators, and build the binary expressions for exponential operators only // Loop through operators, and build the binary expressions for exponential operators only
for (i, operator) in operators.iter().enumerate() { for (i, operator) in operators.iter().enumerate().rev() {
if operator == &Symbol::Exp { if operator == &Symbol::Exp {
let index = i - current_iteration; let right = expressions.remove(i + 1);
let left = expressions.remove(index); let left = expressions.remove(i);
let right = expressions.remove(index);
expressions.insert( expressions.insert(
index, i,
Expression::Binary(BinaryExpression::Exponent(boxed!(left), boxed!(right))), Expression::Binary(BinaryExpression::Exponent(boxed!(left), boxed!(right))),
); );
current_iteration += 1;
} }
} }
// remove all the exponential operators from the operators vector // remove all the exponential operators from the operators vector
operators.retain(|symbol| symbol != &Symbol::Exp); operators.retain(|symbol| symbol != &Symbol::Exp);
current_iteration = 0; let mut current_iteration = 0;
// Loop through operators, and build the binary expressions for multiplication and division operators // Loop through operators, and build the binary expressions for multiplication and division operators
for (i, operator) in operators.iter().enumerate() { for (i, operator) in operators.iter().enumerate() {
@@ -982,6 +979,9 @@ mod tests {
let expr = parser!("4 ** 2 + 5 ** 2").parse()?.unwrap(); let expr = parser!("4 ** 2 + 5 ** 2").parse()?.unwrap();
assert_eq!("((4 ** 2) + (5 ** 2))", expr.to_string()); assert_eq!("((4 ** 2) + (5 ** 2))", expr.to_string());
let expr = parser!("2 ** 3 ** 4").parse()?.unwrap();
assert_eq!("(2 ** (3 ** 4))", expr.to_string());
let expr = parser!("45 * 2 - 15 / 5 + 5 ** 2").parse()?.unwrap(); let expr = parser!("45 * 2 - 15 / 5 + 5 ** 2").parse()?.unwrap();
assert_eq!("(((45 * 2) - (15 / 5)) + (5 ** 2))", expr.to_string()); assert_eq!("(((45 * 2) - (15 / 5)) + (5 ** 2))", expr.to_string());