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