diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index aaa22bc..ddb50cc 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -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>, - function_scope: Vec>, - output: String, + output: &'a mut BufWriter>, 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, @@ -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>, + ) -> 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 { + 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)?; diff --git a/src/main.rs b/src/main.rs index 983c183..8a640d0 100644 --- a/src/main.rs +++ b/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> = 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(()) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f945670..10f5f23 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,9 +1,12 @@ -pub mod tree_node; pub mod sys_call; +pub mod tree_node; -use crate::tokenizer::{ - token::{Keyword, Symbol, Token, TokenType}, - Tokenizer, TokenizerBuffer, TokenizerError, +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 { @@ -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), )) } diff --git a/src/parser/sys_call.rs b/src/parser/sys_call.rs index 6990a15..596139a 100644 --- a/src/parser/sys_call.rs +++ b/src/parser/sys_call.rs @@ -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)] diff --git a/src/tokenizer/mod.rs b/src/tokenizer/mod.rs index 67b3a99..85ab118 100644 --- a/src/tokenizer/mod.rs +++ b/src/tokenizer/mod.rs @@ -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) -> Result { let file = std::fs::File::open(input_file.into())?; - let reader = BufReader::new(Box::new(file) as Box); + let reader = BufReader::new(boxed!(file) as Box); Ok(Self { reader, @@ -53,7 +55,7 @@ impl Tokenizer { impl From for Tokenizer { fn from(input: String) -> Self { - let reader = BufReader::new(Box::new(Cursor::new(input)) as Box); + let reader = BufReader::new(boxed!(Cursor::new(input)) as Box); Self { reader,