aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody <cody@codyq.dev>2023-04-14 04:50:53 -0500
committerCody <cody@codyq.dev>2023-04-14 04:50:53 -0500
commit5dbfa844a37cf00125db891d095d94620ba24879 (patch)
tree441c8408ae873e6926e8864d353e71fdad809b4e
parent97b7cd10d2bec408cc237e13c61562c810d8fd29 (diff)
downloadsloth-5dbfa844a37cf00125db891d095d94620ba24879.tar.gz
awful
-rw-r--r--Cargo.lock2
-rw-r--r--crates/sloth/Cargo.toml3
-rw-r--r--crates/sloth/src/compiler/mod.rs212
-rw-r--r--crates/sloth/src/main.rs77
-rw-r--r--crates/sloth/src/parser/ast.rs14
-rw-r--r--crates/sloth/src/parser/expr.rs8
-rw-r--r--crates/sloth/src/parser/stmt.rs23
-rw-r--r--crates/sloth_bytecode/src/lib.rs1
-rw-r--r--crates/sloth_vm/src/lib.rs5
-rw-r--r--crates/sloth_vm/src/value.rs10
-rw-r--r--crates/sloth_vm/src/vm.rs17
11 files changed, 317 insertions, 55 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b7ccbe0..23e77c7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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() {