Fixed exponent expression not being in right-first order of operations
This commit is contained in:
@@ -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" }
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
mod v2;
|
||||
mod variable_manager;
|
||||
|
||||
use parser::Parser as ASTParser;
|
||||
use parser::sys_call::SysCall;
|
||||
|
||||
@@ -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
170
libs/compiler/src/v2.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
33
libs/compiler/src/variable_manager.rs
Normal file
33
libs/compiler/src/variable_manager.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,25 +361,22 @@ impl Parser {
|
||||
// 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
|
||||
// removing the correct expressions from the vector
|
||||
let mut current_iteration = 0;
|
||||
|
||||
// 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 {
|
||||
let index = i - current_iteration;
|
||||
let left = expressions.remove(index);
|
||||
let right = expressions.remove(index);
|
||||
let right = expressions.remove(i + 1);
|
||||
let left = expressions.remove(i);
|
||||
expressions.insert(
|
||||
index,
|
||||
i,
|
||||
Expression::Binary(BinaryExpression::Exponent(boxed!(left), boxed!(right))),
|
||||
);
|
||||
current_iteration += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// remove all the exponential operators from the operators vector
|
||||
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
|
||||
for (i, operator) in operators.iter().enumerate() {
|
||||
@@ -982,6 +979,9 @@ mod tests {
|
||||
let expr = parser!("4 ** 2 + 5 ** 2").parse()?.unwrap();
|
||||
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();
|
||||
assert_eq!("(((45 * 2) - (15 / 5)) + (5 ** 2))", expr.to_string());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user