Fixed exponent expression not being in right-first order of operations
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -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"
|
||||||
|
|||||||
@@ -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" }
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
// 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());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user