wip refactor
This commit is contained in:
23
Cargo.lock
generated
23
Cargo.lock
generated
@@ -248,10 +248,17 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"indoc",
|
"indoc",
|
||||||
"parser",
|
"parser",
|
||||||
|
"pretty_assertions",
|
||||||
"quick-error",
|
"quick-error",
|
||||||
"tokenizer",
|
"tokenizer",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diff"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -478,6 +485,16 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty_assertions"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||||
|
dependencies = [
|
||||||
|
"diff",
|
||||||
|
"yansi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.1.25"
|
version = "0.1.25"
|
||||||
@@ -1046,6 +1063,12 @@ dependencies = [
|
|||||||
"tap",
|
"tap",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yansi"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.27"
|
version = "0.8.27"
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ tokenizer = { path = "../tokenizer" }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = { version = "1.0" }
|
anyhow = { version = "1.0" }
|
||||||
indoc = { version = "2.0" }
|
indoc = { version = "2.0" }
|
||||||
|
pretty_assertions = "1"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use std::cmp::Ordering;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
|
|
||||||
pub use v2::{Compiler, Error};
|
pub use v2::{Compiler, CompilerConfig, Error};
|
||||||
|
|
||||||
quick_error! {
|
quick_error! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
85
libs/compiler/src/test/declaration_function_invocation.rs
Normal file
85
libs/compiler/src/test/declaration_function_invocation.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use crate::compile;
|
||||||
|
use indoc::indoc;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_arguments() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
fn doSomething() {};
|
||||||
|
let i = doSomething();
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
let to_test = indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
doSomething:
|
||||||
|
push ra
|
||||||
|
sub r0 sp 1
|
||||||
|
get ra db r0
|
||||||
|
sub sp sp 1
|
||||||
|
j ra
|
||||||
|
main:
|
||||||
|
jal doSomething
|
||||||
|
move r8 r15 #i
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(compiled, to_test);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_var_args() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
fn doSomething(arg1) {};
|
||||||
|
let arg1 = 123;
|
||||||
|
let i = doSomething(arg1);
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
doSomething:
|
||||||
|
pop r8 #arg1
|
||||||
|
push ra
|
||||||
|
sub r0 sp 1
|
||||||
|
get ra db r0
|
||||||
|
sub sp sp 1
|
||||||
|
j ra
|
||||||
|
main:
|
||||||
|
move r8 123 #arg1
|
||||||
|
push r8
|
||||||
|
push r8
|
||||||
|
jal doSomething
|
||||||
|
sub r0 sp 1
|
||||||
|
get r8 db r0
|
||||||
|
sub sp sp 1
|
||||||
|
move r9 r15 #i
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn incorrect_args_count() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {
|
||||||
|
debug
|
||||||
|
"
|
||||||
|
fn doSomething(arg1, arg2){};
|
||||||
|
let i = doSomething();
|
||||||
|
"
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
62
libs/compiler/src/test/declaration_literal.rs
Normal file
62
libs/compiler/src/test/declaration_literal.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use indoc::indoc;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variable_declaration_numeric_literal() -> anyhow::Result<()> {
|
||||||
|
let compiled = crate::compile! {
|
||||||
|
debug r#"
|
||||||
|
let i = 20c;
|
||||||
|
"#
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 293.15 #i
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variable_declaration_numeric_literal_stack_spillover() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile! {debug r#"
|
||||||
|
let a = 0;
|
||||||
|
let b = 1;
|
||||||
|
let c = 2;
|
||||||
|
let d = 3;
|
||||||
|
let e = 4;
|
||||||
|
let f = 5;
|
||||||
|
let g = 6;
|
||||||
|
let h = 7;
|
||||||
|
let i = 8;
|
||||||
|
let j = 9;
|
||||||
|
"#};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {
|
||||||
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
move r8 0 #a
|
||||||
|
move r9 1 #b
|
||||||
|
move r10 2 #c
|
||||||
|
move r11 3 #d
|
||||||
|
move r12 4 #e
|
||||||
|
move r13 5 #f
|
||||||
|
move r14 6 #g
|
||||||
|
push 7 #h
|
||||||
|
push 8 #i
|
||||||
|
push 9 #j
|
||||||
|
"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
58
libs/compiler/src/test/function_declaration.rs
Normal file
58
libs/compiler/src/test/function_declaration.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
use indoc::indoc;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> {
|
||||||
|
let compiled = compile!(debug r#"
|
||||||
|
// we need more than 4 params to 'spill' into a stack var
|
||||||
|
fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {};
|
||||||
|
"#);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
compiled,
|
||||||
|
indoc! {"
|
||||||
|
j main
|
||||||
|
doSomething:
|
||||||
|
pop r8 #arg9
|
||||||
|
pop r9 #arg8
|
||||||
|
pop r10 #arg7
|
||||||
|
pop r11 #arg6
|
||||||
|
pop r12 #arg5
|
||||||
|
pop r13 #arg4
|
||||||
|
pop r14 #arg3
|
||||||
|
push ra
|
||||||
|
sub r0 sp 1
|
||||||
|
get ra db r0
|
||||||
|
sub sp sp 3
|
||||||
|
j ra
|
||||||
|
"}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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:
|
||||||
|
pop r8 #arg2
|
||||||
|
pop r9 #arg1
|
||||||
|
push ra
|
||||||
|
sub r0 sp 1
|
||||||
|
get ra db r0
|
||||||
|
sub sp sp 1
|
||||||
|
j ra
|
||||||
|
"}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,21 +1,15 @@
|
|||||||
use super::v2::Compiler;
|
|
||||||
use crate::v2::CompilerConfig;
|
|
||||||
use indoc::indoc;
|
|
||||||
use parser::Parser;
|
|
||||||
use std::io::BufWriter;
|
|
||||||
use tokenizer::Tokenizer;
|
|
||||||
|
|
||||||
macro_rules! output {
|
macro_rules! output {
|
||||||
($input:expr) => {
|
($input:expr) => {
|
||||||
String::from_utf8($input.into_inner()?)?
|
String::from_utf8($input.into_inner()?)?
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
macro_rules! compile {
|
macro_rules! compile {
|
||||||
($source:expr) => {{
|
($source:expr) => {{
|
||||||
let mut writer = BufWriter::new(Vec::new());
|
let mut writer = std::io::BufWriter::new(Vec::new());
|
||||||
let compiler = Compiler::new(
|
let compiler = crate::Compiler::new(
|
||||||
Parser::new(Tokenizer::from(String::from($source))),
|
parser::Parser::new(tokenizer::Tokenizer::from(String::from($source))),
|
||||||
&mut writer,
|
&mut writer,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
@@ -24,73 +18,16 @@ macro_rules! compile {
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
(debug $source:expr) => {{
|
(debug $source:expr) => {{
|
||||||
let mut writer = BufWriter::new(Vec::new());
|
let mut writer = std::io::BufWriter::new(Vec::new());
|
||||||
let compiler = Compiler::new(
|
let compiler = crate::Compiler::new(
|
||||||
Parser::new(Tokenizer::from(String::from($source))),
|
parser::Parser::new(tokenizer::Tokenizer::from(String::from($source))),
|
||||||
&mut writer,
|
&mut writer,
|
||||||
Some(CompilerConfig {
|
Some(crate::CompilerConfig { debug: true }),
|
||||||
debug: true,
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
compiler.compile()?;
|
compiler.compile()?;
|
||||||
output!(writer)
|
output!(writer)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
mod declaration_function_invocation;
|
||||||
#[test]
|
mod declaration_literal;
|
||||||
fn test_function_declaration_with_spillover_params() -> anyhow::Result<()> {
|
mod function_declaration;
|
||||||
let compiled = compile!(debug r#"
|
|
||||||
// we need more than 4 params to 'spill' into a stack var
|
|
||||||
fn doSomething(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) {};
|
|
||||||
"#);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
compiled,
|
|
||||||
indoc! {"
|
|
||||||
j main
|
|
||||||
doSomething:
|
|
||||||
pop r8 #arg9
|
|
||||||
pop r9 #arg8
|
|
||||||
pop r10 #arg7
|
|
||||||
pop r11 #arg6
|
|
||||||
pop r12 #arg5
|
|
||||||
pop r13 #arg4
|
|
||||||
pop r14 #arg3
|
|
||||||
pop r15 #arg2
|
|
||||||
push ra
|
|
||||||
sub r0 sp 1
|
|
||||||
get ra db r0
|
|
||||||
sub sp sp 2
|
|
||||||
j ra
|
|
||||||
"}
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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:
|
|
||||||
pop r8 #arg2
|
|
||||||
pop r9 #arg1
|
|
||||||
push ra
|
|
||||||
sub r0 sp 1
|
|
||||||
get ra db r0
|
|
||||||
sub sp sp 1
|
|
||||||
j ra
|
|
||||||
"}
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::variable_manager::{self, LocationRequest, VariableLocation, VariableScope};
|
use crate::variable_manager::{self, LocationRequest, VariableLocation, VariableScope};
|
||||||
use parser::{
|
use parser::{
|
||||||
Parser as ASTParser,
|
Parser as ASTParser,
|
||||||
tree_node::{BlockExpression, DeviceDeclarationExpression, Expression, FunctionExpression},
|
tree_node::{
|
||||||
|
BlockExpression, DeviceDeclarationExpression, Expression, FunctionExpression,
|
||||||
|
InvocationExpression, Literal,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use quick_error::quick_error;
|
use quick_error::quick_error;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -9,6 +12,16 @@ use std::{
|
|||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
macro_rules! debug {
|
||||||
|
($self: expr, $debug_value: expr) => {
|
||||||
|
if $self.config.debug {
|
||||||
|
format!($debug_value)
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
quick_error! {
|
quick_error! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -22,10 +35,16 @@ quick_error! {
|
|||||||
from()
|
from()
|
||||||
}
|
}
|
||||||
DuplicateFunction(func_name: String) {
|
DuplicateFunction(func_name: String) {
|
||||||
display("{func_name} has already been defined")
|
display("`{func_name}` has already been defined")
|
||||||
|
}
|
||||||
|
UnknownIdentifier(ident: String) {
|
||||||
|
display("`{ident}` is not found in the current scope.")
|
||||||
}
|
}
|
||||||
InvalidDevice(device: String) {
|
InvalidDevice(device: String) {
|
||||||
display("{device} is not valid")
|
display("`{device}` is not valid")
|
||||||
|
}
|
||||||
|
AgrumentMismatch(func_name: String) {
|
||||||
|
display("Incorrect number of arguments passed into `{func_name}`")
|
||||||
}
|
}
|
||||||
Unknown(reason: String) {
|
Unknown(reason: String) {
|
||||||
display("{reason}")
|
display("{reason}")
|
||||||
@@ -41,6 +60,7 @@ pub struct CompilerConfig {
|
|||||||
pub struct Compiler<'a, W: std::io::Write> {
|
pub struct Compiler<'a, W: std::io::Write> {
|
||||||
parser: ASTParser,
|
parser: ASTParser,
|
||||||
function_locations: HashMap<String, usize>,
|
function_locations: HashMap<String, usize>,
|
||||||
|
function_metadata: HashMap<String, Vec<String>>,
|
||||||
devices: HashMap<String, String>,
|
devices: HashMap<String, String>,
|
||||||
output: &'a mut BufWriter<W>,
|
output: &'a mut BufWriter<W>,
|
||||||
current_line: usize,
|
current_line: usize,
|
||||||
@@ -57,6 +77,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Self {
|
Self {
|
||||||
parser,
|
parser,
|
||||||
function_locations: HashMap::new(),
|
function_locations: HashMap::new(),
|
||||||
|
function_metadata: HashMap::new(),
|
||||||
devices: HashMap::new(),
|
devices: HashMap::new(),
|
||||||
output: writer,
|
output: writer,
|
||||||
current_line: 1,
|
current_line: 1,
|
||||||
@@ -93,6 +114,9 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Expression::Declaration(var_name, expr) => {
|
Expression::Declaration(var_name, expr) => {
|
||||||
self.expression_declaration(var_name, *expr, scope)?
|
self.expression_declaration(var_name, *expr, scope)?
|
||||||
}
|
}
|
||||||
|
Expression::Invocation(expr_invoke) => {
|
||||||
|
self.expression_function_invocation(expr_invoke, scope)?
|
||||||
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,16 +129,135 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
expr: Expression,
|
expr: Expression,
|
||||||
scope: &mut VariableScope<'v>,
|
scope: &mut VariableScope<'v>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
scope.add_variable(var_name.clone(), LocationRequest::Persist)?;
|
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
_ => unimplemented!(),
|
Expression::Literal(Literal::Number(num)) => {
|
||||||
|
let var_location =
|
||||||
|
scope.add_variable(var_name.clone(), LocationRequest::Persist)?;
|
||||||
|
let num_str = num.to_string();
|
||||||
|
|
||||||
|
if let VariableLocation::Temporary(reg) | VariableLocation::Persistant(reg) =
|
||||||
|
var_location
|
||||||
|
{
|
||||||
|
self.write_output(format!(
|
||||||
|
"move r{reg} {num_str} {}",
|
||||||
|
debug!(self, "#{var_name}")
|
||||||
|
))?;
|
||||||
|
} else {
|
||||||
|
self.write_output(format!("push {num_str} {}", debug!(self, "#{var_name}")))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::Invocation(invoke_expr) => {
|
||||||
|
self.expression_function_invocation(invoke_expr, scope)?;
|
||||||
|
|
||||||
|
// Return value _should_ be in VariableScope::RETURN_REGISTER
|
||||||
|
match scope.add_variable(var_name.clone(), LocationRequest::Persist)? {
|
||||||
|
VariableLocation::Temporary(reg) | VariableLocation::Persistant(reg) => self
|
||||||
|
.write_output(format!(
|
||||||
|
"move r{reg} r{} {}",
|
||||||
|
VariableScope::RETURN_REGISTER,
|
||||||
|
debug!(self, "#{var_name}")
|
||||||
|
))?,
|
||||||
|
VariableLocation::Stack(_) => self.write_output(format!(
|
||||||
|
"push r{} {}",
|
||||||
|
VariableScope::RETURN_REGISTER,
|
||||||
|
debug!(self, "#{var_name}")
|
||||||
|
))?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(Error::Unknown(
|
||||||
|
"`{var_name}` declaration of this type is not supported.".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression_device<'v>(&mut self, expr: DeviceDeclarationExpression) {
|
fn expression_function_invocation(
|
||||||
|
&mut self,
|
||||||
|
invoke_expr: InvocationExpression,
|
||||||
|
stack: &mut VariableScope,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if !self.function_locations.contains_key(&invoke_expr.name) {
|
||||||
|
return Err(Error::UnknownIdentifier(invoke_expr.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(args) = self.function_metadata.get(&invoke_expr.name) else {
|
||||||
|
return Err(Error::UnknownIdentifier(invoke_expr.name));
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.len() != invoke_expr.arguments.len() {
|
||||||
|
return Err(Error::AgrumentMismatch(invoke_expr.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// backup all used registers to the stack
|
||||||
|
let active_registers = stack.registers().cloned().collect::<Vec<_>>();
|
||||||
|
for register in &active_registers {
|
||||||
|
stack.add_variable(format!("temp_{register}"), LocationRequest::Stack)?;
|
||||||
|
self.write_output(format!("push r{register}"))?;
|
||||||
|
}
|
||||||
|
for arg in invoke_expr.arguments {
|
||||||
|
match arg {
|
||||||
|
Expression::Literal(Literal::Number(num)) => {
|
||||||
|
let num_str = num.to_string();
|
||||||
|
self.write_output(format!("push {num_str}"))?;
|
||||||
|
}
|
||||||
|
Expression::Variable(var_name) => match stack.get_location_of(var_name)? {
|
||||||
|
VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => {
|
||||||
|
self.write_output(format!("push r{reg}"))?;
|
||||||
|
}
|
||||||
|
VariableLocation::Stack(stack_offset) => {
|
||||||
|
self.write_output(format!(
|
||||||
|
"sub r{0} sp {stack_offset}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
self.write_output(format!(
|
||||||
|
"get r{0} db r{0}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
self.write_output(format!(
|
||||||
|
"push r{0}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(Error::Unknown(format!(
|
||||||
|
"Attempted to call `{}` with an unsupported argument",
|
||||||
|
invoke_expr.name
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jump to the function and store current line in ra
|
||||||
|
self.write_output(format!("jal {}", invoke_expr.name))?;
|
||||||
|
|
||||||
|
for register in active_registers {
|
||||||
|
let VariableLocation::Stack(stack_offset) =
|
||||||
|
stack.get_location_of(format!("temp_{register}"))?
|
||||||
|
else {
|
||||||
|
return Err(Error::UnknownIdentifier(format!("temp_{register}")));
|
||||||
|
};
|
||||||
|
self.write_output(format!(
|
||||||
|
"sub r{0} sp {stack_offset}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
self.write_output(format!(
|
||||||
|
"get r{register} db r{0}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stack.stack_offset() > 0 {
|
||||||
|
self.write_output(format!("sub sp sp {}", stack.stack_offset()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_device(&mut self, expr: DeviceDeclarationExpression) {
|
||||||
self.devices.insert(expr.name, expr.device);
|
self.devices.insert(expr.name, expr.device);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,14 +309,17 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
return Err(Error::DuplicateFunction(name));
|
return Err(Error::DuplicateFunction(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.function_locations
|
self.function_metadata
|
||||||
.insert(name.clone(), self.current_line);
|
.insert(name.clone(), arguments.clone());
|
||||||
|
|
||||||
// Declare the function as a line identifier
|
// Declare the function as a line identifier
|
||||||
self.write_output(format!("{}:", name))?;
|
self.write_output(format!("{}:", name))?;
|
||||||
|
|
||||||
|
self.function_locations
|
||||||
|
.insert(name.clone(), self.current_line);
|
||||||
|
|
||||||
// Create a new block scope for the function body
|
// Create a new block scope for the function body
|
||||||
let mut block_scope = VariableScope::scoped(&scope);
|
let mut block_scope = VariableScope::scoped(scope);
|
||||||
|
|
||||||
let mut saved_variables = 0;
|
let mut saved_variables = 0;
|
||||||
|
|
||||||
@@ -189,14 +335,7 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
|
|
||||||
match loc {
|
match loc {
|
||||||
VariableLocation::Persistant(loc) => {
|
VariableLocation::Persistant(loc) => {
|
||||||
self.write_output(format!(
|
self.write_output(format!("pop r{loc} {}", debug!(self, "#{var_name}")))?;
|
||||||
"pop r{loc} {}",
|
|
||||||
if self.config.debug {
|
|
||||||
format!("#{}", var_name)
|
|
||||||
} else {
|
|
||||||
"".into()
|
|
||||||
}
|
|
||||||
))?;
|
|
||||||
}
|
}
|
||||||
VariableLocation::Stack(_) => {
|
VariableLocation::Stack(_) => {
|
||||||
return Err(Error::Unknown(
|
return Err(Error::Unknown(
|
||||||
@@ -234,8 +373,14 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
self.write_output(format!("sub r0 sp {ra_stack_offset}"))?;
|
self.write_output(format!(
|
||||||
self.write_output("get ra db r0")?;
|
"sub r{0} sp {ra_stack_offset}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
self.write_output(format!(
|
||||||
|
"get ra db r{0}",
|
||||||
|
VariableScope::TEMP_STACK_REGISTER
|
||||||
|
))?;
|
||||||
|
|
||||||
if block_scope.stack_offset() > 0 {
|
if block_scope.stack_offset() > 0 {
|
||||||
self.write_output(format!("sub sp sp {}", block_scope.stack_offset()))?;
|
self.write_output(format!("sub sp sp {}", block_scope.stack_offset()))?;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use quick_error::quick_error;
|
|||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
const TEMP: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
|
const TEMP: [u8; 7] = [1, 2, 3, 4, 5, 6, 7];
|
||||||
const PERSIST: [u8; 8] = [8, 9, 10, 11, 12, 13, 14, 15];
|
const PERSIST: [u8; 7] = [8, 9, 10, 11, 12, 13, 14];
|
||||||
|
|
||||||
quick_error! {
|
quick_error! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -72,8 +72,26 @@ impl<'a> Default for VariableScope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VariableScope<'a> {
|
impl<'a> VariableScope<'a> {
|
||||||
pub const TEMP_REGISTER_COUNT: u8 = 8;
|
pub const TEMP_REGISTER_COUNT: u8 = 7;
|
||||||
pub const PERSIST_REGISTER_COUNT: u8 = 8;
|
pub const PERSIST_REGISTER_COUNT: u8 = 7;
|
||||||
|
|
||||||
|
pub const RETURN_REGISTER: u8 = 15;
|
||||||
|
pub const TEMP_STACK_REGISTER: u8 = 0;
|
||||||
|
|
||||||
|
pub fn registers(&self) -> impl Iterator<Item = &u8> {
|
||||||
|
self.var_lookup_table
|
||||||
|
.values()
|
||||||
|
.filter(|val| {
|
||||||
|
matches!(
|
||||||
|
val,
|
||||||
|
VariableLocation::Temporary(_) | VariableLocation::Persistant(_)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(|loc| match loc {
|
||||||
|
VariableLocation::Persistant(reg) | VariableLocation::Temporary(reg) => reg,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scoped(parent: &'a VariableScope<'a>) -> Self {
|
pub fn scoped(parent: &'a VariableScope<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
Reference in New Issue
Block a user