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

View File

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

View File

@@ -1,5 +1,7 @@
#[cfg(test)]
mod test;
mod v2;
mod variable_manager;
use parser::Parser as ASTParser;
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()
}
}
}