compiler wip:
This commit is contained in:
18
output.stationeers
Normal file
18
output.stationeers
Normal file
@@ -0,0 +1,18 @@
|
||||
j main
|
||||
sub sp sp 3
|
||||
pop ra
|
||||
j ra
|
||||
push 9
|
||||
push 1
|
||||
push 2
|
||||
push 3
|
||||
j 1
|
||||
sub sp sp 3
|
||||
pop ra
|
||||
j ra
|
||||
main:
|
||||
push 19
|
||||
push 1
|
||||
push 2
|
||||
push 3
|
||||
j 4
|
||||
@@ -1,10 +1,8 @@
|
||||
use crate::parser::tree_node::*;
|
||||
use crate::parser::Parser as ASTParser;
|
||||
use std::borrow::BorrowMut;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::io::BufWriter;
|
||||
use std::io::Write;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
/// Represents the return keyword. Used as a variable name for the register.
|
||||
const RETURN: &'static str = "ret";
|
||||
@@ -23,71 +21,90 @@ quick_error! {
|
||||
from()
|
||||
display("Write error: {}", err)
|
||||
}
|
||||
DuplicateVariable(variable: String) {
|
||||
display("A variable with the same name already exists in the current scope: {}", variable)
|
||||
}
|
||||
VariableNotFound(variable: String) {
|
||||
display("Variable {} was not found in the current scope.", variable)
|
||||
}
|
||||
MissingFunction(name: String) {
|
||||
display("Function {} was not found in the function table.", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! variable_index {
|
||||
($compiler:expr, $name:expr) => {
|
||||
$compiler
|
||||
.variable_scope
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|v| v.contains_key(&$name))
|
||||
.map(|v| v[&$name])
|
||||
.ok_or(CompileError::ScopeError)?
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Compiler<'a> {
|
||||
parser: ASTParser,
|
||||
/// Max stack size for the program is by default 512.
|
||||
variable_scope: Vec<HashMap<String, usize>>,
|
||||
variable_scope: Vec<HashMap<String, i32>>,
|
||||
function_locations: HashMap<String, usize>,
|
||||
output: &'a mut BufWriter<Box<dyn Write>>,
|
||||
stack_pointer: usize,
|
||||
/// A map of variable names to register numbers. 0-15 are reserved for variables, 16 is the stack pointer, 17 is the return address
|
||||
register: VecDeque<u8>,
|
||||
max_stack_size: usize,
|
||||
current_line: usize,
|
||||
declared_main: bool,
|
||||
}
|
||||
|
||||
impl<'a> Compiler<'a> {
|
||||
pub fn new(
|
||||
parser: ASTParser,
|
||||
max_stack_size: usize,
|
||||
writer: &'a mut BufWriter<Box<dyn Write>>,
|
||||
) -> Self {
|
||||
pub fn new(parser: ASTParser, writer: &'a mut BufWriter<Box<dyn Write>>) -> Self {
|
||||
Self {
|
||||
parser,
|
||||
variable_scope: Vec::new(),
|
||||
function_locations: HashMap::new(),
|
||||
output: writer,
|
||||
stack_pointer: 0,
|
||||
register: VecDeque::new(),
|
||||
max_stack_size,
|
||||
current_line: 0,
|
||||
declared_main: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_variable_index(&self, var_name: &str) -> Result<i32, CompileError> {
|
||||
let mut offset = 0;
|
||||
|
||||
for scope in &self.variable_scope {
|
||||
if let Some(index) = scope.get(var_name) {
|
||||
return Ok(*index + offset);
|
||||
}
|
||||
|
||||
offset += scope.len() as i32;
|
||||
}
|
||||
|
||||
Err(CompileError::VariableNotFound(var_name.to_owned()))
|
||||
}
|
||||
|
||||
fn push_stack(&mut self, var_name: &str) -> Result<(), CompileError> {
|
||||
// check to make sure the variable doesn't already exist in the current scope
|
||||
if self
|
||||
.variable_scope
|
||||
.last()
|
||||
.ok_or(CompileError::ScopeError)?
|
||||
.contains_key(var_name)
|
||||
{
|
||||
return Err(CompileError::DuplicateVariable(var_name.to_string()));
|
||||
}
|
||||
|
||||
let scope_size = self
|
||||
.variable_scope
|
||||
.last()
|
||||
.ok_or(CompileError::ScopeError)?
|
||||
.len();
|
||||
|
||||
self.variable_scope
|
||||
.last_mut()
|
||||
.ok_or(CompileError::ScopeError)?
|
||||
.insert(var_name.to_string(), scope_size as i32);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_output(&mut self, output: impl Into<String>) -> Result<(), CompileError> {
|
||||
self.output.write(output.into().as_bytes())?;
|
||||
self.output.write(b"\n")?;
|
||||
self.current_line += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_register(&mut self) -> Result<(), CompileError> {
|
||||
if self.register.len() >= 15 {
|
||||
return Err(CompileError::ScopeError);
|
||||
}
|
||||
self.register.push_back(self.register.len() as u8);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_register(&mut self) -> Result<u8, CompileError> {
|
||||
self.register.pop_back().ok_or(CompileError::ScopeError)
|
||||
}
|
||||
|
||||
pub fn compile(mut self) -> Result<(), CompileError> {
|
||||
let ast = self.parser.parse_all()?;
|
||||
|
||||
@@ -95,72 +112,119 @@ impl<'a> Compiler<'a> {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
self.expression(ast)?;
|
||||
// Jump directly to the main block. This will avoid executing functions before the main block.
|
||||
self.write_output(format!("j main"))?;
|
||||
|
||||
self.expression(ast)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expression(&mut self, expression: Expression) -> Result<(), CompileError> {
|
||||
match expression {
|
||||
Expression::BinaryExpression(expr) => self.binary_expression(expr)?,
|
||||
Expression::BlockExpression(expr) => self.block_expression(expr)?,
|
||||
Expression::DeclarationExpression(ident, expr) => {
|
||||
self.declaration_expression(ident, *expr)?
|
||||
}
|
||||
Expression::FunctionExpression(expr) => self.function_expression(expr)?,
|
||||
|
||||
Expression::BlockExpression(expr) => self.block_expression(expr)?,
|
||||
Expression::InvocationExpression(expr) => self.invocation_expression(expr)?,
|
||||
Expression::BinaryExpression(expr) => self.binary_expression(expr)?,
|
||||
_ => todo!("{:?}", expression),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn function_expression(&mut self, expr: FunctionExpression) -> Result<(), CompileError> {
|
||||
// in stack order: return address, arguments
|
||||
fn binary_expression(&mut self, expr: BinaryExpression) -> Result<(), CompileError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
self.function_locations
|
||||
.insert(expr.name.clone(), self.current_line);
|
||||
fn invocation_expression(&mut self, expr: InvocationExpression) -> Result<(), CompileError> {
|
||||
let function_name = expr.name;
|
||||
|
||||
self.variable_scope.push(HashMap::new());
|
||||
let total_args = expr.arguments.len();
|
||||
for (index, arg) in expr.arguments.iter().enumerate() {
|
||||
let function_line = self
|
||||
.function_locations
|
||||
.get(&function_name)
|
||||
.ok_or(CompileError::MissingFunction(function_name.clone()))?
|
||||
.clone();
|
||||
|
||||
let mut to_write = String::new();
|
||||
|
||||
let mut iter_index = 0;
|
||||
for arg in expr.arguments {
|
||||
match arg {
|
||||
Expression::Literal(Literal::Number(num)) => {
|
||||
to_write.push_str(&format!("push {}\n", num));
|
||||
}
|
||||
Expression::Variable(var_name) => {
|
||||
let index = self.get_variable_index(&var_name)?;
|
||||
to_write.push_str(&format!("sub r0 sp {index}\n"));
|
||||
to_write.push_str("get r0 db r0\n");
|
||||
to_write.push_str("push r0\n");
|
||||
self.push_stack(&format!("{function_name}{iter_index}"))?;
|
||||
}
|
||||
Expression::BinaryExpression(expr) => {
|
||||
self.binary_expression(expr)?;
|
||||
}
|
||||
_ => todo!("something is up with the arguments"),
|
||||
}
|
||||
|
||||
iter_index += 1;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// push the return address onto the stack. Current + to write + pushing the return address
|
||||
let return_addr = self.current_line + to_write.lines().count() + 2;
|
||||
self.write_output(format!("push {return_addr}"))?;
|
||||
self.output.write(to_write.as_bytes())?;
|
||||
self.current_line = return_addr;
|
||||
|
||||
fn declaration_expression(
|
||||
&mut self,
|
||||
ident: String,
|
||||
expression: Expression,
|
||||
) -> Result<(), CompileError> {
|
||||
let stack_index = self.stack_pointer;
|
||||
self.stack_pointer += 1;
|
||||
|
||||
match expression {
|
||||
Expression::Literal(Literal::Number(num)) => {
|
||||
self.write_output(format!("poke {stack_index} {num}\n"))?;
|
||||
}
|
||||
_ => {
|
||||
self.expression(expression)?;
|
||||
let register = self.register.len();
|
||||
self.write_output(format!("poke {stack_index} r{register}\n"))?;
|
||||
self.register.pop_back();
|
||||
}
|
||||
};
|
||||
self.variable_scope
|
||||
.last_mut()
|
||||
.ok_or(CompileError::ScopeError)?
|
||||
.insert(ident, stack_index);
|
||||
self.write_output(format!("j {function_line}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn block_expression(&mut self, expression: BlockExpression) -> Result<(), CompileError> {
|
||||
fn function_expression(&mut self, expression: FunctionExpression) -> Result<(), CompileError> {
|
||||
let func_name = expression.name;
|
||||
|
||||
self.variable_scope.push(HashMap::new());
|
||||
|
||||
self.function_locations.insert(func_name, self.current_line);
|
||||
|
||||
for arg in expression.arguments {
|
||||
self.push_stack(&arg)?;
|
||||
}
|
||||
|
||||
for expr in expression.body.0 {
|
||||
self.expression(expr)?;
|
||||
}
|
||||
|
||||
let scope = self.variable_scope.pop().ok_or(CompileError::ScopeError)?;
|
||||
|
||||
self.write_output(format!("sub sp sp {0}", scope.len()))?;
|
||||
self.write_output("pop ra")?;
|
||||
self.write_output("j ra")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn block_expression(&mut self, mut expression: BlockExpression) -> Result<(), CompileError> {
|
||||
self.variable_scope.push(HashMap::new());
|
||||
|
||||
// hoist functions to the top of the block
|
||||
expression.0.sort_by(|a, b| {
|
||||
if matches!(a, Expression::FunctionExpression(_))
|
||||
&& matches!(b, Expression::FunctionExpression(_))
|
||||
{
|
||||
Ordering::Equal
|
||||
} else if matches!(a, Expression::FunctionExpression(_)) {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
});
|
||||
|
||||
for expr in expression.0 {
|
||||
// if we haven't declared main yet and we have already declared all the function expressions, declare main
|
||||
if !self.declared_main && !matches!(expr, Expression::FunctionExpression(_)) {
|
||||
self.write_output("main:")?;
|
||||
self.declared_main = true;
|
||||
}
|
||||
self.expression(expr)?;
|
||||
}
|
||||
|
||||
@@ -168,70 +232,4 @@ impl<'a> Compiler<'a> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn binary_expression(&mut self, expression: BinaryExpression) -> Result<(), CompileError> {
|
||||
fn handle_expression<'a>(
|
||||
compiler: &'a mut Compiler,
|
||||
left: Expression,
|
||||
right: Expression,
|
||||
operator: &'static str,
|
||||
register_number: u8,
|
||||
) -> Result<(), CompileError> {
|
||||
let value_left = match left {
|
||||
Expression::Literal(Literal::Number(num)) => {
|
||||
format!("{num}")
|
||||
}
|
||||
Expression::Variable(name) => {
|
||||
let stack_index = variable_index!(compiler, name);
|
||||
compiler.write_output(format!(
|
||||
"get r{0} d0 {stack_index}\n",
|
||||
register_number + 1
|
||||
))?;
|
||||
format!("r{0}", register_number + 1)
|
||||
}
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let value_right = match right {
|
||||
Expression::Literal(Literal::Number(num)) => {
|
||||
format!("{num}")
|
||||
}
|
||||
Expression::Variable(name) => {
|
||||
let stack_index = variable_index!(compiler, name);
|
||||
compiler.write_output(format!(
|
||||
"get r{0} d0 {stack_index}\n",
|
||||
register_number + 2
|
||||
))?;
|
||||
format!("r{0}", register_number + 2)
|
||||
}
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
compiler.write_output(format!(
|
||||
"{operator} r{register_number} {value_left} {value_right}\n"
|
||||
))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let result_register = self.register.len();
|
||||
|
||||
match expression {
|
||||
BinaryExpression::Add(left, right) => {
|
||||
handle_expression(self, *left, *right, "add", result_register as u8)?
|
||||
}
|
||||
BinaryExpression::Subtract(left, right) => {
|
||||
handle_expression(self, *left, *right, "sub", result_register as u8)?
|
||||
}
|
||||
BinaryExpression::Multiply(left, right) => {
|
||||
handle_expression(self, *left, *right, "mul", result_register as u8)?
|
||||
}
|
||||
BinaryExpression::Divide(left, right) => {
|
||||
handle_expression(self, *left, *right, "div", result_register as u8)?
|
||||
}
|
||||
_ => todo!("Exponents have a different instruction set. {{ exp r? a(r?|num) }}"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,6 @@ struct Args {
|
||||
/// What file should be compiled. If not set, input will be read from stdin.
|
||||
#[arg(short, long)]
|
||||
input_file: Option<String>,
|
||||
/// The stack size for the compiled program. Compilation will fail if the compiler detects that the program will exceed this stack size.
|
||||
#[arg(short, long, default_value_t = 512)]
|
||||
stack_size: usize,
|
||||
/// The output file for the compiled program. If not set, output will go to stdout.
|
||||
#[arg(short, long)]
|
||||
output_file: Option<String>,
|
||||
@@ -85,7 +82,7 @@ fn run_logic() -> Result<(), StationlangError> {
|
||||
None => BufWriter::new(boxed!(std::io::stdout())),
|
||||
};
|
||||
|
||||
let compiler = Compiler::new(parser, args.stack_size, &mut writer);
|
||||
let compiler = Compiler::new(parser, &mut writer);
|
||||
|
||||
compiler.compile()?;
|
||||
writer.flush()?;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
let i = 234 + 432;
|
||||
fn test(a, b, c) {
|
||||
|
||||
let y = i / 432.54;
|
||||
};
|
||||
|
||||
let z = i / y;
|
||||
fn test2(x, y, z) {
|
||||
test(1, 2, 3);
|
||||
};
|
||||
|
||||
test2(1, 2, 3);
|
||||
Reference in New Issue
Block a user