diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs new file mode 100644 index 0000000..aaa22bc --- /dev/null +++ b/src/compiler/mod.rs @@ -0,0 +1,107 @@ +use thiserror::Error; + +use crate::parser::tree_node::*; +use crate::parser::Parser as ASTParser; +use std::collections::HashMap; + +/// Represents the return keyword. Used as a variable name for the register. +const RETURN: &'static str = "ret"; + +#[derive(Error, Debug)] +pub enum CompileError { + #[error(transparent)] + ParseError(#[from] crate::parser::ParseError), + #[error("A fatal error has occurred with the compiler. Scope could not be found.")] + ScopeError, +} + +pub struct Compiler { + parser: ASTParser, + /// Max stack size for the program is by default 512. + variable_scope: Vec>, + function_scope: Vec>, + output: String, + 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, + max_stack_size: usize, + current_line: usize, +} + +impl Compiler { + pub fn new(parser: ASTParser, max_stack_size: usize) -> Self { + Self { + parser, + variable_scope: Vec::new(), + function_scope: Vec::new(), + output: String::new(), + stack_pointer: 0, + register: HashMap::new(), + max_stack_size, + current_line: 0, + } + } + + pub fn compile(mut self) -> Result { + let ast = self.parser.parse_all()?; + + let Some(ast) = ast else { + return Ok(String::new()); + }; + + self.expression(ast)?; + + Ok(self.output) + } + + fn expression(&mut self, expression: Expression) -> Result<(), CompileError> { + match expression { + Expression::BlockExpression(block) => self.block_expression(block)?, + Expression::DeclarationExpression(name, expr) => { + self.declaration_expression(name, expr)? + } + Expression::ReturnExpression(expr) => self.return_expression(*expr)?, + _ => todo!(), + }; + + todo!() + } + + 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.stack_pointer + .checked_sub(1) + .ok_or(CompileError::ScopeError)?; + self.current_line += 1; + Ok(()) + } + + fn declaration_expression( + &mut self, + name: String, + expression: Box, + ) -> Result<(), CompileError> { + self.expression(*expression)?; + self.variable_scope + .last_mut() + .ok_or(CompileError::ScopeError)? + .insert(name, self.stack_pointer); + self.stack_pointer += 1; + Ok(()) + } + + fn block_expression(&mut self, expression: BlockExpression) -> Result<(), CompileError> { + // Push a new scope. This will be read from all the child expressions + self.variable_scope.push(HashMap::new()); + + for expr in expression.0 { + self.expression(expr)?; + } + + // Remove the scope we just added, all variables are now out of scope + self.variable_scope.pop(); + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 8a3fc93..983c183 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![feature(error_generic_member_access)] +mod compiler; mod parser; mod tokenizer; @@ -15,6 +16,8 @@ enum StationlangError { #[error(transparent)] ParserError(#[from] parser::ParseError), #[error(transparent)] + CompileError(#[from] compiler::CompileError), + #[error(transparent)] IoError(#[from] std::io::Error), } @@ -54,11 +57,10 @@ fn run_logic() -> Result<(), StationlangError> { let mut parser = ASTParser::new(tokenizer); - let ast = parser.parse_all()?; + let compiler = compiler::Compiler::new(parser, args.stack_size); - if let Some(ast) = ast { - println!("{}", ast); - } + let output = compiler.compile()?; + println!("{}", output); Ok(()) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 95ea527..f945670 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,4 +1,5 @@ -mod tree_node; +pub mod tree_node; +pub mod sys_call; use crate::tokenizer::{ token::{Keyword, Symbol, Token, TokenType}, diff --git a/src/parser/sys_call.rs b/src/parser/sys_call.rs new file mode 100644 index 0000000..6990a15 --- /dev/null +++ b/src/parser/sys_call.rs @@ -0,0 +1,125 @@ +use super::{Literal, LiteralOrVariable}; + +#[derive(Debug, PartialEq, Eq)] +pub enum Math { + /// Gets the absolute value of a number. + /// ## In Game + /// `abs r? a(r?|num)` + Abs(LiteralOrVariable), + /// Rounds a number up to the nearest whole number. + /// ## In Game + /// `ceil r? a(r?|num)` + Ceil(LiteralOrVariable), + /// Rounds a number down to the nearest whole number. + /// ## In Game + /// `floor r? a(r?|num)` + Floor(LiteralOrVariable), + /// Computes the natural logarithm of a number. + /// ## In Game + /// `log r? a(r?|num)` + Log(LiteralOrVariable), + /// Computes the maximum of two numbers. + /// ## In Game + /// `max r? a(r?|num) b(r?|num)` + Max(LiteralOrVariable, LiteralOrVariable), + /// Computes the minimum of two numbers. + /// ## 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), + + /// Returns the tangent of the specified angle in radians. + /// ## In Game + /// `tan r? a(r?|num)` + Tan(LiteralOrVariable), +} + +#[derive(Debug, PartialEq, Eq)] +pub enum System { + /// Pauses execution for exactly 1 tick and then resumes. + /// ## In Game + /// yield + Yield, + /// Represents a function that can be called to sleep for a certain amount of time. + /// ## In Game + /// `sleep a(r?|num)` + Sleep(LiteralOrVariable), + /// Represents a function that can be called to load a variable from a device into a register. + /// ## In Game + /// `l r? d? logicType` + LoadVar(Literal, Literal), + /// Gets the in-game hash for a specific prefab name. + /// ## In Game + /// `HASH("prefabName")` + Hash(LiteralOrVariable), + /// Represents a function which will clear the stack for a provided device. + /// ## In Game + /// `clr d?` + StackClear(Literal), + /// Represents a function which reads a value from a device at a specific address and stores it in a register. + /// ## In Game + /// `get r? d? address(r?|num)` + Get(Literal, LiteralOrVariable), + /// Represents a function which loads a device variable into a register. + /// ## In Game + /// `l r? d? var` + /// ## Examples + /// `l r0 d0 Setting` + /// `l r1 d5 Pressure` + Load(String, LiteralOrVariable), + /// Represents a function which stores a setting into a specific device. + /// ## In Game + /// `s d? logicType r?` + /// ## Example + /// `s d0 Setting r0` + Store(String, LiteralOrVariable, LiteralOrVariable), +} + +#[derive(Debug, PartialEq, Eq)] +/// This represents built in functions that cannot be overwritten, but can be invoked by the user as functions. +pub enum SysCall { + System(System), + /// Represents any mathmatical function that can be called. + Math(Math), +} diff --git a/src/parser/tree_node.rs b/src/parser/tree_node.rs index a2c44bb..3296803 100644 --- a/src/parser/tree_node.rs +++ b/src/parser/tree_node.rs @@ -138,6 +138,13 @@ impl std::fmt::Display for InvocationExpression { } } +#[derive(Debug, PartialEq, Eq)] +pub enum LiteralOrVariable { + Literal(Literal), + Variable(String), +} + + #[derive(Debug, PartialEq, Eq)] pub enum Expression { AssignmentExpression(AssignmentExpression),