diff --git a/Cargo.lock b/Cargo.lock index f9cc9b6..2811c4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "monkey_lang" version = "0.1.0" +dependencies = [ + "lazy_static", +] diff --git a/Cargo.toml b/Cargo.toml index aad13c9..9c6b0b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lazy_static = "1.4" \ No newline at end of file diff --git a/src/compiler.rs b/src/compiler.rs index e69de29..6b46e6f 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -0,0 +1 @@ +pub mod compiler; \ No newline at end of file diff --git a/src/compiler/compiler.rs b/src/compiler/compiler.rs new file mode 100644 index 0000000..f791b9b --- /dev/null +++ b/src/compiler/compiler.rs @@ -0,0 +1,927 @@ +use crate::{ + interpreter::{ + evaluate::object_system::*, + ast::Interface + }, + virtual_machine::{ + bytecode::{ + OpCode, + make_bytecode, + get_raw_instruction, + Instruction, + opcode_lookup, + }, + symbol_table::SymbolTable, + }, + interpreter::ast, +}; + +pub type InstructionPosition = usize; + +#[derive(Debug, Clone, PartialEq)] +pub struct RawAssembly { + pub instructions: String, + pub constants: Vec, +} + +#[derive(Debug, Clone, PartialEq)] +pub struct EmittedInstruction { + pub opcode: Option, + pub position: usize, +} + +pub struct Compiler { + pub raw_assembly: RawAssembly, + pub last_instruction: EmittedInstruction, + pub previous_instruction: EmittedInstruction, + pub symbol_table: SymbolTable, +} + +impl Compiler { + pub fn new() -> Self { + Compiler { + raw_assembly: RawAssembly { instructions: String::new(), constants: Vec::new() }, + last_instruction: EmittedInstruction { + opcode: None, + position: 0, + }, + previous_instruction: EmittedInstruction { + opcode: None, + position: 0, + }, + symbol_table: SymbolTable::new(), + } + } + + pub fn compile(&mut self, _program: &mut ast::Program) -> Result<(), String> { + for statement in _program.statements.iter() { + let result = self.compile_statement(statement); + + match result { + Ok(_) => (), + Err(e) => return Err(e), + } + } + return Ok(()); + } + + pub fn compile_block(&mut self, block: &ast::BlockStatement) -> Result<(), String> { + for statement in block.statements.iter() { + let result = self.compile_statement(statement); + + match result { + Ok(_) => (), + Err(e) => return Err(e), + } + } + return Ok(()); + } + + fn compile_statement(&mut self, statement: &ast::Statement) -> Result<(), String> { + match statement { + ast::Statement::ExpressionStatement(expression_statement) => { + let result = self.compile_expression(&expression_statement.expression); + + match result { + Ok(_) => (), + Err(e) => return Err(e), + } + + let _ = match self.emit(OpCode::OpPop, vec![]) { + Ok(_) => (), + Err(e) => return Err(e), + }; + }, + ast::Statement::LetStatement(let_statement) => { + let _ = match self.compile_expression(&let_statement.value) { + Ok(_) => (), + Err(e) => return Err(e) + }; + + let symbol = match self.symbol_table.define(let_statement.name.value.clone()) { + Ok(symbol) => symbol, + Err(e) => return Err(e), + }; + let _ = self.emit(OpCode::OpSetGlobal, vec![symbol.index as usize]); + } + _ => return Err(format!("Statement type not supported: {:?}", statement)), + } + + return Ok(()); + } + + fn compile_expression(&mut self, expression: &ast::Expression) -> Result<(), String> { + match expression { + ast::Expression::IndexExpression(index_expression) => { + let _ = match self.compile_expression(&index_expression.left) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + let _ = match self.compile_expression(&index_expression.index) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + let _ = match self.emit(OpCode::OpIndex, vec![]) { + Ok(_) => (), + Err(e) => return Err(e), + }; + } + ast::Expression::HashLiteral(hash_literal) => { + let mut keys: Vec = Vec::new(); + for (key, _) in hash_literal.pairs.iter() { + keys.push(key.clone()); + } + + keys.sort_by(|a, b| a.log().cmp(&b.log())); + + for key in keys.iter() { + let _ = match self.compile_expression(key) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + let _ = match self.compile_expression(hash_literal.pairs.get(key).unwrap()) { + Ok(_) => (), + Err(e) => return Err(e), + }; + } + + let _ = match self.emit(OpCode::OpHash, vec![hash_literal.pairs.len()*2]) { + Ok(_) => (), + Err(e) => return Err(e), + }; + } + ast::Expression::ArrayLiteral(array) => { + for element in array.elements.iter() { + let _ = match self.compile_expression(element) { + Ok(_) => (), + Err(e) => return Err(e), + }; + } + + let _ = match self.emit(OpCode::OpArray, vec![array.elements.len()]) { + Ok(_) => (), + Err(e) => return Err(e), + }; + } + ast::Expression::StringLiteral(string_literal) => { + let string_value = string_literal.value.clone(); + let string_object = Object::StringObject(StringObject { value: string_value }); + + let constant_index = self.add_constant(string_object); + let _ = self.emit(OpCode::OpConstant, vec![constant_index]); + } + ast::Expression::Identifier(identifier) => { + let symbol = match self.symbol_table.resolve(identifier.value.clone()) { + Ok(symbol) => symbol, + Err(e) => return Err(e), + }; + + let _ = match self.emit(OpCode::OpGetGlobal, vec![symbol.index as usize]) { + Ok(_) => (), + Err(e) => return Err(e), + }; + } + ast::Expression::IfExpression(if_expr) => { + let _ = match self.compile_expression(&if_expr.condition) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + // emit an OpJumpNotTruthy with a bogus value + let jump_not_truthy_position = match self.emit(OpCode::OpJumpNotTruthy, vec![9999]) { + Ok(pos) => pos, + Err(e) => return Err(e), + }; + + let _ = match self.compile_block(&if_expr.consequence) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + if self.last_instruction.opcode == Some(OpCode::OpPop) { + self.remove_last_pop(); + } + + let jump_position = match self.emit(OpCode::OpJump, vec![9999]) { + Ok(pos) => pos, + Err(e) => return Err(e), + }; + + let after_consequence_position = self.raw_assembly.instructions.len()/2; + _ = self.change_operand(jump_not_truthy_position, after_consequence_position); + + if if_expr.alternative.is_some() { + let _ = match self.compile_block(if_expr.alternative.as_ref().unwrap()) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + if self.last_instruction.opcode == Some(OpCode::OpPop) { + self.remove_last_pop(); + } + } else { + let _ = self.emit(OpCode::OpNull, vec![]); + } + + let after_alternative_position = self.raw_assembly.instructions.len()/2; + _ = self.change_operand(jump_position, after_alternative_position); + + } + ast::Expression::PrefixExpression(prefix) => { + let _ = match self.compile_expression(&prefix.right) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + let opcode = match prefix.operator.as_str() { + "!" => OpCode::OpBang, + "-" => OpCode::OpMinus, + _ => return Err(format!("Operator not supported: {}", prefix.operator)), + }; + + let _ = match self.emit(opcode, vec![]) { + Ok(_) => (), + Err(e) => return Err(e), + }; + }, + ast::Expression::InfixExpression(infix) => { + let _ = match self.compile_expression(&infix.left) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + let _ = match self.compile_expression(&infix.right) { + Ok(_) => (), + Err(e) => return Err(e), + }; + + let opcode = match infix.operator.as_str() { + "+" => OpCode::OpAdd, + "-" => OpCode::OpSub, + "*" => OpCode::OpMul, + "/" => OpCode::OpDiv, + ">" => OpCode::OpGreaterThan, + "<" => OpCode::OpLessThan, + "==" => OpCode::OpEqual, + "!=" => OpCode::OpNotEqual, + _ => return Err(format!("Operator not supported: {}", infix.operator)), + }; + + let _ = match self.emit(opcode, vec![]) { + Ok(_) => (), + Err(e) => return Err(e), + }; + }, + ast::Expression::IntegerLiteral(integer) => { + let integer_object = Object::Integer(Integer { value: integer.value }); + let constant_index = self.add_constant(integer_object); + + let result = self.emit(OpCode::OpConstant, vec![constant_index]); + + match result { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + ast::Expression::BooleanLiteral(boolean) => { + let opcode = match boolean.value { + true => OpCode::OpTrue, + false => OpCode::OpFalse, + }; + + let result = self.emit(opcode, vec![]); + + match result { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + _ => return Err(format!("Expression type not supported: {:?}", expression)), + } + + return Ok(()); + } + + fn add_constant(&mut self, object: Object) -> usize { + self.raw_assembly.constants.push(object); + return self.raw_assembly.constants.len() - 1; + } + + fn emit(&mut self, opcode: OpCode, operands: Vec) -> Result { + let position_new_instruction = self.raw_assembly.instructions.len(); + _ = self.add_instruction( + match make_bytecode(opcode, operands) { + Ok(instruction) => instruction, + Err(e) => return Err(e), + } + ); + + self.set_last_instruction(opcode, position_new_instruction); + + return Ok(position_new_instruction); + } + + pub fn set_last_instruction(&mut self, opcode: OpCode, position: usize) { + self.previous_instruction = self.last_instruction.clone(); + + let last_instruction = EmittedInstruction { + opcode: Some(opcode), + position: position, + }; + + self.last_instruction = last_instruction; + } + + pub fn add_instruction(&mut self, instruction: Instruction) -> Result<(), String> { + self.raw_assembly.instructions += &match get_raw_instruction(instruction) { + Ok(raw_instruction) => raw_instruction, + Err(e) => return Err(e), + }; + + return Ok(()) + } + + pub fn change_operand(&mut self, position: usize, operand: usize) -> Result<(), String> { + let instruction = match make_bytecode( + match opcode_lookup( + match usize::from_str_radix(&self.raw_assembly.instructions[position..position+2], 16) { + Ok(opcode) => opcode, + Err(e) => return Err(e.to_string()), + } + ) { + Ok(opcode) => opcode, + Err(e) => return Err(e) + }, + vec![operand], + ) { + Ok(instruction) => instruction, + Err(e) => return Err(e), + }; + + self.replace_instruction( + position, + get_raw_instruction(instruction).unwrap().as_str() + ); + + return Ok(()) + } + + pub fn replace_instruction(&mut self, pos: usize, new_instruction: &str) { + self.raw_assembly.instructions.replace_range(pos..pos + new_instruction.len(), new_instruction); + } + + pub fn remove_last_pop(&mut self) { + let last_instruction_position = self.last_instruction.position; + self.raw_assembly.instructions = self.raw_assembly.instructions[..last_instruction_position].to_string(); + self.last_instruction = self.previous_instruction.clone(); + } + + pub fn raw_assembly(&mut self) -> Result { + return Ok(self.raw_assembly.clone()) + } +} + +#[cfg(test)] +mod test { + use core::panic; + use std::vec; + use crate::{parser::Parser, interpreter::lexer::Lexer}; + use crate::virtual_machine::bytecode::{make_bytecode, OpCode, Instructions, get_raw_assembly, format_raw_assembly}; + + use super::*; + + pub struct CompilerTest { + input: String, + expected_constants: Vec, + expected_instructions: Instructions, + } + + pub fn test_instructions(expected: String, actual: String) -> Result<(), String> { + if expected != actual { + return Err( + format!("Wrong instructions.\nExpected: {}\nActual: {}", + format_raw_assembly(expected).unwrap(), format_raw_assembly(actual).unwrap() + )); + } + return Ok(()); + } + + fn test_constants(expected: Vec, actual: Vec) -> Result<(), String> { + if expected.len() != actual.len() { + return Err(format!("Wrong constants length.\nExpected: {}\nActual: {}", expected.len(), actual.len())); + } + + for (i, expected_constant) in expected.iter().enumerate() { + let actual_constant = actual.get(i).unwrap(); + + if expected_constant != actual_constant { + return Err(format!("Wrong constant at {}.\nExpected: {:?}\nActual: {:?}", i, expected_constant, actual_constant)); + } + } + + return Ok(()); + } + + pub fn parse(input: String) -> ast::Program { + let lexer = Lexer::new(input); + let mut parser = Parser::new(lexer); + + return parser.parse_program().unwrap(); + } + + fn run_compiler_tests(tests: Vec) { + for test in tests { + let mut program = parse(test.input); + + let mut compiler = Compiler::new(); + let result = match compiler.compile(&mut program) { + Ok(_) => Ok(()), + Err(e) => Err(e), + }; + + assert_eq!(result, Ok(())); + + let _raw_assembly = match compiler.raw_assembly() { + Ok(bytecode) => bytecode, + Err(e) => panic!("{e}"), + }; + + _ = match test_instructions( + get_raw_assembly(test.expected_instructions).unwrap(), + compiler.raw_assembly.instructions + ) { + Ok(_) => (), + Err(e) => panic!("{e}"), + }; + + _ = match test_constants(test.expected_constants, compiler.raw_assembly.constants) { + Ok(_) => (), + Err(e) => panic!("{e}"), + }; + } + } + + #[test] + fn test_integer_arithmetic() { + let input = vec![ + CompilerTest { + input: String::from("1 + 2"), + expected_constants: vec![ + Object::Integer(Integer { value: 1 }), + Object::Integer(Integer { value: 2 }), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("1 - 2"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpSub, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("1 * 2"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpMul, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("2/1"), + expected_constants: vec![ + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 1}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpDiv, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("-1"), + expected_constants: vec![ + Object::Integer( + Integer {value: 1} + ) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpMinus, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + } + ]; + run_compiler_tests(input); + } + + #[test] + fn test_boolean_expressions() { + let input = vec![ + CompilerTest { + input: String::from("true"), + expected_constants: vec![], + expected_instructions: vec![ + make_bytecode(OpCode::OpTrue, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("false"), + expected_constants: vec![], + expected_instructions: vec![ + make_bytecode(OpCode::OpFalse, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("1 > 2"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpGreaterThan, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("1 < 2"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpLessThan, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("1 == 2"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpEqual, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("1 != 2"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpNotEqual, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("true == false"), + expected_constants: vec![], + expected_instructions: vec![ + make_bytecode(OpCode::OpTrue, vec![]).unwrap(), + make_bytecode(OpCode::OpFalse, vec![]).unwrap(), + make_bytecode(OpCode::OpEqual, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("true != false"), + expected_constants: vec![], + expected_instructions: vec![ + make_bytecode(OpCode::OpTrue, vec![]).unwrap(), + make_bytecode(OpCode::OpFalse, vec![]).unwrap(), + make_bytecode(OpCode::OpNotEqual, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("!true"), + expected_constants: vec![], + expected_instructions: vec![ + make_bytecode(OpCode::OpTrue, vec![]).unwrap(), + make_bytecode(OpCode::OpBang, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + } + ]; + run_compiler_tests(input); + } + + #[test] + fn test_conditionals() { + let inputs = vec![ + CompilerTest { + input: String::from("if (true) { 10 }; 3333;"), + expected_constants: vec![ + Object::Integer(Integer {value: 10}), + Object::Integer(Integer {value: 3333}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpTrue, vec![]).unwrap(), + make_bytecode(OpCode::OpJumpNotTruthy, vec![10]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpJump, vec![11]).unwrap(), + make_bytecode(OpCode::OpNull, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + CompilerTest { + input: String::from("if (true) { 10 } else { 20 }; 3333;"), + expected_constants: vec![ + Object::Integer(Integer {value: 10}), + Object::Integer(Integer {value: 20}), + Object::Integer(Integer {value: 3333}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpTrue, vec![]).unwrap(), + make_bytecode(OpCode::OpJumpNotTruthy, vec![10]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpJump, vec![13]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + } + ]; + run_compiler_tests(inputs); + } + + #[test] + fn test_global_let_statements() { + let inputs = vec![ + CompilerTest { + input: String::from("let one = 1; let two = 2;"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpSetGlobal, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpSetGlobal, vec![1]).unwrap(), + + ] + }, + CompilerTest { + input: String::from("let one = 1; one;"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpSetGlobal, vec![0]).unwrap(), + make_bytecode(OpCode::OpGetGlobal, vec![0]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + CompilerTest { + input: String::from("let one = 1; let two = one; two;"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpSetGlobal, vec![0]).unwrap(), + make_bytecode(OpCode::OpGetGlobal, vec![0]).unwrap(), + make_bytecode(OpCode::OpSetGlobal, vec![1]).unwrap(), + make_bytecode(OpCode::OpGetGlobal, vec![1]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + ]; + run_compiler_tests(inputs); + } + + #[test] + fn test_string_expressions() { + let inputs = vec![ + CompilerTest { + input: String::from("\"monkey\""), + expected_constants: vec![ + Object::StringObject(StringObject {value: String::from("monkey")}) + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + CompilerTest { + input: String::from("\"mon\" + \"key\""), + expected_constants: vec![ + Object::StringObject(StringObject {value: String::from("mon")}), + Object::StringObject(StringObject {value: String::from("key")}), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + } + ]; + run_compiler_tests(inputs); + } + + #[test] + fn test_array_literals() { + let inputs = vec![ + CompilerTest { + input: String::from("[]"), + expected_constants: vec![], + expected_instructions: vec![ + make_bytecode(OpCode::OpArray, vec![0]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + CompilerTest { + input: String::from("[1,2,3]"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 3}), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpArray, vec![3]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + CompilerTest { + input: String::from("[1+2,3-4,5*6]"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 3}), + Object::Integer(Integer {value: 4}), + Object::Integer(Integer {value: 5}), + Object::Integer(Integer {value: 6}), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![3]).unwrap(), + make_bytecode(OpCode::OpSub, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![4]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![5]).unwrap(), + make_bytecode(OpCode::OpMul, vec![]).unwrap(), + make_bytecode(OpCode::OpArray, vec![3]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + } + ]; + run_compiler_tests(inputs); + } + + #[test] + fn test_hash_literals() { + let inputs = vec![ + CompilerTest { + input: String::from("{}"), + expected_constants: vec![], + expected_instructions: vec![ + make_bytecode(OpCode::OpHash, vec![0]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + CompilerTest { + input: String::from("{1:2, 3:4, 5:6}"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 3}), + Object::Integer(Integer {value: 4}), + Object::Integer(Integer {value: 5}), + Object::Integer(Integer {value: 6}), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![3]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![4]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![5]).unwrap(), + make_bytecode(OpCode::OpHash, vec![6]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + }, + CompilerTest { + input: String::from("{1+1:2+3, 4+5:5*6}"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 3}), + Object::Integer(Integer {value: 4}), + Object::Integer(Integer {value: 5}), + Object::Integer(Integer {value: 5}), + Object::Integer(Integer {value: 6}), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![3]).unwrap(), + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![4]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![5]).unwrap(), + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![6]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![7]).unwrap(), + make_bytecode(OpCode::OpMul, vec![]).unwrap(), + make_bytecode(OpCode::OpHash, vec![4]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + } + ]; + run_compiler_tests(inputs); + } + + #[test] + fn test_index_expressions() { + let inputs = vec![ + CompilerTest { + input: String::from("[1,2,3][1+1]"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 3}), + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 1}), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpArray, vec![3]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![3]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![4]).unwrap(), + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpIndex, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ], + }, + CompilerTest { + input: String::from("{1:2}[2 - 1]"), + expected_constants: vec![ + Object::Integer(Integer {value: 1}), + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 2}), + Object::Integer(Integer {value: 1}), + ], + expected_instructions: vec![ + make_bytecode(OpCode::OpConstant, vec![0]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpHash, vec![2]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![3]).unwrap(), + make_bytecode(OpCode::OpSub, vec![]).unwrap(), + make_bytecode(OpCode::OpIndex, vec![]).unwrap(), + make_bytecode(OpCode::OpPop, vec![]).unwrap(), + ] + } + ]; + run_compiler_tests(inputs); + } +} \ No newline at end of file diff --git a/src/interpreter/evaluate.rs b/src/interpreter/evaluate.rs index f9786ce..5dfbf52 100644 --- a/src/interpreter/evaluate.rs +++ b/src/interpreter/evaluate.rs @@ -2,12 +2,15 @@ pub use crate::interpreter::{ ast, tokens }; +use std::collections::HashMap; use std::ops::Deref; use std::hash::{Hash, Hasher}; use std::rc::Rc; use std::cell::RefCell; +use crate::virtual_machine::bytecode::Instructions; + pub mod object_system { use crate::interpreter::ast::Interface; @@ -24,6 +27,7 @@ pub mod object_system { ArrayObject(ArrayObject), HashObject(HashObject), EvalError(EvalError), + CompiledFunction(CompiledFunction), Null, } @@ -37,6 +41,7 @@ pub mod object_system { ArrayObject, HashObject, EvalError, + CompiledFunction, NULL, } @@ -57,6 +62,7 @@ pub mod object_system { Object::BuiltinFunctionObject(bfo) => bfo.log(), Object::ArrayObject(ao) => ao.log(), Object::HashObject(ho) => ho.log(), + Object::CompiledFunction(cf) => cf.log(), Object::Null => "0".to_string(), } } @@ -72,6 +78,7 @@ pub mod object_system { Object::ArrayObject(ao) => ao.object_type(), Object::StringObject(so) => so.object_type(), Object::HashObject(ho) => ho.object_type(), + Object::CompiledFunction(cf) => cf.object_type(), Object::Null => ObjectType::NULL, } } @@ -82,6 +89,7 @@ pub mod object_system { Integer(Integer), Boolean(Boolean), StringObject(StringObject), + CompiledFunction(CompiledFunction), } impl ObjectInterface for HashableObject { @@ -90,6 +98,7 @@ pub mod object_system { HashableObject::Integer(i) => i.log(), HashableObject::Boolean(b) => b.log(), HashableObject::StringObject(so) => so.log(), + HashableObject::CompiledFunction(cf) => cf.log(), } } @@ -98,13 +107,14 @@ pub mod object_system { HashableObject::Integer(i) => i.object_type(), HashableObject::Boolean(b) => b.object_type(), HashableObject::StringObject(so) => so.object_type(), + HashableObject::CompiledFunction(cf) => cf.object_type(), } } } #[derive(Debug, Clone, PartialEq)] pub struct HashObject { - pub pairs: std::collections::HashMap, + pub pairs: HashMap, } impl Hash for HashObject { @@ -127,6 +137,29 @@ pub mod object_system { } } + #[derive(Debug, Clone, PartialEq)] + #[derive(Eq, Hash)] + pub struct CompiledFunction { + pub instructions: Instructions + } + + impl ObjectInterface for CompiledFunction { + fn log(&self) -> String { + format!( + "[{}]", + self.instructions + .iter() + .map(|e| format!("{:?}", e)) + .collect::>() + .join(", ") + ) + } + + fn object_type(&self) -> ObjectType { + return ObjectType::CompiledFunction; + } + } + #[derive(Debug, Clone, PartialEq)] pub struct ArrayObject { pub elements: Vec, diff --git a/src/lib.rs b/src/lib.rs index 8d72ebd..6914c84 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ use interpreter::{ lexer, }; +use compiler::compiler::{Compiler}; +use virtual_machine::virtual_machine::VirtualMachine; + use std::fs; use crate::interpreter::evaluate::object_system::ObjectInterface; @@ -42,6 +45,104 @@ fn run_code(input: &str, env: Option<&mut environment::Environment>) -> object_s result } +pub fn compile_and_run( + input: &str, +) -> object_system::Object { + let lexer = lexer::Lexer::new(input.to_string()); + let mut parser = parser::Parser::new(lexer); + + let mut program = match parser.parse_program() { + Ok(program) => program, + Err(e) => { + return object_system::Object::EvalError( + object_system::EvalError{ + message: e.message + } + ) + } + }; + + let mut compiler = Compiler::new(); + match compiler.compile(&mut program) { + Ok(_) => (), + Err(e) => { + return object_system::Object::EvalError( + object_system::EvalError{ + message: e + } + ) + } + } + + let bytecode = match compiler.raw_assembly() { + Ok(bytecode) => bytecode, + Err(e) => { + return object_system::Object::EvalError( + object_system::EvalError{ + message: e + } + ) + } + }; + + let mut vm = VirtualMachine::new(bytecode); + + match vm.run() { + Ok(_) => (), + Err(e) => { + return object_system::Object::EvalError( + object_system::EvalError{ + message: e + } + ) + } + } + + let result = match vm.stack.last_popped_stack_element() { + Ok(object) => object, + Err(e) => object_system::Object::EvalError( + object_system::EvalError{ + message: e + } + ), + }; + + result +} + +pub fn start_compiler_repl() { + let mut input = String::new(); + loop { + // read input + println!(">>"); + let mut command = String::new(); + match std::io::stdin().read_line(&mut command) { + Ok(_) => {}, + Err(e) => println!("Error reading command: {}", e), + } + + // if command is "quit", break + if command.trim() == "quit" { + break; + } + + input = input + &command; + + let result = compile_and_run(&input); + + // print result + match result { + object_system::Object::Integer(i) => println!("{}", i.log()), + object_system::Object::Boolean(b) => println!("{}", b.log()), + object_system::Object::EvalError(e) => println!("{}", e.log()), + object_system::Object::ReturnValue(r) => println!("{}", r.log()), + object_system::Object::StringObject(s) => println!("{}", s.log()), + object_system::Object::ArrayObject(a) => println!("{}", a.log()), + _ => {} + } + } +} + pub fn start_repl() { let mut env = environment::Environment::new(None); loop { @@ -86,6 +187,7 @@ pub fn start_interpreter(config: &config::Config) { object_system::Object::ArrayObject(a) => println!("{:?}", a), object_system::Object::HashObject(h) => println!("{:?}", h), object_system::Object::BuiltinFunctionObject(b) => println!("{:?}", b), + _ => panic!("Incorrect Output") } } diff --git a/src/main.rs b/src/main.rs index 7c23b14..8641b98 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,8 @@ pub use monkey_lang::{ config::Config, interpreter::evaluate, start_interpreter, - start_repl + start_repl, + start_compiler_repl }; fn main() { @@ -13,7 +14,7 @@ fn main() { }, None => { println!("Running REPL"); - start_repl() + start_compiler_repl() } }; } diff --git a/src/virtual_machine.rs b/src/virtual_machine.rs index 348db31..bfa5c9c 100644 --- a/src/virtual_machine.rs +++ b/src/virtual_machine.rs @@ -1 +1,3 @@ -pub mod bytecode; \ No newline at end of file +pub mod bytecode; +pub mod virtual_machine; +pub mod symbol_table; diff --git a/src/virtual_machine/bytecode.rs b/src/virtual_machine/bytecode.rs index 9ed8b47..ef8a276 100644 --- a/src/virtual_machine/bytecode.rs +++ b/src/virtual_machine/bytecode.rs @@ -1,38 +1,284 @@ -use crate::object_system::Object; -use std::vec; +use std::{vec, hash::Hash, collections::{HashMap}}; +use lazy_static::lazy_static; -pub struct Bytecode { - pub instructions: Instructions, - pub constants: Vec, -} +pub type Byte = u8; +pub type Instruction = Vec; +pub type Instructions = Vec; -type Instructions = Vec; -type Instruction = Vec; +#[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)] +pub enum OpCode { + OpConstant, + OpAdd, + OpSub, + OpMul, + OpDiv, + OpPop, + OpTrue, + OpFalse, + OpEqual, + OpNotEqual, + OpGreaterThan, + OpLessThan, + OpMinus, + OpBang, + OpJumpNotTruthy, + OpJump, + OpNull, + OpSetGlobal, + OpGetGlobal, + OpArray, + OpHash, + OpIndex, + OpCall, + OpReturnValue, +} -#[derive(Debug, Clone, PartialEq)] -pub struct Opcode { - pub name: OpcodeName, - pub operand_widths: Vec, +#[derive(Debug, Clone)] +pub struct OpCodeLayout { + pub name: OpCode, + pub operand_widths: Vec, // number of bytes each operand takes up } -#[derive(Debug, Clone, PartialEq)] -pub enum OpcodeName { - OpConstant, +lazy_static! { + static ref OPCODE_LAYOUTS: HashMap = { + HashMap::from([ + ( + OpCode::OpReturnValue, + OpCodeLayout { + name: OpCode::OpReturnValue, + operand_widths: vec![] + } + ), + ( + OpCode::OpCall, + OpCodeLayout { + name: OpCode::OpCall, + operand_widths: vec![1] + } + ), + ( + OpCode::OpIndex, + OpCodeLayout { + name: OpCode::OpIndex, + operand_widths: vec![] + } + ), + ( + OpCode::OpHash, + OpCodeLayout { + name: OpCode::OpHash, + operand_widths: vec![2] + } + ), + ( + OpCode::OpConstant, + OpCodeLayout { + name: OpCode::OpConstant, + operand_widths: vec![2], + }, + ), + ( + OpCode::OpAdd, + OpCodeLayout { + name: OpCode::OpAdd, + operand_widths: vec![], + }, + ), + ( + OpCode::OpSub, + OpCodeLayout { + name: OpCode::OpSub, + operand_widths: vec![], + }, + ), + ( + OpCode::OpMul, + OpCodeLayout { + name: OpCode::OpMul, + operand_widths: vec![], + }, + ), + ( + OpCode::OpDiv, + OpCodeLayout { + name: OpCode::OpDiv, + operand_widths: vec![], + }, + ), + ( + OpCode::OpPop, + OpCodeLayout { + name: OpCode::OpPop, + operand_widths: vec![], + }, + ), + ( + OpCode::OpTrue, + OpCodeLayout { + name: OpCode::OpTrue, + operand_widths: vec![], + }, + ), + ( + OpCode::OpFalse, + OpCodeLayout { + name: OpCode::OpFalse, + operand_widths: vec![], + }, + ), + ( + OpCode::OpEqual, + OpCodeLayout { + name: OpCode::OpEqual, + operand_widths: vec![], + }, + ), + ( + OpCode::OpNotEqual, + OpCodeLayout { + name: OpCode::OpNotEqual, + operand_widths: vec![], + }, + ), + ( + OpCode::OpGreaterThan, + OpCodeLayout { + name: OpCode::OpGreaterThan, + operand_widths: vec![], + }, + ), + ( + OpCode::OpLessThan, + OpCodeLayout { + name: OpCode::OpLessThan, + operand_widths: vec![], + }, + ), + ( + OpCode::OpMinus, + OpCodeLayout { + name: OpCode::OpMinus, + operand_widths: vec![] + }, + ), + ( + OpCode::OpBang, + OpCodeLayout { + name: OpCode::OpBang, + operand_widths: vec![] + }, + ), + ( + OpCode::OpJumpNotTruthy, + OpCodeLayout { + name: OpCode::OpJumpNotTruthy, + operand_widths: vec![2] + } + ), + ( + OpCode::OpJump, + OpCodeLayout { + name: OpCode::OpJump, + operand_widths: vec![2] + } + ), + ( + OpCode::OpNull, + OpCodeLayout { + name: OpCode::OpNull, + operand_widths: vec![] + } + ), + ( + OpCode::OpSetGlobal, + OpCodeLayout { + name: OpCode::OpSetGlobal, + operand_widths: vec![2] + } + ), + ( + OpCode::OpGetGlobal, + OpCodeLayout { + name: OpCode::OpGetGlobal, + operand_widths: vec![2] + } + ), + ( + OpCode::OpArray, + OpCodeLayout { + name: OpCode::OpArray, + operand_widths: vec![2] + } + ) + ]) + }; } -impl Opcode { - pub fn new(name: OpcodeName) -> Opcode { - match name { - OpcodeName::OpConstant => Opcode { - name, - operand_widths: vec![2], - }, - } +pub fn opcode_lookup(opcode: usize) -> Result { + match opcode { + 0 => Ok(OpCode::OpConstant), + 1 => Ok(OpCode::OpAdd), + 2 => Ok(OpCode::OpSub), + 3 => Ok(OpCode::OpMul), + 4 => Ok(OpCode::OpDiv), + 5 => Ok(OpCode::OpPop), + 6 => Ok(OpCode::OpTrue), + 7 => Ok(OpCode::OpFalse), + 8 => Ok(OpCode::OpEqual), + 9 => Ok(OpCode::OpNotEqual), + 10 => Ok(OpCode::OpGreaterThan), + 11 => Ok(OpCode::OpLessThan), + 12 => Ok(OpCode::OpMinus), + 13 => Ok(OpCode::OpBang), + 14 => Ok(OpCode::OpJumpNotTruthy), + 15 => Ok(OpCode::OpJump), + 16 => Ok(OpCode::OpNull), + 17 => Ok(OpCode::OpSetGlobal), + 18 => Ok(OpCode::OpGetGlobal), + 19 => Ok(OpCode::OpArray), + 20 => Ok(OpCode::OpHash), + 21 => Ok(OpCode::OpIndex), + 22 => Ok(OpCode::OpCall), + 23 => Ok(OpCode::OpReturnValue), + _ => Err(format!("Opcode {} not found", opcode)), } } -pub fn make_instruction(opcode: Opcode, operands: Vec) -> Result { - let expected_operand_count = opcode.operand_widths.len(); +/// Creates a bytecode instruction from an opcode and operands +/// +/// # Arguments +/// +/// * `opcode_name` - The opcode to use +/// * `operands` - The operands to use +/// +/// # Returns +/// +/// * `Result` - The bytecode instruction +/// * `Ok(Instruction)` - The bytecode instruction +/// * `Err(String)` - The error message +/// +/// # Examples +/// +/// ``` +/// use monkey_lang::virtual_machine::bytecode::{make_bytecode, OpCode}; +/// +/// let result = make_bytecode(OpCode::OpConstant, vec![65534]); +/// assert_eq!(result, Ok(vec![0, 255, 254])); +/// ``` +/// +/// ``` +/// use monkey_lang::virtual_machine::bytecode::{make_bytecode, OpCode}; +/// +/// let result = make_bytecode(OpCode::OpConstant, vec![65535]); +/// assert_eq!(result, Ok(vec![0, 255, 255])); +/// ``` +pub fn make_bytecode(opcode_name: OpCode, operands: Vec) -> Result { + let opcode_layout = match OPCODE_LAYOUTS.get(&opcode_name) { + Some(layout) => layout, + None => return Err(format!("Opcode {} not found", opcode_name as u8)), + }; + let expected_operand_count = opcode_layout.operand_widths.len(); + let actual_operand_count = operands.len(); if expected_operand_count != actual_operand_count { @@ -41,52 +287,312 @@ pub fn make_instruction(opcode: Opcode, operands: Vec) -> Result (operand as u16).to_be_bytes().to_vec(), - _ => return Err(format!("Invalid operand width {}", operand_width)), - }; - byte_operands.append(&mut bytes); + } + + let mut byte_operands = Vec::new(); + for i in 0..operands.len() { + let operand_width = opcode_layout.operand_widths[i]; + let operand = operands[i]; + + let mut bytes= match operand_width { + 1 => (operand as u8).to_be_bytes().to_vec(), + 2 => (operand as u16).to_be_bytes().to_vec(), + _ => return Err(format!("Invalid operand width {}", operand_width)), + }; + byte_operands.append(&mut bytes); + } + let mut instruction = vec![opcode_layout.name as u8]; + instruction.append(&mut byte_operands); + Ok(instruction) +} + +pub fn get_raw_instruction(instruction: Instruction) -> Result { + return Ok(format!( + "{}", + instruction + .iter() + .map(|byte| format!("{:02X}", byte)) // format as hexadecimals + .collect::() + )); +} + +pub fn get_raw_assembly(instructions: Instructions) -> Result { + let mut bytecode = String::new(); + for instruction in instructions { + bytecode = bytecode + &match get_raw_instruction(instruction) { + Ok(raw_instruction) => raw_instruction, + Err(e) => return Err(e) } - let mut instruction = vec![opcode.name as u8]; - instruction.append(&mut byte_operands); - Ok(instruction) - } + } + Ok(bytecode) +} + +pub fn format_raw_assembly(raw_assembly: String) -> Result { + // println!("bytecode: {:?}", raw_assembly); + let mut result = String::new(); + let mut offset = 0; + + while offset < raw_assembly.len() { + let opcode = opcode_lookup( + usize::from_str_radix(&raw_assembly[offset..(offset+2)], 16).unwrap() + ).unwrap(); + // println!("opcode: {:?}", opcode); + + // get instruction operands + let (operands, bytes_read) = read_operands(opcode, &raw_assembly[(offset+2)..]).unwrap(); + + // format instruction + result = result + &format!("{:04} ", offset/2) + &format_instruction(opcode, operands) + "\n"; + + // increment offset + offset += 2*(1 + bytes_read); + } + + Ok(result) } +pub fn read_operands(opcode: OpCode, operands: &str) -> Result<(Vec, usize), String> { + // println!("opcode: {:?}", opcode); + // println!("operands: {:?}", operands); + + // let operands = operands.as_bytes(); + // println!("operands: {:?}", operands); + + let opcode_layout = match OPCODE_LAYOUTS.get(&opcode) { + Some(layout) => layout, + None => return Err(format!("Opcode {} not found", opcode as u8)), + }; + + let mut offset = 0; + let mut operands_vec = Vec::new(); + for operand_width in opcode_layout.operand_widths.iter() { + let operand = match operand_width { + 2 => { + usize::from_str_radix(&operands[offset..(offset + 2*operand_width)], 16).unwrap() + } + _ => return Err(format!("Invalid operand width {}", operand_width)), + }; + operands_vec.push(operand); + offset += operand_width; + } + Ok((operands_vec, offset)) +} + +pub fn format_instruction(opcode: OpCode, operands: Vec) -> String { + let opcode_layout = match OPCODE_LAYOUTS.get(&opcode) { + Some(layout) => layout, + None => return format!("Opcode {} not found", opcode as u8), + }; + + let operand_count = opcode_layout.operand_widths.len(); + if operands.len() != operand_count { + return format!( + "Expected {} operands, got {}", + operand_count, + operands.len() + ); + } + + match opcode { + OpCode::OpConstant => format!("OpConstant {:?}", operands[0]), + OpCode::OpAdd => format!("OpAdd"), + OpCode::OpPop => format!("OpPop"), + OpCode::OpTrue => format!("OpTrue"), + OpCode::OpFalse => format!("OpFalse"), + OpCode::OpEqual => format!("OpEqual"), + OpCode::OpNotEqual => format!("OpNotEqual"), + OpCode::OpGreaterThan => format!("OpGreaterThan"), + OpCode::OpLessThan => format!("OpLessThan"), + OpCode::OpMinus => format!("OpMinus"), + OpCode::OpBang => format!("OpBang"), + OpCode::OpJumpNotTruthy => format!("OpJumpNotTruthy {:?}", operands[0]), + OpCode::OpJump => format!("OpJump {:?}", operands[0]), + OpCode::OpNull => format!("OpNull"), + OpCode::OpSetGlobal => format!("OpSetGlobal {:?}", operands[0]), + OpCode::OpGetGlobal => format!("OpGetGlobal {:?}", operands[0]), + OpCode::OpArray => format!("OpArray {:?}", operands[0]), + OpCode::OpHash => format!("OpHash {:?}", operands[0]), + OpCode::OpIndex => format!("OpIndex"), + OpCode::OpCall => format!("OpCall {:?}", operands[0]), + OpCode::OpReturnValue => format!("OpReturnValue"), + _ => format!("Opcode {} not implemented", opcode as u8), + } +} #[cfg(test)] mod tests { use super::*; #[test] - fn test_make_instruction() { + fn test_read_operands() { struct Test { - opcode: Opcode, + opcode: OpCode, + operands: Vec, + bytes_read: usize, + } + + let inputs = vec![ + Test { + opcode: OpCode::OpConstant, + operands: vec![65534], + bytes_read: 2, + }, + Test { + opcode: OpCode::OpAdd, + operands: vec![], + bytes_read: 0, + }, + ]; + + for input in inputs { + let instruction = get_raw_assembly( + vec![make_bytecode(input.opcode, input.operands.clone()).unwrap()] + ).unwrap(); + // println!("instruction: {:?}", instruction); + + let opcode = opcode_lookup( + instruction[0..2].parse::().unwrap() + ).unwrap(); + // println!("opcode: {:?}", opcode); + + let (operands, bytes_read) = read_operands(opcode, &instruction[2..instruction.len()]).unwrap(); + println!("operands_vec: {:?}", operands); + println!("offset: {:?}", bytes_read); + + assert_eq!(bytes_read, input.bytes_read); + for (i, operand) in input.operands.iter().enumerate() { + assert_eq!(*operand, operands[i]); + } + } + } + + #[test] + fn test_format_raw_assembly() { + struct Test { + raw_assembly: String, + expected: String, + } + + let inputs = vec![ + Test { + raw_assembly: "00000100000200FFFF00FFFE".to_string(), + expected: String::from("0000 OpConstant 1\n0003 OpConstant 2\n0006 OpConstant 65535\n0009 OpConstant 65534\n"), + }, + Test { + raw_assembly: "000001".to_string(), + expected: String::from("0000 OpConstant 1\n"), + }, + Test { + raw_assembly: "01000001000002".to_string(), + expected: String::from("0000 OpAdd\n0001 OpConstant 1\n0004 OpConstant 2\n"), + }, + Test { + raw_assembly: "0100000100000205".to_string(), + expected: String::from("0000 OpAdd\n0001 OpConstant 1\n0004 OpConstant 2\n0007 OpPop\n"), + }, + ]; + + for test in inputs { + let result = match format_raw_assembly(test.raw_assembly) { + Ok(result) => result, + Err(error) => panic!("Error: {}", error), + }; + + assert_eq!(result, test.expected); + } + } + + #[test] + fn test_make_raw_assembly() { + struct Test { + bytecode: Instructions, + expected: String, + } + + let inputs = vec![ + Test { + bytecode: vec![ + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![65535]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![65534]).unwrap(), + ], + expected: String::from("00000100000200FFFF00FFFE"), + }, + Test { + bytecode: vec![ + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + ], + expected: String::from("000001"), + }, + Test { + bytecode: vec![ + make_bytecode(OpCode::OpAdd, vec![]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![1]).unwrap(), + make_bytecode(OpCode::OpConstant, vec![2]).unwrap(), + ], + expected: String::from("01000001000002"), + }, + ]; + + for test in inputs { + let result = match get_raw_assembly(test.bytecode) { + Ok(result) => result, + Err(error) => panic!("Error: {}", error), + }; + + assert_eq!(result, test.expected); + } + } + + #[test] + fn test_make_bytecode() { + struct Test { + opcode: OpCode, operands: Vec, expected: Instruction, } let inputs = vec![ Test { - opcode: Opcode::new(OpcodeName::OpConstant), + opcode: OpCode::OpConstant, operands: vec![65534], expected: vec![0, 255, 254], }, Test { - opcode: Opcode::new(OpcodeName::OpConstant), + opcode: OpCode::OpConstant, + operands: vec![65535], + expected: vec![0, 255, 255], + }, + Test { + opcode: OpCode::OpConstant, operands: vec![45], expected: vec![0, 0, 45], }, + Test { + opcode: OpCode::OpConstant, + operands: vec![0], + expected: vec![0, 0, 0], + }, + Test { + opcode: OpCode::OpConstant, + operands: vec![1], + expected: vec![0, 0, 1], + }, + Test { + opcode: OpCode::OpConstant, + operands: vec![2], + expected: vec![0, 0, 2], + }, + Test { + opcode: OpCode::OpAdd, + operands: vec![], + expected: vec![1], + }, ]; for test in inputs { - let result = match make_instruction(test.opcode, test.operands) { + let result = match make_bytecode(test.opcode, test.operands) { Ok(instruction) => instruction, Err(error) => panic!("Error: {}", error), }; diff --git a/src/virtual_machine/symbol_table.rs b/src/virtual_machine/symbol_table.rs new file mode 100644 index 0000000..dd7f083 --- /dev/null +++ b/src/virtual_machine/symbol_table.rs @@ -0,0 +1,90 @@ +use lazy_static::lazy_static; +use std::collections::HashMap; + +pub type SymbolScope = String; + +lazy_static! { + static ref GLOBAL_SCOPE: SymbolScope = String::from("GLOBAL"); +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Symbol { + pub name: String, + pub scope: SymbolScope, + pub index: i32 +} + +#[derive(Debug, Clone)] +pub struct SymbolTable { + pub store: HashMap, + pub number_of_definitions: i32 +} + +impl SymbolTable { + pub fn new() -> Self { + SymbolTable { + store: HashMap::new(), + number_of_definitions: 0 + } + } + + pub fn define(&mut self, symbol_name: String) -> Result { + let symbol = Symbol { + name: symbol_name.clone(), + scope: GLOBAL_SCOPE.clone(), + index: self.number_of_definitions + }; + + self.store.insert(symbol_name, symbol.clone()); + self.number_of_definitions += 1; + + return Ok(symbol) + } + + pub fn resolve(&mut self, symbol_name: String) -> Result { + match self.store.get(&symbol_name) { + Some(symbol) => Ok(symbol.clone()), + None => Err(format!("Symbol {} not found", symbol_name)) + } + } +} + +mod test { + + #[test] + fn test_define_symbols() { + let expected = super::HashMap::from( + [ + ("a" , super::Symbol{name: "a".to_string(), scope: super::GLOBAL_SCOPE.clone(), index: 0}), + ("b" , super::Symbol{name: "b".to_string(), scope: super::GLOBAL_SCOPE.clone(), index: 1}), + ] + ); + + let mut global = super::SymbolTable::new(); + + let a = global.define("a".to_string()).unwrap(); + + assert!(a == expected["a"]); + + let b = global.define("b".to_string()).unwrap(); + + assert!(b == expected["b"]); + } + + #[test] + fn test_resolve_symbol() { + let mut global = super::SymbolTable::new(); + global.define("a".to_string()).unwrap(); + global.define("b".to_string()).unwrap(); + + let expected = vec![ + super::Symbol{name: "a".to_string(), scope: super::GLOBAL_SCOPE.clone(), index: 0}, + super::Symbol{name: "b".to_string(), scope: super::GLOBAL_SCOPE.clone(), index: 1}, + ]; + + for sym in expected { + let result = global.resolve(sym.name.clone()).unwrap(); + assert!(result == sym); + } + } +} \ No newline at end of file diff --git a/src/virtual_machine/virtual_machine.rs b/src/virtual_machine/virtual_machine.rs new file mode 100644 index 0000000..c92ed92 --- /dev/null +++ b/src/virtual_machine/virtual_machine.rs @@ -0,0 +1,1383 @@ +use std::collections::HashMap; + +use crate::interpreter::evaluate::object_system::{self, ObjectInterface}; +use crate::object_system::*; +use crate::compiler::compiler::RawAssembly; +use crate::virtual_machine::bytecode::{ + OpCode, + opcode_lookup, +}; + +pub const STACK_SIZE: usize = 2048; +pub const GLOBALS_SIZE: usize = 65536; + +pub struct VmStack { + pub stack: Vec, + pub stack_pointer: usize, +} + +impl VmStack { + pub fn new() -> Self { + VmStack { + stack: vec![object_system::Object::Null; STACK_SIZE], + stack_pointer: 0, + } + } + + pub fn stack_top(&self) -> Result { + if self.stack_pointer == 0 { + return Err(String::from("Stack is empty")); + } + + let stack_top = self.stack[self.stack_pointer - 1].clone(); + + return Ok(stack_top.clone()); + } + + pub fn last_popped_stack_element(&self) -> Result { + let stack_top = self.stack[self.stack_pointer].clone(); + + return Ok(stack_top.clone()); + } + + pub fn stack_pop(&mut self) -> Result { + if self.stack_pointer == 0 { + return Err(String::from("Stack is empty")); + } + + let stack_top = self.stack[self.stack_pointer - 1].clone(); + self.stack_pointer -= 1; + + return Ok(stack_top); + } + + pub fn push_constant(&mut self, object: Object) -> Result<(), String> { + if self.stack_pointer >= STACK_SIZE { + return Err(String::from("Stack overflow")); + } + self.stack[self.stack_pointer] = object.clone(); + self.stack_pointer += 1; + + return Ok(()); + } +} + +pub struct VmGlobals { + pub store: Vec, + pub number_of_definitions: usize, +} + +impl VmGlobals { + pub fn new() -> Self { + VmGlobals { + store: vec![object_system::Object::Null; GLOBALS_SIZE], + number_of_definitions: 0, + } + } +} + +pub struct VirtualMachine { + pub stack: VmStack, + pub globals: VmGlobals, + pub assembly: RawAssembly, +} + +impl VirtualMachine { + pub fn new(assembly: RawAssembly) -> Self { + VirtualMachine { + stack: VmStack::new(), + globals: VmGlobals::new(), + assembly, + } + } + + pub fn stack_top(&self) -> Result { + self.stack.stack_top() + } + + pub fn stack_pop(&mut self) -> Result { + self.stack.stack_pop() + } + + pub fn run(&mut self) -> Result<(), String> { + let mut instruction_pointer = 0; + while instruction_pointer < self.assembly.instructions.len() { + let opcode = opcode_lookup( + usize::from_str_radix(&self.assembly.instructions[instruction_pointer..instruction_pointer+2], 16).unwrap(), + ).unwrap(); + instruction_pointer += 2; + + match opcode { + OpCode::OpIndex => { + let index = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + let left = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + _ = match self.execute_index_operation(left, index) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpHash => { + let number_of_elements = usize::from_str_radix( + &self.assembly.instructions[instruction_pointer..instruction_pointer+4], + 16 + ).unwrap(); + instruction_pointer += 4; + + let mut hash_pairs = HashMap::new(); + for _ in 0..(number_of_elements/2) { + let value = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + let key = match self.stack.stack_pop() { + Ok(object) => { + match object.object_type() { + object_system::ObjectType::BOOLEAN => { + match object { + Object::Boolean(boolean) => { + HashableObject::Boolean(boolean) + }, + _ => return Err(String::from("Unsupported types")), + } + }, + object_system::ObjectType::INTEGER => { + match object { + Object::Integer(integer) => { + HashableObject::Integer(integer) + }, + _ => return Err(String::from("Unsupported types")), + } + }, + object_system::ObjectType::StringObject => { + match object { + Object::StringObject(string) => { + HashableObject::StringObject(string) + }, + _ => return Err(String::from("Unsupported types")), + } + }, + _ => return Err(String::from("Unsupported types")), + } + } + Err(e) => return Err(e), + }; + + hash_pairs.insert( + HashableObject::from(key), + value + ); + } + + match self.stack.push_constant( + Object::HashObject(object_system::HashObject { pairs: hash_pairs }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpArray => { + let number_of_elements = usize::from_str_radix( + &self.assembly.instructions[instruction_pointer..instruction_pointer+4], + 16 + ).unwrap(); + instruction_pointer += 4; + + let mut array_elements = Vec::new(); + for _ in 0..number_of_elements { + let element = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + array_elements.push(element); + } + + array_elements.reverse(); + + match self.stack.push_constant( + Object::ArrayObject(object_system::ArrayObject { elements: array_elements }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpSetGlobal => { + let global_index = self.assembly.instructions[instruction_pointer..instruction_pointer+4].parse::().unwrap(); + instruction_pointer += 4; + + self.globals.store[global_index] = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + self.globals.number_of_definitions += 1; + } + OpCode::OpGetGlobal => { + let global_index = self.assembly.instructions[instruction_pointer..instruction_pointer+4].parse::().unwrap(); + instruction_pointer += 4; + + match self.stack.push_constant( + self.globals.store[global_index].clone() + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + } + OpCode::OpConstant => { + let const_index = self.assembly.instructions[instruction_pointer..instruction_pointer+4].parse::().unwrap(); + instruction_pointer += 4; + + match self.stack.push_constant( + self.assembly.constants[const_index].clone() + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpAdd | OpCode::OpDiv | OpCode::OpMul | OpCode::OpSub => { + let _ = match self.run_binary_operation(opcode) { + Ok(_) => (), + Err(e) => return Err(e), + }; + }, + OpCode::OpPop => { + match self.stack.stack_pop() { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpTrue => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: true }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpFalse => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: false }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpEqual | OpCode::OpNotEqual | OpCode::OpGreaterThan | OpCode::OpLessThan => { + let _ = match self.run_comparison(opcode) { + Ok(_) => (), + Err(e) => return Err(e), + }; + }, + OpCode::OpBang => { + let _ = match self.run_bang_operator() { + Ok(_) => (), + Err(e) => return Err(e), + }; + }, + OpCode::OpMinus => { + let _ = match self.run_minus_operator() { + Ok(_) => (), + Err(e) => return Err(e), + }; + }, + OpCode::OpJump => { + let jump_position = usize::from_str_radix( + &self.assembly.instructions[instruction_pointer..instruction_pointer+4], + 16 + ).unwrap(); + instruction_pointer = jump_position*2; + }, + OpCode::OpJumpNotTruthy => { + let jump_position = usize::from_str_radix( + &self.assembly.instructions[instruction_pointer..instruction_pointer+4], + 16 + ).unwrap(); + instruction_pointer += 4; + + let condition = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + match condition { + object_system::Object::Boolean(boolean) => { + if !boolean.value { + instruction_pointer = jump_position*2; + } + }, + _ => () // defaults to True => No Jump Required + } + }, + OpCode::OpNull => { + match self.stack.push_constant( + Object::Null + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + _ => return Err(format!("Opcode not implemented: {:?}", opcode)), + } + } + + return Ok(()); + } + + pub fn execute_index_operation(&mut self, left: object_system::Object, index: object_system::Object) -> Result<(), String> { + match (left.object_type(), index.object_type()) { + (object_system::ObjectType::ArrayObject, object_system::ObjectType::INTEGER) => { + match self.execute_array_index_operation(left, index) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + (object_system::ObjectType::HashObject, _) => { + match self.execute_hash_index_operation(left, index) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + _ => return Err(String::from("Unsupported types")), + } + + return Ok(()); + } + + pub fn execute_hash_index_operation(&mut self, left: object_system::Object, index: object_system::Object) -> Result<(), String> { + let left = match left { + Object::HashObject(hash) => hash.pairs, + _ => return Err(String::from("Unsupported types")), + }; + + let index = match index { + Object::Boolean(boolean) => HashableObject::Boolean(boolean), + Object::Integer(integer) => HashableObject::Integer(integer), + Object::StringObject(string) => HashableObject::StringObject(string), + _ => return Err(String::from("Unsupported types")), + }; + + match left.get(&index) { + Some(value) => { + match self.stack.push_constant( + value.clone() + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + None => { + match self.stack.push_constant( + Object::Null + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + } + + return Ok(()) + } + + pub fn execute_array_index_operation(&mut self, left: object_system::Object, index: object_system::Object) -> Result<(), String> { + let left = match left { + Object::ArrayObject(array) => array.elements, + _ => return Err(String::from("Unsupported types")), + }; + + let index = match index { + Object::Integer(integer) => integer.value, + _ => return Err(String::from("Unsupported types")), + }; + + if index >=0 && index < left.len() as i64 { + match self.stack.push_constant( + left[index as usize].clone() + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + } else { + match self.stack.push_constant( + Object::Null + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + } + + return Ok(()) + } + + pub fn run_minus_operator(&mut self) -> Result<(), String> { + let operand = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + if operand.object_type() != object_system::ObjectType::INTEGER { + return Err(String::from("Unsupported types")); + } + + let operand = match operand { + Object::Integer(integer) => integer.value, + _ => return Err(String::from("Unsupported types")), + }; + + match self.stack.push_constant( + Object::Integer(object_system::Integer { value: -operand }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + + return Ok(()) + } + + pub fn run_bang_operator(&mut self) -> Result<(), String> { + let operand = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + match operand { + object_system::Object::Boolean(boolean) => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: !boolean.value }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + _ => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: false }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + } + return Ok(()) + } + + pub fn run_comparison(&mut self, opcode: OpCode) -> Result<(), String> { + let right = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + let left = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + match (left.object_type(), right.object_type()) { + (object_system::ObjectType::INTEGER, object_system::ObjectType::INTEGER) => { + match self.run_int_comparison(opcode, left, right) { + Ok(_) => (), + Err(e) => return Err(e), + } + } + (object_system::ObjectType::BOOLEAN, object_system::ObjectType::BOOLEAN) => { + match self.run_boolean_comparison(opcode, left, right) { + Ok(_) => (), + Err(e) => return Err(e), + } + } + _ => return Err(String::from("Mismatched/UnSupported types")), + } + + return Ok(()); + } + + pub fn run_boolean_comparison(&mut self, opcode: OpCode, left: object_system::Object, right: object_system::Object) -> Result<(), String> { + match opcode { + OpCode::OpEqual => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: left == right }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpNotEqual => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: left != right }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + _ => return Err(String::from("Unsupported types")), + } + return Ok(()) + } + + pub fn run_int_comparison(&mut self, opcode: OpCode, left: object_system::Object, right: object_system::Object) -> Result<(), String> { + let left_value = match left { + Object::Integer(integer) => integer.value, + _ => return Err(String::from("Unsupported types")), + }; + + let right_value = match right { + Object::Integer(integer) => integer.value, + _ => return Err(String::from("Unsupported types")), + }; + + _ = match opcode { + OpCode::OpEqual => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: left_value == right_value }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpNotEqual => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: left_value != right_value }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpGreaterThan => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: left_value > right_value }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + OpCode::OpLessThan => { + match self.stack.push_constant( + Object::Boolean(object_system::Boolean { value: left_value < right_value }) + ) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + _ => return Err(String::from("Unsupported types")), + }; + return Ok(()) + } + + pub fn run_binary_operation(&mut self, opcode: OpCode) -> Result<(), String> { + let right = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + let left = match self.stack.stack_pop() { + Ok(object) => object, + Err(e) => return Err(e), + }; + + match (left.object_type(), right.object_type()) { + (object_system::ObjectType::INTEGER, object_system::ObjectType::INTEGER) => { + match self.run_int_binary_operation(opcode, left, right) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + (object_system::ObjectType::StringObject , object_system::ObjectType::StringObject) => { + match self.run_string_binary_operation(opcode, left, right) { + Ok(_) => (), + Err(e) => return Err(e), + } + }, + _ => return Err(String::from("Unsupported types")), + } + + return Ok(()); + } + + pub fn run_string_binary_operation(&mut self, opcode: OpCode, left: object_system::Object, right: object_system::Object) -> Result<(), String> { + let left = match left { + Object::StringObject(string) => string.value, + _ => return Err(String::from("Unsupported types")), + }; + + let right = match right { + Object::StringObject(string) => string.value, + _ => return Err(String::from("Unsupported types")), + }; + + let result = match opcode { + OpCode::OpAdd => left + &right, + _ => return Err(String::from("Unsupported types")), + }; + + let result_object = Object::StringObject(object_system::StringObject { value: result }); + + match self.stack.push_constant(result_object) { + Ok(_) => (), + Err(e) => return Err(e), + } + + return Ok(()); + } + + pub fn run_int_binary_operation(&mut self, opcode: OpCode, left: object_system::Object, right: object_system::Object) -> Result<(), String> { + let left = match left { + Object::Integer(integer) => integer.value, + _ => return Err(String::from("Unsupported types")), + }; + + let right = match right { + Object::Integer(integer) => integer.value, + _ => return Err(String::from("Unsupported types")), + }; + + let result = match opcode { + OpCode::OpAdd => left + right, + OpCode::OpSub => left - right, + OpCode::OpMul => left * right, + OpCode::OpDiv => { + if right == 0 { + return Err(String::from("Division by zero")); + } + left / right + } + _ => return Err(String::from("Unsupported types")), + }; + + let result_object = Object::Integer(object_system::Integer { value: result }); + + match self.stack.push_constant(result_object) { + Ok(_) => (), + Err(e) => return Err(e), + } + + return Ok(()); + } +} + +#[cfg(test)] +mod tests { + use std::vec; + use std::collections::HashMap; + + use crate::virtual_machine::bytecode::format_raw_assembly; + use crate::{parser::Parser, interpreter::lexer::Lexer}; + use crate::object_system::{ + Object, + Integer, + }; + use crate::compiler::compiler::Compiler; + use crate::interpreter::ast; + + use super::*; + + fn parse(input: String) -> ast::Program { + let lexer = Lexer::new(input); + let mut parser = Parser::new(lexer); + let program = parser.parse_program(); + + program.unwrap() + } + + fn test_expected_object(expected: Object, actual: Object) -> Result<(), String> { + match expected { + Object::Integer(integer) => { + test_integer_object(integer.value, actual) + }, + Object::Boolean(boolean) => { + test_boolean_object(boolean.value, actual) + }, + Object::Null => { + test_null_object(actual) + }, + Object::StringObject(string) => { + test_string_object(string.value, actual) + }, + Object::ArrayObject(array) => { + test_array_object(array.elements, actual) + }, + Object::HashObject(hash) => { + test_hash_object(hash.pairs, actual) + }, + _ => { + return Err(format!("Wrong object type. Expected: Integer Actual: {:?}", actual)); + } + } + } + + fn test_hash_object(expected: HashMap, actual: Object) -> Result<(), String> { + match actual { + Object::HashObject(hash) => { + if hash.pairs.len() != expected.len() { + return Err(format!("Wrong hash length. Expected: {} Actual: {}", expected.len(), hash.pairs.len())); + } + + for (key, value) in expected.iter() { + let pair = match hash.pairs.get(key) { + Some(pair) => pair, + None => return Err(format!("Key not found in hash")), + }; + + test_expected_object( + value.clone(), + pair.clone() + ).unwrap(); + } + }, + _ => { + return Err(format!("Wrong object type. Expected: Hash Actual: {:?}", actual)); + } + } + + return Ok(()); + } + + fn test_array_object(expected: Vec, actual: Object) -> Result<(), String> { + match actual { + Object::ArrayObject(array) => { + if array.elements.len() != expected.len() { + return Err(format!("Wrong array length. Expected: {} Actual: {}", expected.len(), array.elements.len())); + } + + for (index, element) in array.elements.iter().enumerate() { + test_expected_object( + expected.get(index).unwrap().clone(), + element.clone() + ).unwrap(); + } + }, + _ => { + return Err(format!("Wrong object type. Expected: Array Actual: {:?}", actual)); + } + } + + return Ok(()); + } + + fn test_string_object(expected: String, actual: Object) -> Result<(), String> { + match actual { + Object::StringObject(string) => { + if string.value != expected { + return Err(format!("Wrong string value. Expected: {} Actual: {}", expected, string.value)); + } + }, + _ => { + return Err(format!("Wrong object type. Expected: String Actual: {:?}", actual)); + } + } + + return Ok(()); + } + + fn test_null_object(actual: Object) -> Result<(), String> { + match actual { + Object::Null => (), + _ => { + return Err(format!("Wrong object type. Expected: Null Actual: {:?}", actual)); + } + } + + return Ok(()); + } + + fn test_boolean_object(expected: bool, actual: Object) -> Result<(), String> { + match actual { + Object::Boolean(boolean) => { + if boolean.value != expected { + return Err(format!("Wrong boolean value. Expected: {} Actual: {}", expected, boolean.value)); + } + }, + _ => { + return Err(format!("Wrong object type. Expected: Boolean Actual: {:?}", actual)); + } + } + + return Ok(()); + } + + fn test_integer_object(expected: i64, actual: Object) -> Result<(), String> { + match actual { + Object::Integer(integer) => { + if integer.value != expected { + return Err(format!("Wrong integer value. Expected: {} Actual: {}", expected, integer.value)); + } + }, + _ => { + return Err(format!("Wrong object type. Expected: Integer Actual: {:?}", actual)); + } + } + + return Ok(()); + } + + struct VirtualMachineTest { + pub input: String, + pub expected_stack: Vec, + } + + fn run_vm_tests(tests: Vec) { + for test in tests { + let mut program = parse(test.input); + + let mut compiler = Compiler::new(); + + match compiler.compile(&mut program) { + Ok(_) => (), + Err(e) => panic!("{e}"), + } + + let raw_assembly = compiler.raw_assembly().unwrap(); + + println!("Raw Assembly : {:?}", format_raw_assembly(raw_assembly.instructions.clone())); + + let mut vm = VirtualMachine::new( + raw_assembly + ); + + match vm.run() { + Ok(_) => (), + Err(e) => panic!("{e}"), + } + + let stack_element = vm.stack.last_popped_stack_element().unwrap(); + + test_expected_object( + test.expected_stack.get(0).unwrap().clone(), + stack_element.clone() + ).unwrap(); + } + } + + #[test] + fn test_integer_arithmetic() { + let tests = vec![ + VirtualMachineTest { + input: String::from("1 + 2"), + expected_stack: vec![ + Object::Integer(Integer { value: 3 }), + ], + }, + VirtualMachineTest { + input: String::from("1"), + expected_stack: vec![ + Object::Integer(Integer { value: 1 }), + ], + }, + VirtualMachineTest { + input: String::from("2"), + expected_stack: vec![ + Object::Integer(Integer { value: 2 }), + ], + }, + VirtualMachineTest { + input: String::from("1 - 2"), + expected_stack: vec![ + Object::Integer(Integer { value: -1 }), + ], + }, + VirtualMachineTest { + input: String::from("1 * 2"), + expected_stack: vec![ + Object::Integer(Integer { value: 2 }), + ], + }, + VirtualMachineTest { + input: String::from("4 / 2"), + expected_stack: vec![ + Object::Integer(Integer { value: 2 }), + ], + }, + VirtualMachineTest { + input: String::from("50 / 2 * 2 + 10 - 5"), + expected_stack: vec![ + Object::Integer(Integer { value: 55 }), + ], + }, + VirtualMachineTest { + input: String::from("5 * (2 + 10)"), + expected_stack: vec![ + Object::Integer(Integer { value: 60 }), + ], + }, + VirtualMachineTest { + input: String::from("-5"), + expected_stack: vec![ + Object::Integer(Integer { value: -5 }), + ], + }, + VirtualMachineTest { + input: String::from("-10"), + expected_stack: vec![ + Object::Integer(Integer { value: -10 }), + ], + }, + VirtualMachineTest { + input: String::from("-50 + 100 + -50"), + expected_stack: vec![ + Object::Integer(Integer { value: 0 }), + ], + }, + VirtualMachineTest { + input: String::from("(5 + 10 * 2 + 15 / 3) * 2 + -10"), + expected_stack: vec![ + Object::Integer(Integer { value: 50 }), + ], + }, + VirtualMachineTest { + input: String::from("5 + 5 + 5 + 5 - 10"), + expected_stack: vec![ + Object::Integer(Integer { value: 10 }), + ], + }, + VirtualMachineTest { + input: String::from("2*2*2*2*2"), + expected_stack: vec![ + Object::Integer(Integer { value: 32 }), + ], + }, + VirtualMachineTest { + input: String::from("5 * 2 + 10"), + expected_stack: vec![ + Object::Integer(Integer { value: 20 }), + ], + }, + VirtualMachineTest { + input: String::from("5 + 2 * 10"), + expected_stack: vec![ + Object::Integer(Integer { value: 25 }), + ], + }, + VirtualMachineTest { + input: String::from("5 * (2 + 10)"), + expected_stack: vec![ + Object::Integer(Integer { value: 60 }), + ], + }, + ]; + run_vm_tests(tests); + } + + #[test] + fn test_boolean_expressions() { + let inputs = vec![ + VirtualMachineTest { + input: String::from("true"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("1 < 2"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("1 > 2"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("1 < 1"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("1 > 1"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("1 == 1"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("1 != 1"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("1 == 2"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("1 != 2"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("true == true"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("false == false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("true == false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("true != false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("false != true"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("(1 < 2) == true"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("(1 < 2) == false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("(1 > 2) == true"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("(1 > 2) == false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("!true"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("!false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("!5"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("!!true"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + VirtualMachineTest { + input: String::from("!!false"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: false }), + ], + }, + VirtualMachineTest { + input: String::from("!!5"), + expected_stack: vec![ + Object::Boolean(object_system::Boolean { value: true }), + ], + }, + ]; + run_vm_tests(inputs); + } + + #[test] + fn test_conditionals() { + let inputs = vec![ + VirtualMachineTest { + input: String::from("if (true) { 10 }"), + expected_stack: vec![ + Object::Integer(Integer { value: 10 }), + ], + }, + VirtualMachineTest { + input: String::from("if (true) { 10 } else { 20 }"), + expected_stack: vec![ + Object::Integer(Integer { value: 10 }), + ], + }, + VirtualMachineTest { + input: String::from("if (false) { 10 } else { 20 }"), + expected_stack: vec![ + Object::Integer(Integer { value: 20 }), + ], + }, + VirtualMachineTest { + input: String::from("if (1) { 10 }"), + expected_stack: vec![ + Object::Integer(Integer { value: 10 }), + ], + }, + VirtualMachineTest { + input: String::from("if (1 < 2) { 10 }"), + expected_stack: vec![ + Object::Integer(Integer { value: 10 }), + ], + }, + VirtualMachineTest { + input: String::from("if (1 < 2) { 10 } else { 20 }"), + expected_stack: vec![ + Object::Integer(Integer { value: 10 }), + ], + }, + VirtualMachineTest { + input: String::from("if (1 > 2) { 10 } else { 20 }"), + expected_stack: vec![ + Object::Integer(Integer { value: 20 }), + ], + }, + VirtualMachineTest { + input: String::from("if (1 > 2) { 10 }"), + expected_stack: vec![ + Object::Null, + ], + }, + VirtualMachineTest { + input: String::from("if (false) { 10 }"), + expected_stack: vec![ + Object::Null, + ], + } + ]; + run_vm_tests(inputs); + } + + #[test] + fn test_global_let_statements() { + let inputs = vec![ + VirtualMachineTest { + input: String::from("let one = 1; one;"), + expected_stack: vec![ + Object::Integer(Integer { value: 1 }), + ], + }, + VirtualMachineTest { + input: String::from("let one = 1; let two = 2; one + two;"), + expected_stack: vec![ + Object::Integer(Integer { value: 3 }), + ], + }, + VirtualMachineTest { + input: String::from("let one = 1; let two = one + one; one + two;"), + expected_stack: vec![ + Object::Integer(Integer { value: 3 }), + ], + } + ]; + run_vm_tests(inputs); + } + + #[test] + fn test_string_literals() { + let inputs = vec![ + VirtualMachineTest { + input: String::from("\"monkey\""), + expected_stack: vec![ + Object::StringObject(StringObject { value: String::from("monkey") }), + ], + }, + VirtualMachineTest { + input: String::from("\"mon\" + \"key\""), + expected_stack: vec![ + Object::StringObject(StringObject { value: String::from("monkey") }), + ], + }, + VirtualMachineTest { + input: String::from("\"mon\" + \"key\" + \"banana\""), + expected_stack: vec![ + Object::StringObject(StringObject { value: String::from("monkeybanana") }), + ], + } + ]; + run_vm_tests(inputs); + } + + #[test] + fn test_array_literals() { + let inputs = vec![ + VirtualMachineTest { + input: String::from("[]"), + expected_stack: vec![ + Object::ArrayObject(object_system::ArrayObject { elements: vec![] }), + ], + }, + VirtualMachineTest { + input: String::from("[1, 2, 3]"), + expected_stack: vec![ + Object::ArrayObject(object_system::ArrayObject { elements: vec![ + Object::Integer(Integer { value: 1 }), + Object::Integer(Integer { value: 2 }), + Object::Integer(Integer { value: 3 }), + ] }), + ], + }, + VirtualMachineTest { + input: String::from("[1 + 2, 3 - 4, 5 * 6]"), + expected_stack: vec![ + Object::ArrayObject(object_system::ArrayObject { elements: vec![ + Object::Integer(Integer { value: 3 }), + Object::Integer(Integer { value: -1 }), + Object::Integer(Integer { value: 30 }), + ] }), + ], + }, + ]; + run_vm_tests(inputs); + } + + #[test] + fn test_hash_literals() { + let inputs = vec![ + VirtualMachineTest { + input: String::from("{}"), + expected_stack: vec![ + Object::HashObject(object_system::HashObject { pairs: HashMap::new() }), + ], + }, + VirtualMachineTest { + input: String::from("{1: 2, 2: 3}"), + expected_stack: vec![ + Object::HashObject(object_system::HashObject { + pairs: { + let mut pairs = HashMap::new(); + pairs.insert(HashableObject::Integer(Integer { value: 1 }), Object::Integer(Integer { value: 2 })); + pairs.insert(HashableObject::Integer(Integer { value: 2 }), Object::Integer(Integer { value: 3 })); + pairs + } + }), + ], + }, + VirtualMachineTest { + input: String::from("{1+1:2+3, 4+5:5*6}"), + expected_stack: vec![ + Object::HashObject(object_system::HashObject { + pairs: { + let mut pairs = HashMap::new(); + pairs.insert(HashableObject::Integer(Integer { value: 2 }), Object::Integer(Integer { value: 5 })); + pairs.insert(HashableObject::Integer(Integer { value: 9 }), Object::Integer(Integer { value: 30 })); + pairs + } + }), + ], + }, + VirtualMachineTest { + input: String::from("{1+1: 2*2, 3+3: 4*4}"), + expected_stack: vec![ + Object::HashObject(object_system::HashObject { + pairs: { + let mut pairs = HashMap::new(); + pairs.insert(HashableObject::Integer(Integer { value: 2 }), Object::Integer(Integer { value: 4 })); + pairs.insert(HashableObject::Integer(Integer { value: 6 }), Object::Integer(Integer { value: 16 })); + pairs + } + }), + ], + } + ]; + run_vm_tests(inputs); + } + + #[test] + fn test_index_expressions() { + let inputs = vec![ + VirtualMachineTest { + input: String::from("[1,2,3][1]"), + expected_stack: vec![ + Object::Integer(Integer { value: 2 }), + ], + }, + VirtualMachineTest { + input: String::from("[1,2,3][0+2]"), + expected_stack: vec![ + Object::Integer(Integer { value: 3 }), + ], + }, + VirtualMachineTest { + input: String::from("[[1,1,1]][0][0]"), + expected_stack: vec![ + Object::Integer(Integer { value: 1 }), + ], + }, + VirtualMachineTest { + input: String::from("[][0]"), + expected_stack: vec![ + Object::Null, + ], + }, + VirtualMachineTest { + input: String::from("[1,2,3][99]"), + expected_stack: vec![ + Object::Null, + ], + }, + VirtualMachineTest { + input: String::from("[1][-1]"), + expected_stack: vec![ + Object::Null, + ], + }, + VirtualMachineTest { + input: String::from("{1:1, 2:2}[1]"), + expected_stack: vec![ + Object::Integer(Integer { value: 1 }), + ], + }, + VirtualMachineTest { + input: String::from("{1:1, 2:2}[2]"), + expected_stack: vec![ + Object::Integer(Integer { value: 2 }), + ], + }, + VirtualMachineTest { + input: String::from("{1:1}[0]"), + expected_stack: vec![ + Object::Null, + ], + }, + VirtualMachineTest { + input: String::from("{}[0]"), + expected_stack: vec![ + Object::Null, + ], + }, + ]; + run_vm_tests(inputs); + } +}