diff --git a/runtime/src/bytecode.rs b/runtime/src/bytecode.rs index b47d0b0..2fe783b 100644 --- a/runtime/src/bytecode.rs +++ b/runtime/src/bytecode.rs @@ -4,8 +4,9 @@ use parser::position::Range; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Instruction { - Constant(usize), Pop, + Constant(usize), + Array(usize), } #[derive(Debug, PartialEq, Clone)] diff --git a/runtime/src/compiler.rs b/runtime/src/compiler.rs index e6709d6..4b32856 100644 --- a/runtime/src/compiler.rs +++ b/runtime/src/compiler.rs @@ -70,22 +70,18 @@ impl Compiler { match &node.value { ast::NodeValue::Identifier(_) => todo!(), ast::NodeValue::IntegerLiteral(int) => { - let const_idx = self.add_constant(Object::Integer(*int)); - self.emit(Instruction::Constant(const_idx), node.range); + self.compile_constant(Object::Integer(*int), node.range); } ast::NodeValue::FloatLiteral(flt) => { - let const_idx = self.add_constant(Object::Float(*flt)); - self.emit(Instruction::Constant(const_idx), node.range); + self.compile_constant(Object::Float(*flt), node.range); } ast::NodeValue::BoolLiteral(boolean) => { - let const_idx = self.add_constant(Object::Boolean(*boolean)); - self.emit(Instruction::Constant(const_idx), node.range); + self.compile_constant(Object::Boolean(*boolean), node.range); } ast::NodeValue::StringLiteral(string) => { - let const_idx = self.add_constant(Object::String(Rc::new(string.to_string()))); - self.emit(Instruction::Constant(const_idx), node.range); + self.compile_constant(Object::String(Rc::new(string.to_string())), node.range); } - ast::NodeValue::ArrayLiteral(_) => todo!(), + ast::NodeValue::ArrayLiteral(arr) => self.compile_array(arr, node.range), ast::NodeValue::HashLiteral(_) => todo!(), ast::NodeValue::PrefixOperator { .. } => todo!(), ast::NodeValue::InfixOperator { .. } => todo!(), @@ -102,4 +98,17 @@ impl Compiler { ast::NodeValue::Use(_) => todo!(), } } + + fn compile_constant(&mut self, constant: Object, range: Range) { + let const_idx = self.add_constant(constant); + self.emit(Instruction::Constant(const_idx), range); + } + + fn compile_array(&mut self, arr: &[ast::Node], range: Range) { + for node in arr { + self.compile_node(node); + } + + self.emit(Instruction::Array(arr.len()), range); + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2d46c99..ec20a80 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,3 +1,5 @@ +use parser::ast; + pub mod object; mod bytecode; @@ -7,10 +9,10 @@ mod vm; #[cfg(test)] mod test; -pub fn run(input: &str) { - // TODO: Haandle errors and add vm - let program = parser::parse(input).unwrap(); - +pub fn run(program: &ast::Program) { let compiler = compiler::Compiler::new(); - let _bytecode = compiler.compile(&program); + let bytecode = compiler.compile(program); + + let vm = vm::VirtualMachine::new(); + vm.run(&bytecode); } diff --git a/runtime/src/object.rs b/runtime/src/object.rs index 5a476b9..c7afcd9 100644 --- a/runtime/src/object.rs +++ b/runtime/src/object.rs @@ -2,9 +2,10 @@ use std::rc::Rc; #[derive(Debug, PartialEq, Clone)] pub enum Object { + Null, Integer(i64), Float(f64), Boolean(bool), String(Rc), - Null, + Array(Rc>), } diff --git a/runtime/src/test/compiler.rs b/runtime/src/test/compiler.rs index 454ff5f..35d7998 100644 --- a/runtime/src/test/compiler.rs +++ b/runtime/src/test/compiler.rs @@ -79,3 +79,119 @@ fn constants() { assert_eq!(bytecode, expected); } } + +#[test] +fn arrays() { + let tests = [ + ( + "[]", + Bytecode { + constants: vec![], + instructions: vec![Instruction::Array(0), Instruction::Pop], + ranges: vec![ + Range { + start: Position::new(0, 0), + end: Position::new(0, 2), + }; + 2 + ], + }, + ), + ( + "[1]", + Bytecode { + constants: vec![Object::Integer(1)], + instructions: vec![ + Instruction::Constant(0), + Instruction::Array(1), + Instruction::Pop, + ], + ranges: vec![ + Range { + start: Position::new(0, 1), + end: Position::new(0, 2), + }, + Range { + start: Position::new(0, 0), + end: Position::new(0, 3), + }, + Range { + start: Position::new(0, 0), + end: Position::new(0, 3), + }, + ], + }, + ), + ( + "[1, \"foo\"]", + Bytecode { + constants: vec![ + Object::Integer(1), + Object::String(Rc::new("foo".to_string())), + ], + instructions: vec![ + Instruction::Constant(0), + Instruction::Constant(1), + Instruction::Array(2), + Instruction::Pop, + ], + ranges: vec![ + Range { + start: Position::new(0, 1), + end: Position::new(0, 2), + }, + Range { + start: Position::new(0, 4), + end: Position::new(0, 9), + }, + Range { + start: Position::new(0, 0), + end: Position::new(0, 10), + }, + Range { + start: Position::new(0, 0), + end: Position::new(0, 10), + }, + ], + }, + ), + ]; + + for (input, expected) in tests { + let program = parse(input).unwrap(); + let compiler = Compiler::new(); + let bytecode = compiler.compile(&program); + assert_eq!(bytecode, expected); + } + + let tests = [( + "[1, [2, 3], 4]", + Bytecode { + constants: vec![ + Object::Integer(1), + Object::Integer(2), + Object::Integer(3), + Object::Integer(4), + ], + instructions: vec![ + Instruction::Constant(0), + Instruction::Constant(1), + Instruction::Constant(2), + Instruction::Array(2), + Instruction::Constant(3), + Instruction::Array(3), + Instruction::Pop, + ], + ranges: vec![], + }, + )]; + + for (input, expected) in tests { + let program = parse(input).unwrap(); + let compiler = Compiler::new(); + let mut bytecode = compiler.compile(&program); + bytecode.ranges = vec![]; + + assert_eq!(bytecode, expected); + } +} diff --git a/runtime/src/test/mod.rs b/runtime/src/test/mod.rs index 815219b..444abbc 100644 --- a/runtime/src/test/mod.rs +++ b/runtime/src/test/mod.rs @@ -1 +1,2 @@ mod compiler; +mod vm; diff --git a/runtime/src/test/vm.rs b/runtime/src/test/vm.rs new file mode 100644 index 0000000..d4d2f80 --- /dev/null +++ b/runtime/src/test/vm.rs @@ -0,0 +1,56 @@ +use std::rc::Rc; + +use crate::{compiler::Compiler, object::Object, vm::VirtualMachine}; + +fn run_test(input: &str, expected: Object) { + let program = parser::parse(input).unwrap(); + + let compiler = Compiler::new(); + let bytecode = compiler.compile(&program); + + let vm = VirtualMachine::new(); + let obj = vm.run(&bytecode); + + assert_eq!(obj, expected); +} + +#[test] +fn constants() { + let tests = [ + ("10", Object::Integer(10)), + ("6.9", Object::Float(6.9)), + ("\"foo\"", Object::String(Rc::new("foo".to_string()))), + ("true", Object::Boolean(true)), + ]; + + for (input, expected) in tests { + run_test(input, expected); + } +} + +#[test] +fn array() { + let tests = [ + ("[]", vec![]), + ("[1]", vec![Object::Integer(1)]), + ( + "[1, \"foo\"]", + vec![ + Object::Integer(1), + Object::String(Rc::new("foo".to_string())), + ], + ), + ( + "[1, [2, 3], 4]", + vec![ + Object::Integer(1), + Object::Array(Rc::new(vec![Object::Integer(2), Object::Integer(3)])), + Object::Integer(4), + ], + ), + ]; + + for (input, expected) in tests { + run_test(input, Object::Array(Rc::new(expected))); + } +} diff --git a/runtime/src/vm.rs b/runtime/src/vm.rs index a25fd79..afb6099 100644 --- a/runtime/src/vm.rs +++ b/runtime/src/vm.rs @@ -1,4 +1,9 @@ -use crate::{bytecode::Bytecode, object::Object}; +use std::rc::Rc; + +use crate::{ + bytecode::{Bytecode, Instruction}, + object::Object, +}; const STACK_SIZE: usize = 4096; @@ -28,20 +33,39 @@ impl VirtualMachine { } fn pop(&mut self) -> Object { + if self.sp == 0 { + panic!("No more elements"); + } + self.sp -= 1; self.stack[self.sp].clone() } - pub fn run(&mut self, bytecode: &Bytecode) { + /// Runs the program and returns the first element on the stack. + /// + /// This is primarily used to test if the vm works correctly. + /// The first element on the stack should be the last popped element + /// if the compiler and vm both work correctly. + pub fn run(mut self, bytecode: &Bytecode) -> Object { for inst in &bytecode.instructions { match inst { - crate::bytecode::Instruction::Constant(idx) => { - self.push(bytecode.constants[*idx].clone()) - } - crate::bytecode::Instruction::Pop => { + Instruction::Constant(idx) => self.push(bytecode.constants[*idx].clone()), + Instruction::Pop => { self.pop(); } + Instruction::Array(len) => self.execute_array(*len), } } + + self.stack[0].clone() + } + + fn execute_array(&mut self, len: usize) { + let start = self.sp - len; + + let arr = self.stack[start..self.sp].to_vec(); + self.sp -= len; + + self.push(Object::Array(Rc::new(arr))); } }