diff --git a/src/bytecode.rs b/src/bytecode.rs new file mode 100644 index 0000000..58ba890 --- /dev/null +++ b/src/bytecode.rs @@ -0,0 +1,191 @@ +use std::collections::HashMap; + +use crate::ast::{Ast, Expr, Stmt, BinOpType}; + +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +pub enum OP { + Push, + Pop, + Load, + Store, + + Add, + Subtract, + Multiply, + Divide, + Modulo, + + Eq, + Neq, + + Gt, + Lt, + + Jump, + + JumpTrue, + + JumpFalse, + + Value(u32), + + Print, +} + +#[derive(Debug, Default)] +pub struct Compiler { + ops: Vec, + global_vars: HashMap, +} + +impl Compiler { + pub fn new() -> Self { + Compiler::default() + } + + pub fn compile(&mut self, ast: &Ast) { + for stmt in &ast.prog { + match stmt { + Stmt::Expr(expr) => { + self.compile_expr(expr); + self.ops.push(OP::Pop); + } + Stmt::Let(name, rhs) => { + let id = self.global_vars.len() as u64; + self.global_vars.insert(name.clone(), id); + + self.compile_expr(rhs); + self.gen_store(id); + } + Stmt::While(cond, body) => { + let idx_start = self.ops.len(); + self.compile_expr(cond); + + self.ops.push(OP::JumpFalse); + let idx_jmp = self.ops.len(); + self.gen_i64(0); + + self.compile(body); + + self.ops.push(OP::Jump); + self.gen_i64(idx_start as i64); + + self.overwrite_i64(idx_jmp, self.ops.len() as i64); + } + Stmt::For(_, _, _, _) => todo!(), + Stmt::If(cond, if_block, else_block) => { + self.compile_expr(cond); + + self.ops.push(OP::JumpFalse); + let idx_if = self.ops.len(); + self.gen_i64(0); + + self.compile(if_block); + + self.ops.push(OP::Jump); + let idx_else = self.ops.len(); + self.gen_i64(0); + + self.overwrite_i64(idx_if, self.ops.len() as i64); + + self.compile(else_block); + + self.overwrite_i64(idx_else, self.ops.len() as i64); + + }, + Stmt::DbgPrint(_) => todo!(), + Stmt::Print(expr) => { + self.compile_expr(expr); + self.ops.push(OP::Print); + } + } + } + } + + pub fn into_ops(self) -> Vec { + self.ops + } + + pub fn compile_expr(&mut self, expr: &Expr) { + match expr { + Expr::I64(val) => { + self.ops.push(OP::Push); + self.gen_i64(*val) + } + Expr::Ident(name) => { + match self.global_vars.get(name).copied() { + Some(addr) => self.gen_load(addr), + None => panic!("Variable '{}' used before declaration", name), + } + }, + Expr::BinOp(bo, lhs, rhs) => self.compile_binop(bo, lhs, rhs), + Expr::UnOp(_, _) => todo!(), + Expr::Str(_) => todo!(), + } + } + + fn compile_binop(&mut self, bo: &BinOpType, lhs: &Expr, rhs: &Expr) { + + if matches!(bo, BinOpType::Assign) { + self.compile_expr(rhs); + + if let Expr::Ident(name) = lhs { + let addr = *self.global_vars.get(name).expect("Trying to assign var before decl"); + self.gen_store(addr); + } else { + panic!("Trying to assign value to rvalue"); + } + return; + } + + self.compile_expr(lhs); + self.compile_expr(rhs); + + match bo { + BinOpType::Add => self.ops.push(OP::Add), + BinOpType::Sub => self.ops.push(OP::Subtract), + BinOpType::Mul => self.ops.push(OP::Multiply), + BinOpType::Div => self.ops.push(OP::Divide), + BinOpType::Mod => self.ops.push(OP::Modulo), + BinOpType::BOr => todo!(), + BinOpType::BAnd => todo!(), + BinOpType::BXor => todo!(), + BinOpType::Shl => todo!(), + BinOpType::Shr => todo!(), + BinOpType::Equ => self.ops.push(OP::Eq), + BinOpType::Neq => self.ops.push(OP::Neq), + BinOpType::Gt => self.ops.push(OP::Gt), + BinOpType::Ge => todo!(), + BinOpType::Lt => self.ops.push(OP::Lt), + BinOpType::Le => todo!(), + BinOpType::Assign => todo!(), + } + } + + fn gen_i64(&mut self, val: i64) { + self.ops.push(OP::Value((val & u32::MAX as i64) as u32)); + self.ops.push(OP::Value((val >> 32) as u32)); + } + + fn overwrite_i64(&mut self, idx: usize, val: i64) { + self.ops[idx] = OP::Value((val & u32::MAX as i64) as u32); + self.ops[idx+1] = OP::Value((val >> 32) as u32); + } + + fn gen_load(&mut self, addr: u64) { + self.ops.push(OP::Load); + self.gen_i64(addr as i64) + } + + fn gen_store(&mut self, addr: u64) { + self.ops.push(OP::Store); + self.gen_i64(addr as i64) + } +} + +pub fn compile(ast: &Ast) -> Vec { + let mut compiler = Compiler::new(); + compiler.compile(ast); + compiler.into_ops() +} diff --git a/src/lib.rs b/src/lib.rs index 1921d05..5de2a02 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,3 +3,5 @@ pub mod parser; pub mod interpreter; pub mod token; pub mod ast; +pub mod bytecode; +pub mod vm; diff --git a/src/main.rs b/src/main.rs index 8cc4edb..97707a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use std::{env::args, io::Write}; -use nek_lang::interpreter::Interpreter; +use nek_lang::{interpreter::Interpreter, lexer::lex, parser::parse, bytecode::compile, vm::Vm}; #[derive(Debug, Default)] struct CliConfig { @@ -27,7 +27,18 @@ fn main() { if let Some(file) = &cfg.file { let code = std::fs::read_to_string(file).expect(&format!("File not found: '{}'", file)); - interpreter.run_text(&code, cfg.print_tokens, cfg.print_ast); + let tokens = lex(&code); + let ast = parse(tokens); + + let prog = compile(&ast); + + // println!("{:?}", prog); + + let mut vm = Vm::new(prog); + + vm.run(); + + // interpreter.run_text(&code, cfg.print_tokens, cfg.print_ast); } if cfg.interactive || cfg.file.is_none() { diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..6d5be5b --- /dev/null +++ b/src/vm.rs @@ -0,0 +1,153 @@ +use crate::{bytecode::OP, interpreter::Value}; + +#[derive(Debug, Default)] +pub struct Vm { + prog: Vec, + ip: usize, + stack: Vec, + + /// This isn't actually a heap. It's actually still more of a f*cked up stack + heap: Vec, +} + +impl Vm { + pub fn new(prog: Vec) -> Self { + Self { + prog, + ..Default::default() + } + } + + pub fn run(&mut self) { + while let Some(op) = self.prog.get(self.ip).copied() { + self.ip += 1; + + match op { + OP::Push => { + let val = self.read_i64(); + self.stack.push(Value::I64(val)); + } + OP::Pop => { + self.stack.pop(); + } + OP::Load => { + let addr = self.read_i64() as usize; + + if let Some(val) = self.heap.get(addr) { + self.stack.push(val.clone()); + } else { + panic!("Trying to load from uninitialized heap"); + } + } + OP::Store => { + let val = self + .stack + .pop() + .expect("Trying to pop value from stack for storing"); + let addr = self.read_i64() as usize; + if self.heap.len() == addr { + self.heap.push(val); + } else { + self.heap[addr] = val; + } + } + OP::Print => { + let val = self + .stack + .pop() + .expect("Trying to pop value from stack for printing"); + print!("{}", val); + } + + OP::Add => { + let vals = self.pop2_i64(); + self.stack.push(Value::I64(vals.0 + vals.1)) + } + OP::Subtract => { + let vals = self.pop2_i64(); + self.stack.push(Value::I64(vals.0 - vals.1)) + } + OP::Multiply => { + let vals = self.pop2_i64(); + self.stack.push(Value::I64(vals.0 * vals.1)) + } + OP::Divide => { + let vals = self.pop2_i64(); + self.stack.push(Value::I64(vals.0 / vals.1)) + } + OP::Modulo => { + let vals = self.pop2_i64(); + self.stack.push(Value::I64(vals.0 % vals.1)) + } + OP::Eq => { + let vals = self.pop2_i64(); + self.stack + .push(Value::I64(if vals.0 == vals.1 { 1 } else { 0 })) + } + OP::Neq => { + let vals = self.pop2_i64(); + self.stack + .push(Value::I64(if vals.0 != vals.1 { 1 } else { 0 })) + } + OP::Gt => { + let vals = self.pop2_i64(); + self.stack + .push(Value::I64(if vals.0 > vals.1 { 1 } else { 0 })) + } + OP::Lt => { + let vals = self.pop2_i64(); + self.stack + .push(Value::I64(if vals.0 < vals.1 { 1 } else { 0 })) + } + OP::Value(_) => { + panic!("This is not an instruction, but data. This should never be evaluated") + } + OP::Jump => { + self.ip = self.read_i64() as usize; + } + OP::JumpTrue => { + let jmp_target = self.read_i64() as usize; + if !matches!(self.stack.pop(), Some(Value::I64(0))) { + self.ip = jmp_target; + } + } + OP::JumpFalse => { + let jmp_target = self.read_i64() as usize; + if matches!(self.stack.pop(), Some(Value::I64(0))) { + self.ip = jmp_target; + } + } + } + } + } + + fn pop2_i64(&mut self) -> (i64, i64) { + let rhs = self.stack.pop(); + let lhs = self.stack.pop(); + match (lhs, rhs) { + (Some(Value::I64(lhs)), Some(Value::I64(rhs))) => (lhs, rhs), + _ => panic!("Invalid data for add"), + } + } + + fn read_i64(&mut self) -> i64 { + let mut val = if let Some(OP::Value(val)) = self.prog.get(self.ip).copied() { + val + } else { + panic!("Expected Value as next OP") + } as i64; + + self.ip += 1; + + val |= (if let Some(OP::Value(val)) = self.prog.get(self.ip).copied() { + val + } else { + panic!("Expected Value as next OP") + } as i64) + << 32; + + self.ip += 1; + + val + } +}