diff options
| author | Cody <cody@codyq.dev> | 2023-04-14 04:50:53 -0500 |
|---|---|---|
| committer | Cody <cody@codyq.dev> | 2023-04-14 04:50:53 -0500 |
| commit | 5dbfa844a37cf00125db891d095d94620ba24879 (patch) | |
| tree | 441c8408ae873e6926e8864d353e71fdad809b4e | |
| parent | 97b7cd10d2bec408cc237e13c61562c810d8fd29 (diff) | |
| download | sloth-5dbfa844a37cf00125db891d095d94620ba24879.tar.gz | |
awful
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | crates/sloth/Cargo.toml | 3 | ||||
| -rw-r--r-- | crates/sloth/src/compiler/mod.rs | 212 | ||||
| -rw-r--r-- | crates/sloth/src/main.rs | 77 | ||||
| -rw-r--r-- | crates/sloth/src/parser/ast.rs | 14 | ||||
| -rw-r--r-- | crates/sloth/src/parser/expr.rs | 8 | ||||
| -rw-r--r-- | crates/sloth/src/parser/stmt.rs | 23 | ||||
| -rw-r--r-- | crates/sloth_bytecode/src/lib.rs | 1 | ||||
| -rw-r--r-- | crates/sloth_vm/src/lib.rs | 5 | ||||
| -rw-r--r-- | crates/sloth_vm/src/value.rs | 10 | ||||
| -rw-r--r-- | crates/sloth_vm/src/vm.rs | 17 |
11 files changed, 317 insertions, 55 deletions
@@ -111,6 +111,8 @@ name = "sloth" version = "0.1.0" dependencies = [ "itertools", + "sloth_bytecode", + "sloth_vm", "thiserror", ] diff --git a/crates/sloth/Cargo.toml b/crates/sloth/Cargo.toml index 2ee6919..9ab22e1 100644 --- a/crates/sloth/Cargo.toml +++ b/crates/sloth/Cargo.toml @@ -6,5 +6,8 @@ version.workspace = true edition.workspace = true [dependencies] +sloth_vm = { path = "../sloth_vm" } +sloth_bytecode = { path = "../sloth_bytecode" } + itertools = "0.10.5" thiserror = "1.0.40" diff --git a/crates/sloth/src/compiler/mod.rs b/crates/sloth/src/compiler/mod.rs new file mode 100644 index 0000000..bd05d1d --- /dev/null +++ b/crates/sloth/src/compiler/mod.rs @@ -0,0 +1,212 @@ +#![allow(unused)] + +use std::collections::HashMap; + +use sloth_bytecode::Opcode; +use sloth_vm::value::{Function, Object, ObjectType, Primitive}; +use sloth_vm::{Chunk, ObjectMap}; + +use crate::parser::ast::{BinaryOp, Expr, Literal, Stmt, UnaryOp}; + +#[derive(Default, Clone)] +pub struct CompilerScope { + objects: HashMap<String, u32>, +} + +#[derive(Eq, PartialEq)] +pub enum CompilerType { + Program, + Function, +} + +pub struct Compiler<'a> { + variables: HashMap<String, u32>, + + objects: &'a mut ObjectMap, + chunk: Chunk, +} + +impl<'a> Compiler<'a> { + pub fn compile( + objects: &'a mut ObjectMap, + variables: HashMap<String, u32>, + stmts: Vec<Stmt>, + ) -> Chunk { + let mut me = Self { + variables, + objects, + chunk: Chunk::default(), + }; + + for stmt in &stmts { + me.parse_statement(stmt); + } + + me.chunk + } + + fn push_constant(&mut self, value: Primitive) -> u16 { + let next = self.chunk.constants.len(); + self.chunk.constants.push(value); + next as u16 + } + + #[inline(always)] + fn write_opcode(&mut self, opcode: Opcode) { + self.write_u8(opcode as u8); + } + + #[inline(always)] + fn write_u8(&mut self, value: u8) { + self.chunk.code.push(value); + } + + #[inline(always)] + fn write_u16(&mut self, value: u16) { + let bytes = ((value >> 8) as u8, value as u8); + + self.write_u8(bytes.0); + self.write_u8(bytes.1); + } +} + +impl<'a> Compiler<'a> { + fn parse_statement(&mut self, stmt: &Stmt) { + match stmt { + Stmt::ExprStmt(expr) => { + self.parse_expression(expr); + } + Stmt::DefineFunction { + ident, + args, + body, + return_type, + } => { + if args.len() > 255 { + panic!("Function can not have more than 255 arguments"); + } + + let internal = + Compiler::compile(self.objects, self.variables.clone(), body.clone()); + let function = Function { + name: Some(ident.to_owned()), + chunk: internal, + arity: args.len() as u8, + returns_value: return_type.is_some(), + }; + + // Allocate the object as a function + let object = Object::new(ObjectType::Function(function)); + let ptr = self.objects.allocate(object); + + // Store it in variables + // Whenever there is a need to reference it just make a new constant lol + self.variables.insert(ident.to_owned(), ptr as u32); + } + Stmt::DefineVariable { name, value, typ } => { + // Take the expression and put whatever it evaluates to onto the stack + self.parse_expression(value); + // Store that as a variable + } + Stmt::DefineValue { name, value, typ } => todo!(), + Stmt::AssignVariable { name, value } => { + self.parse_expression(value); + + // + } + Stmt::If { + expr, + body, + else_if, + els, + } => todo!(), + Stmt::For { name, iter, body } => todo!(), + Stmt::While { condition, body } => todo!(), + Stmt::Return { value } => { + self.parse_expression(value); + self.write_opcode(Opcode::Return); + } + } + } +} + +impl<'a> Compiler<'a> { + fn parse_expression(&mut self, expr: &Expr) { + match expr { + Expr::Grouping(e) => self.parse_expression(e), + Expr::BinaryOp { op, lhs, rhs } => { + self.parse_expression(lhs); + self.parse_expression(rhs); + + let opcode = match op { + BinaryOp::Add => Opcode::Add, + BinaryOp::Con => todo!(), + BinaryOp::Sub => Opcode::Sub, + BinaryOp::Mul => Opcode::Mul, + BinaryOp::Pow => todo!(), + BinaryOp::Div => Opcode::Div, + BinaryOp::Mod => todo!(), + BinaryOp::BWSftRight => todo!(), + BinaryOp::BWSftLeft => todo!(), + BinaryOp::BWAnd => todo!(), + BinaryOp::BWOr => todo!(), + BinaryOp::BWXor => todo!(), + BinaryOp::Lt => todo!(), + BinaryOp::Gt => todo!(), + BinaryOp::LtEq => todo!(), + BinaryOp::GtEq => todo!(), + BinaryOp::EqEq => Opcode::Eq, + BinaryOp::NotEq => Opcode::Ne, + BinaryOp::LogAnd => todo!(), + BinaryOp::LogOr => todo!(), + BinaryOp::Range => todo!(), + }; + + self.write_opcode(opcode); + } + Expr::UnaryOp { op, value } => { + self.parse_expression(value); + + let opcode = match op { + UnaryOp::Not => todo!(), + UnaryOp::Neg => todo!(), + UnaryOp::BWComp => todo!(), + }; + + self.write_opcode(opcode); + } + Expr::Call { ident, args } => { + for arg in args.iter().rev() { + self.parse_expression(arg); + } + + self.parse_expression(ident); + self.write_opcode(Opcode::Call); + } + Expr::Variable(var) => { + // TODO: THIS IS AWFUL AND I HATE IT + let a = self.variables.get(var).cloned().expect("Uh oh spaghettio"); + + let pos = self.chunk.constants.len(); + self.chunk.constants.push(Primitive::Object(a)); + self.write_opcode(Opcode::Constant); + self.write_u16(pos as u16); + } + Expr::Literal(lit) => { + let pos = self.chunk.constants.len(); + + match lit { + Literal::Integer(i) => self.chunk.constants.push(Primitive::Integer(*i)), + Literal::Float(f) => self.chunk.constants.push(Primitive::Float(*f)), + Literal::Bool(b) => self.chunk.constants.push(Primitive::Bool(*b)), + Literal::String(s) => todo!(), + _ => todo!(), + } + + self.write_opcode(Opcode::Constant); + self.write_u16(pos as u16); + } + Expr::Lambda => todo!(), + } + } +} diff --git a/crates/sloth/src/main.rs b/crates/sloth/src/main.rs index 0d33e91..8fad9a7 100644 --- a/crates/sloth/src/main.rs +++ b/crates/sloth/src/main.rs @@ -6,37 +6,72 @@ unused_lifetimes )] +pub mod compiler; pub mod lexer; pub mod parser; +use std::collections::HashMap; use std::{env, fs}; use itertools::Itertools; use lexer::Lexer; use parser::AstParser; +use sloth_vm::value::Function; +use sloth_vm::{ObjectMap, VM}; + +use crate::compiler::Compiler; fn main() { - let args = env::args().collect_vec(); - - if args.len() < 2 { - println!("Sloth programming language interpreter\n"); - println!("Usage: sloth <file>"); - return; - } - - let source_path = &args[1]; - let Ok(source) = fs::read_to_string(source_path) else { - println!("Error while reading '{source_path}'"); - return; - }; - - let tokens = Lexer::new(&source).collect_vec(); - // for t in &tokens{ - // println!("{:#?}", t); + // let args = env::args().collect_vec(); + // + // if args.len() < 2 { + // println!("Sloth programming language interpreter\n"); + // println!("Usage: sloth <file>"); + // return; // } - let mut parser = AstParser::new(tokens); - // println!("{:#?}", parser); - let parsed = &parser.parse(); + // + // let source_path = &args[1]; + // let Ok(source) = fs::read_to_string(source_path) else { + // println!("Error while reading '{source_path}'"); + // return; + // }; + let source = " 3 + 7 ;"; + let source = r#" + + fn hello() -> int { + return 3 + 7; + } + + hello(); + hello(); + hello(); + hello(); + hello(); + hello(); + + "#; + let source = r#" + + fn hello() -> int { + var x = 5; + x = 7; + return x; + } + + hello(); + + "#; + + let tokens = Lexer::new(source).collect_vec(); + let ast = AstParser::new(tokens).parse(); + let mut object_map = ObjectMap::default(); + let code = Compiler::compile(&mut object_map, HashMap::default(), ast.clone()); + + println!("{ast:?}\n\n"); + println!("{:?}\n\n", code.constants); + println!("{:?}\n\n", code.code); - println!("{:?}", parsed); + let mut vm = VM::new(object_map, Function::root(code)); + vm.run(); + println!("{:?}", vm.stack.peek()); } diff --git a/crates/sloth/src/parser/ast.rs b/crates/sloth/src/parser/ast.rs index 3c8cdeb..2da1512 100644 --- a/crates/sloth/src/parser/ast.rs +++ b/crates/sloth/src/parser/ast.rs @@ -1,4 +1,4 @@ -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum BinaryOp { Add, Con, @@ -24,14 +24,14 @@ pub enum BinaryOp { LogOr, Range, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum UnaryOp { Not, Neg, BWComp, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Literal { Integer(i128), Float(f64), @@ -41,7 +41,7 @@ pub enum Literal { Regex(String), List(Vec<Expr>), } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Expr { Grouping(Box<Expr>), BinaryOp { @@ -61,17 +61,17 @@ pub enum Expr { Literal(Literal), Lambda, // TODO: Lambda } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Clone, Debug)] pub struct FuncArgs { pub name: String, pub typ: Option<String>, } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Clone, Debug)] pub enum Stmt { ExprStmt(Expr), DefineFunction { ident: String, - args: Option<Vec<FuncArgs>>, + args: Vec<FuncArgs>, body: Vec<Stmt>, return_type: Option<String>, }, diff --git a/crates/sloth/src/parser/expr.rs b/crates/sloth/src/parser/expr.rs index ad35d20..62c7893 100644 --- a/crates/sloth/src/parser/expr.rs +++ b/crates/sloth/src/parser/expr.rs @@ -73,13 +73,7 @@ impl<'a> AstParser<'a> { TokenType::Character(literal) => Expr::Literal(Literal::Char(literal)), TokenType::String(literal) => Expr::Literal(Literal::String(literal)), TokenType::Regex(literal) => Expr::Literal(Literal::Regex(literal)), - TokenType::Identifier(ident) => { - if self.peek().tt.clone() != TokenType::OpeningParen { - Expr::Variable(ident) - } else { - Expr::Literal(Literal::String(ident)) - } - } + TokenType::Identifier(ident) => Expr::Variable(ident), TokenType::OpeningParen => { let expr = self.expression(); self.consume(TokenType::ClosingParen, "Must end expression with ')'"); diff --git a/crates/sloth/src/parser/stmt.rs b/crates/sloth/src/parser/stmt.rs index 2c48da8..c193f4f 100644 --- a/crates/sloth/src/parser/stmt.rs +++ b/crates/sloth/src/parser/stmt.rs @@ -1,7 +1,6 @@ use super::ast::{Expr, FuncArgs, Stmt}; use super::AstParser; use crate::lexer::TokenType; -use crate::parser::ast::Literal; impl<'a> AstParser<'a> { pub fn parse(&mut self) -> Vec<Stmt> { @@ -82,7 +81,7 @@ impl<'a> AstParser<'a> { self.consume(TokenType::SemiColon, "No semi colon for me i guess"); return Stmt::ExprStmt(Expr::Call { - ident: (Box::new(Expr::Literal(Literal::String(ident)))), + ident: Box::new(Expr::Variable(ident)), args: (arguments), }); } @@ -287,10 +286,11 @@ impl<'a> AstParser<'a> { while !self.eof() && self.peek().tt != TokenType::ClosingBrace { body.push(self.statement()); } + self.consume(TokenType::ClosingBrace, "Expected '}' after body"); Stmt::DefineFunction { ident: (ident), - args: (Some(args)), + args: (args), body: (body), return_type: (typ), } @@ -415,11 +415,11 @@ mod tests { condition: (Expr::Literal(Literal::Bool(true))), body: (vec![ Stmt::ExprStmt(Expr::Call { - ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), + ident: Box::new(Expr::Variable("print".to_string())), args: (vec![Expr::Literal(Literal::String("Hello World".to_string()))]), }), Stmt::ExprStmt(Expr::Call { - ident: (Box::new(Expr::Literal(Literal::String("println".to_string())))), + ident: Box::new(Expr::Variable("println".to_string())), args: (vec![Expr::BinaryOp { op: (BinaryOp::Add), lhs: (Box::new(Expr::Literal(Literal::Integer(5)))), @@ -466,7 +466,7 @@ mod tests { rhs: (Box::new(Expr::Literal(Literal::Integer(10)))), }), body: (vec![Stmt::ExprStmt(Expr::Call { - ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), + ident: (Box::new(Expr::Variable("print".to_string()))), args: (vec![Expr::Variable("a".to_string())]), })]), else_if: (Vec::new()), @@ -483,7 +483,7 @@ mod tests { rhs: (Box::new(Expr::Literal(Literal::Integer(10)))), }), body: (vec![Stmt::ExprStmt(Expr::Call { - ident: (Box::new(Expr::Literal(Literal::String("println".to_string())))), + ident: (Box::new(Expr::Variable("println".to_string()))), args: (vec![Expr::Literal(Literal::Integer(10))]), })]), else_if: (Vec::new()), @@ -501,9 +501,12 @@ mod tests { }), body: (vec![ Stmt::ExprStmt(Expr::Call { - ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), + ident: (Box::new(Expr::Variable("print".to_string()))), + // ident: (Box::new(Expr::Literal(Literal::String("print".to_string())))), args: (vec![Expr::Call { - ident: Box::new(Expr::Literal(Literal::String("toString".to_string()))), + ident: (Box::new(Expr::Variable("toString".to_string()))), + // ident: Box::new(Expr::Literal(Literal::String("toString". + // to_string()))), args: vec![Expr::Literal(Literal::Integer(10))], }]), }), @@ -610,7 +613,7 @@ mod tests { Expr::Literal(Literal::Integer(3)), ]))), body: (vec![Stmt::ExprStmt(Expr::Call { - ident: Box::new(Expr::Literal(Literal::String("print".to_string()))), + ident: Box::new(Expr::Variable("print".to_string())), args: (vec![Expr::BinaryOp { op: (BinaryOp::Mul), lhs: (Box::new(Expr::Variable("j".to_string()))), diff --git a/crates/sloth_bytecode/src/lib.rs b/crates/sloth_bytecode/src/lib.rs index 2319330..c8152f6 100644 --- a/crates/sloth_bytecode/src/lib.rs +++ b/crates/sloth_bytecode/src/lib.rs @@ -51,6 +51,7 @@ opcodes! { 0x13 SetGlobal "Set a global value", 0x14 GetLocal "Get a local value", 0x15 SetLocal "Set a local value", + 0x16 Box "Box a value on the stack", 0x20 Add "Add the last 2 values on the stack", 0x21 Sub "Subtract the last 2 values on the stack", diff --git a/crates/sloth_vm/src/lib.rs b/crates/sloth_vm/src/lib.rs index 295827d..9cf552b 100644 --- a/crates/sloth_vm/src/lib.rs +++ b/crates/sloth_vm/src/lib.rs @@ -19,9 +19,10 @@ use value::{Object, ObjectType}; use crate::value::Primitive; pub use crate::vm::VM; +#[derive(Default)] pub struct Chunk { - constants: Vec<Primitive>, - code: Vec<u8>, + pub constants: Vec<Primitive>, + pub code: Vec<u8>, } const STACK_SIZE: usize = 1024; diff --git a/crates/sloth_vm/src/value.rs b/crates/sloth_vm/src/value.rs index f149c0e..4450b5a 100644 --- a/crates/sloth_vm/src/value.rs +++ b/crates/sloth_vm/src/value.rs @@ -35,14 +35,14 @@ pub enum ObjectType { } pub struct Function { - pub(crate) name: Option<String>, - pub(crate) chunk: Chunk, - pub(crate) arity: u8, - pub(crate) returns_value: bool, + pub name: Option<String>, + pub chunk: Chunk, + pub arity: u8, + pub returns_value: bool, } impl Function { - pub(crate) fn root(chunk: Chunk) -> Self { + pub fn root(chunk: Chunk) -> Self { Self { name: None, chunk, diff --git a/crates/sloth_vm/src/vm.rs b/crates/sloth_vm/src/vm.rs index 2d58c3d..3600719 100644 --- a/crates/sloth_vm/src/vm.rs +++ b/crates/sloth_vm/src/vm.rs @@ -3,7 +3,7 @@ use std::mem::MaybeUninit; use sloth_bytecode::Opcode; use crate::value::{Function, Object, ObjectType, Primitive}; -use crate::{native, ObjectMap, Stack}; +use crate::{native, vm, ObjectMap, Stack}; #[derive(Clone, Copy)] pub struct CallFrame { @@ -62,10 +62,11 @@ impl CallStack { } } +// TODO: Fix visibility pub struct VM { - stack: Stack, + pub stack: Stack, call_stack: CallStack, - objects: ObjectMap, + pub objects: ObjectMap, } impl Default for VM { @@ -128,6 +129,16 @@ impl VM { self.stack[self.call_stack.peek().stack_offset + idx] = value; } + Opcode::Box => { + // FIXME: TODO: MEGA CURSED + let pos = self.read_u16() as usize; + let value = self.stack.pop(); + + let object = vm::Object::new(ObjectType::Box(value)); + + self.objects.heap[pos] = object; + self.stack.push(Object(pos as u32)); + } Opcode::Add => { let value = match self.stack.pop2() { |
