compiler wip
This commit is contained in:
@@ -3,6 +3,8 @@ use thiserror::Error;
|
||||
use crate::parser::tree_node::*;
|
||||
use crate::parser::Parser as ASTParser;
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufWriter;
|
||||
use std::io::Write;
|
||||
|
||||
/// Represents the return keyword. Used as a variable name for the register.
|
||||
const RETURN: &'static str = "ret";
|
||||
@@ -13,14 +15,15 @@ pub enum CompileError {
|
||||
ParseError(#[from] crate::parser::ParseError),
|
||||
#[error("A fatal error has occurred with the compiler. Scope could not be found.")]
|
||||
ScopeError,
|
||||
#[error(transparent)]
|
||||
WriteError(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
pub struct Compiler {
|
||||
pub struct Compiler<'a> {
|
||||
parser: ASTParser,
|
||||
/// Max stack size for the program is by default 512.
|
||||
variable_scope: Vec<HashMap<String, usize>>,
|
||||
function_scope: Vec<HashMap<String, usize>>,
|
||||
output: String,
|
||||
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: HashMap<String, u8>,
|
||||
@@ -28,13 +31,16 @@ pub struct Compiler {
|
||||
current_line: usize,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
pub fn new(parser: ASTParser, max_stack_size: usize) -> Self {
|
||||
impl<'a> Compiler<'a> {
|
||||
pub fn new(
|
||||
parser: ASTParser,
|
||||
max_stack_size: usize,
|
||||
writer: &'a mut BufWriter<Box<dyn Write>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
parser,
|
||||
variable_scope: Vec::new(),
|
||||
function_scope: Vec::new(),
|
||||
output: String::new(),
|
||||
output: writer,
|
||||
stack_pointer: 0,
|
||||
register: HashMap::new(),
|
||||
max_stack_size,
|
||||
@@ -42,16 +48,16 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile(mut self) -> Result<String, CompileError> {
|
||||
pub fn compile(mut self) -> Result<(), CompileError> {
|
||||
let ast = self.parser.parse_all()?;
|
||||
|
||||
let Some(ast) = ast else {
|
||||
return Ok(String::new());
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
self.expression(ast)?;
|
||||
|
||||
Ok(self.output)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expression(&mut self, expression: Expression) -> Result<(), CompileError> {
|
||||
@@ -61,16 +67,35 @@ impl Compiler {
|
||||
self.declaration_expression(name, expr)?
|
||||
}
|
||||
Expression::ReturnExpression(expr) => self.return_expression(*expr)?,
|
||||
_ => todo!(),
|
||||
Expression::FunctionExpression(func) => self.function_expression(func)?,
|
||||
_ => todo!("{:?}", expression),
|
||||
};
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn function_expression(&mut self, func: FunctionExpression) -> Result<(), CompileError> {
|
||||
for arg in func.arguments {
|
||||
self.variable_scope
|
||||
.last_mut()
|
||||
.ok_or(CompileError::ScopeError)?
|
||||
.insert(arg, self.stack_pointer);
|
||||
self.stack_pointer += 1;
|
||||
}
|
||||
|
||||
for expr in func.body.0 {
|
||||
self.expression(expr)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn return_expression(&mut self, expression: Expression) -> Result<(), CompileError> {
|
||||
// pop last var off the stack and push it into the first available register
|
||||
let register = self.register.len() as u8;
|
||||
self.output.push_str(&format!("pop r{}\n", register));
|
||||
self.output
|
||||
.write(&format!("pop r{}\n", register).as_bytes())?;
|
||||
|
||||
self.stack_pointer
|
||||
.checked_sub(1)
|
||||
.ok_or(CompileError::ScopeError)?;
|
||||
|
||||
27
src/main.rs
27
src/main.rs
@@ -5,10 +5,22 @@ mod parser;
|
||||
mod tokenizer;
|
||||
|
||||
use clap::Parser;
|
||||
use compiler::Compiler;
|
||||
use parser::Parser as ASTParser;
|
||||
use std::io::Read;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufWriter, Read, Write},
|
||||
};
|
||||
use tokenizer::{Tokenizer, TokenizerError};
|
||||
|
||||
#[macro_export]
|
||||
/// A macro to create a boxed value.
|
||||
macro_rules! boxed {
|
||||
($e:expr) => {
|
||||
Box::new($e)
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum StationlangError {
|
||||
#[error(transparent)]
|
||||
@@ -55,13 +67,18 @@ fn run_logic() -> Result<(), StationlangError> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut parser = ASTParser::new(tokenizer);
|
||||
let parser = ASTParser::new(tokenizer);
|
||||
|
||||
let compiler = compiler::Compiler::new(parser, args.stack_size);
|
||||
let mut writer: BufWriter<Box<dyn Write>> = match args.output_file {
|
||||
Some(output_file) => BufWriter::new(boxed!(File::create(output_file)?)),
|
||||
None => BufWriter::new(boxed!(std::io::stdout())),
|
||||
};
|
||||
|
||||
let output = compiler.compile()?;
|
||||
println!("{}", output);
|
||||
let compiler = Compiler::new(parser, args.stack_size, &mut writer);
|
||||
|
||||
compiler.compile()?;
|
||||
|
||||
writer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
pub mod tree_node;
|
||||
pub mod sys_call;
|
||||
pub mod tree_node;
|
||||
|
||||
use crate::tokenizer::{
|
||||
use crate::{
|
||||
boxed,
|
||||
tokenizer::{
|
||||
token::{Keyword, Symbol, Token, TokenType},
|
||||
Tokenizer, TokenizerBuffer, TokenizerError,
|
||||
},
|
||||
};
|
||||
use std::{
|
||||
backtrace::{self, Backtrace},
|
||||
@@ -289,7 +292,7 @@ impl Parser {
|
||||
|
||||
Ok(AssignmentExpression {
|
||||
identifier,
|
||||
expression: Box::new(expression),
|
||||
expression: boxed!(expression),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -355,8 +358,8 @@ impl Parser {
|
||||
expressions.insert(
|
||||
index,
|
||||
Expression::BinaryExpression(BinaryExpression::Exponent(
|
||||
Box::new(left),
|
||||
Box::new(right),
|
||||
boxed!(left),
|
||||
boxed!(right),
|
||||
)),
|
||||
);
|
||||
current_iteration += 1;
|
||||
@@ -378,15 +381,15 @@ impl Parser {
|
||||
Symbol::Asterisk => expressions.insert(
|
||||
index,
|
||||
Expression::BinaryExpression(BinaryExpression::Multiply(
|
||||
Box::new(left),
|
||||
Box::new(right),
|
||||
boxed!(left),
|
||||
boxed!(right),
|
||||
)),
|
||||
),
|
||||
Symbol::Slash => expressions.insert(
|
||||
index,
|
||||
Expression::BinaryExpression(BinaryExpression::Divide(
|
||||
Box::new(left),
|
||||
Box::new(right),
|
||||
boxed!(left),
|
||||
boxed!(right),
|
||||
)),
|
||||
),
|
||||
// safety: we have already checked for the operator
|
||||
@@ -411,15 +414,15 @@ impl Parser {
|
||||
Symbol::Plus => expressions.insert(
|
||||
index,
|
||||
Expression::BinaryExpression(BinaryExpression::Add(
|
||||
Box::new(left),
|
||||
Box::new(right),
|
||||
boxed!(left),
|
||||
boxed!(right),
|
||||
)),
|
||||
),
|
||||
Symbol::Minus => expressions.insert(
|
||||
index,
|
||||
Expression::BinaryExpression(BinaryExpression::Subtract(
|
||||
Box::new(left),
|
||||
Box::new(right),
|
||||
boxed!(left),
|
||||
boxed!(right),
|
||||
)),
|
||||
),
|
||||
// safety: we have already checked for the operator
|
||||
@@ -475,7 +478,7 @@ impl Parser {
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Box::new(expression))
|
||||
Ok(boxed!(expression))
|
||||
}
|
||||
|
||||
fn invocation(&mut self) -> Result<InvocationExpression, ParseError> {
|
||||
@@ -564,7 +567,7 @@ impl Parser {
|
||||
if token_matches!(current_token, TokenType::Keyword(Keyword::Return)) {
|
||||
self.assign_next()?;
|
||||
let expression = self.expression()?.ok_or(ParseError::UnexpectedEOF)?;
|
||||
let return_expr = Expression::ReturnExpression(Box::new(expression));
|
||||
let return_expr = Expression::ReturnExpression(boxed!(expression));
|
||||
expressions.push(return_expr);
|
||||
self.assign_next()?;
|
||||
}
|
||||
@@ -611,7 +614,7 @@ impl Parser {
|
||||
|
||||
Ok(Expression::DeclarationExpression(
|
||||
identifier,
|
||||
Box::new(assignment_expression),
|
||||
boxed!(assignment_expression),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,22 @@ use super::{Literal, LiteralOrVariable};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Math {
|
||||
/// Returns the angle in radians whose cosine is the specified number.
|
||||
/// ## In Game
|
||||
/// `acos r? a(r?|num)`
|
||||
Acos(LiteralOrVariable),
|
||||
/// Returns the angle in radians whose sine is the specified number.
|
||||
/// ## In Game
|
||||
/// `asin r? a(r?|num)`
|
||||
Asin(LiteralOrVariable),
|
||||
/// Returns the angle in radians whose tangent is the specified number.
|
||||
/// ## In Game
|
||||
/// `atan r? a(r?|num)`
|
||||
Atan(LiteralOrVariable),
|
||||
/// Returns the angle in radians whose tangent is the quotient of the specified numbers.
|
||||
/// ## In Game
|
||||
/// `atan2 r? a(r?|num) b(r?|num)`
|
||||
Atan2(LiteralOrVariable, LiteralOrVariable),
|
||||
/// Gets the absolute value of a number.
|
||||
/// ## In Game
|
||||
/// `abs r? a(r?|num)`
|
||||
@@ -10,6 +26,10 @@ pub enum Math {
|
||||
/// ## In Game
|
||||
/// `ceil r? a(r?|num)`
|
||||
Ceil(LiteralOrVariable),
|
||||
/// Returns the cosine of the specified angle in radians.
|
||||
/// ## In Game
|
||||
/// cos r? a(r?|num)
|
||||
Cos(LiteralOrVariable),
|
||||
/// Rounds a number down to the nearest whole number.
|
||||
/// ## In Game
|
||||
/// `floor r? a(r?|num)`
|
||||
@@ -26,53 +46,26 @@ pub enum Math {
|
||||
/// ## In Game
|
||||
/// `min r? a(r?|num) b(r?|num)`
|
||||
Min(LiteralOrVariable, LiteralOrVariable),
|
||||
/// Computes the square root of a number.
|
||||
/// ## In Game
|
||||
/// `sqrt r? a(r?|num)`
|
||||
Sqrt(LiteralOrVariable),
|
||||
/// Gets a random number between 0 and 1.
|
||||
/// ## In Game
|
||||
/// `rand r?`
|
||||
Rand,
|
||||
/// Truncates a number by removing the decimal portion.
|
||||
/// ## In Game
|
||||
/// `trunc r? a(r?|num)`
|
||||
Trunc(LiteralOrVariable),
|
||||
|
||||
/// Returns the angle in radians whose cosine is the specified number.
|
||||
/// ## In Game
|
||||
/// `acos r? a(r?|num)`
|
||||
Acos(LiteralOrVariable),
|
||||
|
||||
/// Returns the angle in radians whose sine is the specified number.
|
||||
/// ## In Game
|
||||
/// `asin r? a(r?|num)`
|
||||
Asin(LiteralOrVariable),
|
||||
|
||||
/// Returns the angle in radians whose tangent is the specified number.
|
||||
/// ## In Game
|
||||
/// `atan r? a(r?|num)`
|
||||
Atan(LiteralOrVariable),
|
||||
|
||||
/// Returns the angle in radians whose tangent is the quotient of the specified numbers.
|
||||
/// ## In Game
|
||||
/// `atan2 r? a(r?|num) b(r?|num)`
|
||||
Atan2(LiteralOrVariable, LiteralOrVariable),
|
||||
|
||||
/// Returns the cosine of the specified angle in radians.
|
||||
/// ## In Game
|
||||
/// cos r? a(r?|num)
|
||||
Cos(LiteralOrVariable),
|
||||
|
||||
/// Returns the sine of the specified angle in radians.
|
||||
/// ## In Game
|
||||
/// `sin r? a(r?|num)`
|
||||
Sin(LiteralOrVariable),
|
||||
|
||||
/// Computes the square root of a number.
|
||||
/// ## In Game
|
||||
/// `sqrt r? a(r?|num)`
|
||||
Sqrt(LiteralOrVariable),
|
||||
/// Returns the tangent of the specified angle in radians.
|
||||
/// ## In Game
|
||||
/// `tan r? a(r?|num)`
|
||||
Tan(LiteralOrVariable),
|
||||
/// Truncates a number by removing the decimal portion.
|
||||
/// ## In Game
|
||||
/// `trunc r? a(r?|num)`
|
||||
Trunc(LiteralOrVariable),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
||||
@@ -10,6 +10,8 @@ use std::{
|
||||
use thiserror::Error;
|
||||
use token::{Keyword, Number, Symbol, Token, TokenType};
|
||||
|
||||
use crate::boxed;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TokenizerError {
|
||||
#[error("IO Error: {0}")]
|
||||
@@ -39,7 +41,7 @@ pub(crate) struct Tokenizer {
|
||||
impl Tokenizer {
|
||||
pub fn from_path(input_file: impl Into<PathBuf>) -> Result<Self, TokenizerError> {
|
||||
let file = std::fs::File::open(input_file.into())?;
|
||||
let reader = BufReader::new(Box::new(file) as Box<dyn Tokenize>);
|
||||
let reader = BufReader::new(boxed!(file) as Box<dyn Tokenize>);
|
||||
|
||||
Ok(Self {
|
||||
reader,
|
||||
@@ -53,7 +55,7 @@ impl Tokenizer {
|
||||
|
||||
impl From<String> for Tokenizer {
|
||||
fn from(input: String) -> Self {
|
||||
let reader = BufReader::new(Box::new(Cursor::new(input)) as Box<dyn Tokenize>);
|
||||
let reader = BufReader::new(boxed!(Cursor::new(input)) as Box<dyn Tokenize>);
|
||||
|
||||
Self {
|
||||
reader,
|
||||
|
||||
Reference in New Issue
Block a user