diff options
| -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 | 79 | ||||
| -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 | 55 | ||||
| -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, 340 insertions, 66 deletions
@@ -112,6 +112,8 @@ version = "0.1.0" dependencies = [ "itertools", "libc", + "sloth_bytecode", + "sloth_vm", "thiserror", ] diff --git a/crates/sloth/Cargo.toml b/crates/sloth/Cargo.toml index 1b7a9fc..8f49802 100644 --- a/crates/sloth/Cargo.toml +++ b/crates/sloth/Cargo.toml @@ -6,6 +6,9 @@ version.workspace = true edition.workspace = true [dependencies] +sloth_vm = { path = "../sloth_vm" } +sloth_bytecode = { path = "../sloth_bytecode" } + itertools = "0.10.5" libc = "0.2.142" 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 8fab058..ef27570 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::{env, fs}; +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 12dad13..543ea3a 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, @@ -25,7 +25,7 @@ pub enum BinaryOp { Range, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum UnaryOp { Not, Neg, @@ -33,7 +33,7 @@ pub enum UnaryOp { BWComp, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Literal { Integer(i128), Float(f64), @@ -44,7 +44,7 @@ pub enum Literal { List(Vec<Expr>), } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum Expr { Grouping(Box<Expr>), BinaryOp { @@ -65,18 +65,18 @@ pub enum Expr { 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 8036552..9e81f7f 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 7c468ef..1a961b1 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), }); } @@ -213,8 +212,14 @@ impl<'a> AstParser<'a> { ); let mut body = Vec::new(); while !self.eof() && self.peek().tt != TokenType::ClosingBrace { + println!("{:?}", self.peek().tt); body.push(self.statement()); } + self.consume( + TokenType::ClosingBrace, + "Expected '}' after block on while loop", + ); + self.advance(); Stmt::While { condition, body } } @@ -227,17 +232,23 @@ impl<'a> AstParser<'a> { // if let Expr::Literal(_ident) = &expr { // let value = self.expression(); - // self.consume(TokenType::SemiColon, "Expected ';' at end of - // statement"); // return Stmt::DefineVariable { - // // name: (ident.clone()), - // // value: (value), - // // typ: (None), - // // }; + // self.consume( + // TokenType::SemiColon, + // "Expected ';' at end of + // statement", + // ); // return Stmt::DefineVariable { + // // name: (ident.clone()), + // // value: (value), + // // typ: (None), + // // }; // return Stmt::ExprStmt(expr); // } // } - self.consume(TokenType::SemiColon, "Expected ';' at end of statement"); + self.consume( + TokenType::SemiColon, + "Expected ';' at end of expr statement", + ); Stmt::ExprStmt(expr) } @@ -287,10 +298,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), } @@ -366,7 +378,7 @@ mod tests { let expected_ast = Stmt::DefineFunction { ident: ("test_c".to_string()), - args: Some(vec![ + args: (vec![ FuncArgs { name: ("a".to_string()), typ: None, @@ -415,11 +427,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 +478,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 +495,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 +513,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))], }]), }), @@ -559,10 +574,10 @@ mod tests { let expected_ast = Stmt::DefineFunction { ident: ("times_two".to_string()), - args: (Some(vec![FuncArgs { + args: (vec![FuncArgs { name: ("x".to_string()), typ: (Some("int".to_string())), - }])), + }]), body: (vec![ Stmt::DefineValue { name: "y".to_string(), @@ -610,7 +625,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() { |
