From 41ee60c0e2f339f0e084bd74d44ed2aa82c4b71d Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 15 Mar 2025 23:47:56 +0800 Subject: [PATCH 1/3] update with new rust version --- .gitignore | 2 + src/ast.rs | 47 ++- src/code.rs | 105 +++-- src/compiler.rs | 1016 ++++++++++++++++++++++++++++++---------------- src/evaluator.rs | 790 ++++++++++++++++++++++++----------- src/lexer.rs | 22 +- src/lib.rs | 14 +- src/main.rs | 50 +-- src/object.rs | 132 +++--- src/parser.rs | 571 +++++++++++++++++++------- src/repl.rs | 38 +- src/token.rs | 2 +- src/vm.rs | 758 +++++++++++++++++++++++----------- 13 files changed, 2396 insertions(+), 1151 deletions(-) diff --git a/.gitignore b/.gitignore index 3a68cc6..acb9461 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ Cargo.lock .idea/ notes.txt + +.vscode/ \ No newline at end of file diff --git a/src/ast.rs b/src/ast.rs index 0acc288..c9524a1 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,6 +1,6 @@ use crate::token; -use std::fmt; use std::collections::HashMap; +use std::fmt; use std::hash::{Hash, Hasher}; #[derive(Debug)] @@ -71,7 +71,7 @@ impl fmt::Display for Expression { #[derive(Eq, PartialEq, Clone, Debug)] pub struct HashLiteral { - pub pairs: HashMap, + pub pairs: HashMap, } // Had to implement Hash for this because HashMap doesn't. Doesn't matter what this is because @@ -84,7 +84,10 @@ impl Hash for HashLiteral { impl fmt::Display for HashLiteral { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let pairs: Vec = (&self.pairs).into_iter().map(|(k, v)| format!("{}:{}", k.to_string(), v.to_string())).collect(); + let pairs: Vec = (&self.pairs) + .iter() + .map(|(k, v)| format!("{}:{}", k, v)) + .collect(); write!(f, "{{{}}}", pairs.join(", ")) } } @@ -114,7 +117,7 @@ pub struct ArrayLiteral { impl fmt::Display for ArrayLiteral { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let elements: Vec = (&self.elements).into_iter().map(|e| e.to_string()).collect(); + let elements: Vec = (&self.elements).iter().map(|e| e.to_string()).collect(); write!(f, "[{}]", elements.join(", ")) } } @@ -127,7 +130,7 @@ pub struct IndexExpression { impl fmt::Display for IndexExpression { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "({}[{}])", self.left.to_string(), self.index.to_string()) + write!(f, "({}[{}])", self.left, self.index) } } @@ -153,7 +156,7 @@ pub struct FunctionLiteral { impl fmt::Display for FunctionLiteral { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let param_list: Vec = (&self.parameters).into_iter().map(|p| p.to_string()).collect(); + let param_list: Vec = (&self.parameters).iter().map(|p| p.to_string()).collect(); write!(f, "({}) {}", param_list.join(", "), self.body) } } @@ -166,8 +169,11 @@ pub struct CallExpression { impl fmt::Display for CallExpression { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let arg_list: Vec = (&self.arguments).into_iter().map(|exp| exp.to_string()).collect(); - write!(f, "{}({})", self.function.to_string(), arg_list.join(", ")) + let arg_list: Vec = (&self.arguments) + .iter() + .map(|exp| exp.to_string()) + .collect(); + write!(f, "{}({})", self.function, arg_list.join(", ")) } } @@ -243,7 +249,7 @@ impl fmt::Display for ReturnStatement { #[derive(Hash, Eq, PartialEq, Clone, Debug)] pub struct ExpressionStatement { - pub expression: Expression + pub expression: Expression, } impl fmt::Display for ExpressionStatement { @@ -257,6 +263,12 @@ pub struct Program { pub statements: Vec, } +impl Default for Program { + fn default() -> Self { + Self::new() + } +} + impl Program { pub fn new() -> Program { Program { @@ -267,7 +279,10 @@ impl Program { impl fmt::Display for Program { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let statements: Vec = (&self.statements).into_iter().map(|stmt| stmt.to_string()).collect(); + let statements: Vec = (&self.statements) + .iter() + .map(|stmt| stmt.to_string()) + .collect(); write!(f, "{}", statements.join("")) } } @@ -278,13 +293,11 @@ mod test { #[test] fn display() { - - let p = Program{ - statements: vec![ - Statement::Let(Box::new( - LetStatement{ - name: "asdf".to_string(), - value: Expression::Identifier("bar".to_string())}))], + let p = Program { + statements: vec![Statement::Let(Box::new(LetStatement { + name: "asdf".to_string(), + value: Expression::Identifier("bar".to_string()), + }))], }; let expected = "let asdf = bar;"; diff --git a/src/code.rs b/src/code.rs index a4511ab..5060ec1 100644 --- a/src/code.rs +++ b/src/code.rs @@ -1,6 +1,4 @@ -use byteorder; -use std::io::Cursor; -use self::byteorder::{ByteOrder, BigEndian, WriteBytesExt, ReadBytesExt}; +use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; pub type Instructions = Vec; @@ -15,12 +13,16 @@ impl InstructionsFns for Instructions { let mut i = 0; while i < self.len() { - let op:u8 = *self.get(i).unwrap(); + let op: u8 = *self.get(i).unwrap(); let op = unsafe { ::std::mem::transmute(op) }; - let (operands, read) = read_operands(&op, &self[i+1..]); + let (operands, read) = read_operands(&op, &self[i + 1..]); - ret.push_str(&format!("{:04} {}\n", i, Self::fmt_instruction(&op, &operands))); + ret.push_str(&format!( + "{:04} {}\n", + i, + Self::fmt_instruction(&op, &operands) + )); i = i + 1 + read; } @@ -31,13 +33,12 @@ impl InstructionsFns for Instructions { match op.operand_widths().len() { 2 => format!("{} {} {}", op.name(), operands[0], operands[1]), 1 => format!("{} {}", op.name(), operands[0]), - 0 => format!("{}", op.name()), - _ => panic!("unsuported operand width") + 0 => op.name().to_string(), + _ => panic!("unsuported operand width"), } } } - #[repr(u8)] #[derive(Eq, PartialEq, Debug, Clone)] pub enum Op { @@ -109,13 +110,29 @@ impl Op { pub fn operand_widths(&self) -> Vec { match self { - Op::Constant | Op::JumpNotTruthy | Op::Jump | - Op::SetGobal | Op::GetGlobal | Op::Array | Op::Hash => vec![2], - Op::Add | Op::Sub | Op::Mul | - Op::Div | Op::Pop | Op::True | - Op::False | Op::Equal | Op::NotEqual | - Op::GreaterThan | Op::Minus | Op::Bang | Op::Null | - Op::Index | Op::ReturnValue | Op::Return => vec![], + Op::Constant + | Op::JumpNotTruthy + | Op::Jump + | Op::SetGobal + | Op::GetGlobal + | Op::Array + | Op::Hash => vec![2], + Op::Add + | Op::Sub + | Op::Mul + | Op::Div + | Op::Pop + | Op::True + | Op::False + | Op::Equal + | Op::NotEqual + | Op::GreaterThan + | Op::Minus + | Op::Bang + | Op::Null + | Op::Index + | Op::ReturnValue + | Op::Return => vec![], Op::GetLocal | Op::SetLocal | Op::Call | Op::GetBuiltin | Op::GetFree => vec![1], Op::Closure => vec![2, 1], } @@ -127,14 +144,10 @@ pub fn make_instruction(op: Op, operands: &Vec) -> Vec { let widths = op.operand_widths(); instruction.push(op as u8); - for (o, width) in operands.into_iter().zip(widths) { + for (o, width) in operands.iter().zip(widths) { match width { - 2 => { - instruction.write_u16::(*o as u16).unwrap() - }, - 1 => { - instruction.write_u8(*o as u8).unwrap() - }, + 2 => instruction.write_u16::(*o as u16).unwrap(), + 1 => instruction.write_u8(*o as u8).unwrap(), _ => panic!("unsupported operand width {}", width), }; } @@ -149,14 +162,14 @@ pub fn read_operands(op: &Op, instructions: &[u8]) -> (Vec, usize) { for width in op.operand_widths() { match width { 2 => { - operands.push(BigEndian::read_u16(&instructions[offset..offset+2]) as usize); + operands.push(BigEndian::read_u16(&instructions[offset..offset + 2]) as usize); offset += 2; - }, + } 1 => { operands.push(instructions[offset] as usize); offset += 1; - }, - _ => panic!("width not supported for operand") + } + _ => panic!("width not supported for operand"), } } @@ -176,10 +189,26 @@ mod test { } let tests = vec![ - Test{op: Op::Constant, operands: vec![65534], expected: vec![Op::Constant as u8, 255, 254]}, - Test{op: Op::Add, operands: vec![], expected: vec![Op::Add as u8]}, - Test{op: Op::GetLocal, operands: vec![255], expected: vec![Op::GetLocal as u8, 255]}, - Test{op: Op::Call, operands: vec![255], expected: vec![Op::Call as u8, 255]}, + Test { + op: Op::Constant, + operands: vec![65534], + expected: vec![Op::Constant as u8, 255, 254], + }, + Test { + op: Op::Add, + operands: vec![], + expected: vec![Op::Add as u8], + }, + Test { + op: Op::GetLocal, + operands: vec![255], + expected: vec![Op::GetLocal as u8, 255], + }, + Test { + op: Op::Call, + operands: vec![255], + expected: vec![Op::Call as u8, 255], + }, ]; for t in tests { @@ -216,8 +245,16 @@ mod test { } let tests = vec![ - Test{op: Op::Constant, operands: vec![65535], bytes_read: 2}, - Test{op: Op::GetLocal, operands: vec![255], bytes_read: 1}, + Test { + op: Op::Constant, + operands: vec![65535], + bytes_read: 2, + }, + Test { + op: Op::GetLocal, + operands: vec![255], + bytes_read: 1, + }, ]; for t in tests { @@ -228,4 +265,4 @@ mod test { assert_eq!(operands_read, t.operands); } } -} \ No newline at end of file +} diff --git a/src/compiler.rs b/src/compiler.rs index eb78f60..fbebaf2 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,15 +1,12 @@ -use crate::object::{Object, CompiledFunction, Builtin}; -use crate::code::{Instructions, InstructionsFns, Op, make_instruction}; -use crate::parser::parse; use crate::ast; +use crate::code::{make_instruction, Instructions, Op}; +use crate::object::{Builtin, CompiledFunction, Object}; use crate::token::Token; -use std::{error, fmt}; +use enum_iterator::IntoEnumIterator; +use std::collections::HashMap; use std::fmt::Display; use std::rc::Rc; -use std::cell::RefCell; -use std::borrow::Borrow; -use std::collections::HashMap; -use enum_iterator::IntoEnumIterator; +use std::{error, fmt}; pub struct Bytecode<'a> { pub instructions: &'a Instructions, @@ -22,15 +19,6 @@ struct EmittedInstruction { position: usize, } -impl EmittedInstruction { - fn is_pop(&self) -> bool { - match self.op_code { - Op::Pop => true, - _ => false, - } - } -} - #[derive(Eq, PartialEq, Debug, Clone)] pub enum SymbolScope { Global, @@ -54,9 +42,15 @@ pub struct SymbolTable { free_symbols: Vec>, } +impl Default for SymbolTable { + fn default() -> Self { + Self::new() + } +} + impl SymbolTable { pub fn new() -> SymbolTable { - SymbolTable{ + SymbolTable { outer: None, store: HashMap::new(), num_definitions: 0, @@ -65,7 +59,7 @@ impl SymbolTable { } pub fn new_enclosed_symbol_table(outer: SymbolTable) -> SymbolTable { - SymbolTable{ + SymbolTable { outer: Some(Box::new(outer)), store: HashMap::new(), num_definitions: 0, @@ -78,7 +72,11 @@ impl SymbolTable { Some(_) => SymbolScope::Local, _ => SymbolScope::Global, }; - let symbol = Rc::new(Symbol{name: name.to_string(), scope, index: self.num_definitions}); + let symbol = Rc::new(Symbol { + name: name.to_string(), + scope, + index: self.num_definitions, + }); self.store.insert(name.to_string(), Rc::clone(&symbol)); self.num_definitions += 1; @@ -86,7 +84,11 @@ impl SymbolTable { } fn define_builtin(&mut self, name: String, index: usize) -> Rc { - let symbol = Rc::new(Symbol{name: name.clone(), scope: SymbolScope::Builtin, index: index}); + let symbol = Rc::new(Symbol { + name: name.clone(), + scope: SymbolScope::Builtin, + index, + }); self.store.insert(name, Rc::clone(&symbol)); symbol } @@ -95,13 +97,12 @@ impl SymbolTable { for builtin in Builtin::into_enum_iter() { self.define_builtin(builtin.string(), builtin as usize); } - } fn define_free(&mut self, original: &Rc) -> Rc { self.free_symbols.push(Rc::clone(original)); - let symbol = Rc::new(Symbol{ + let symbol = Rc::new(Symbol { name: original.name.clone(), scope: SymbolScope::Free, index: self.free_symbols.len() - 1, @@ -114,21 +115,17 @@ impl SymbolTable { fn resolve(&mut self, name: &str) -> Option> { // TODO: clean this mess up match self.store.get(name) { - Some(sym) => Some(Rc::clone(&sym)), + Some(sym) => Some(Rc::clone(sym)), None => match &mut self.outer { - Some(outer) => { - match outer.resolve(name) { - Some(sym) => { - match sym.scope { - SymbolScope::Global | SymbolScope::Builtin => Some(Rc::clone(&sym)), - SymbolScope::Local | SymbolScope::Free => { - let free = self.define_free(&sym); - Some(free) - } - } - }, - None => None, - } + Some(outer) => match outer.resolve(name) { + Some(sym) => match sym.scope { + SymbolScope::Global | SymbolScope::Builtin => Some(Rc::clone(&sym)), + SymbolScope::Local | SymbolScope::Free => { + let free = self.define_free(&sym); + Some(free) + } + }, + None => None, }, None => None, }, @@ -150,35 +147,43 @@ pub struct Compiler { scope_index: usize, } +impl Default for Compiler { + fn default() -> Self { + Self::new() + } +} + impl Compiler { pub fn new() -> Compiler { let mut symbol_table = SymbolTable::new(); symbol_table.load_builtins(); - Compiler{ + Compiler { constants: vec![], symbol_table, - scopes: vec![CompilationScope{ + scopes: vec![CompilationScope { instructions: vec![], last_instruction: None, - previous_instruction: None}], + previous_instruction: None, + }], scope_index: 0, } } pub fn new_with_state(symbol_table: SymbolTable, constants: Vec>) -> Compiler { - Compiler{ + Compiler { constants, symbol_table, - scopes: vec![CompilationScope{ + scopes: vec![CompilationScope { instructions: vec![], last_instruction: None, - previous_instruction: None}], + previous_instruction: None, + }], scope_index: 0, } } - pub fn compile(&mut self, node: ast::Node) -> Result { + pub fn compile(&mut self, node: ast::Node) -> CompileResult { match node { ast::Node::Program(prog) => self.compile_program(&prog)?, ast::Node::Statement(stmt) => self.compile_statement(&stmt)?, @@ -193,37 +198,39 @@ impl Compiler { } pub fn bytecode(&self) -> Bytecode { - Bytecode{ + Bytecode { instructions: &self.scopes[self.scope_index].instructions, constants: &self.constants, } } fn emit(&mut self, op: Op, operands: &Vec) -> usize { - let mut ins = make_instruction(op.clone(), &operands); - let pos= self.add_instruction(&mut ins); + let mut ins = make_instruction(op.clone(), operands); + let pos = self.add_instruction(&mut ins); self.set_last_instruction(op, pos); - return pos; + pos } fn set_last_instruction(&mut self, op_code: Op, position: usize) { - match &self.scopes[self.scope_index].last_instruction { - Some(ins) => self.scopes[self.scope_index].previous_instruction = Some(ins.clone()), - _ => (), + if let Some(ins) = &self.scopes[self.scope_index].last_instruction { + self.scopes[self.scope_index].previous_instruction = Some(ins.clone()) } - self.scopes[self.scope_index].last_instruction = Some(EmittedInstruction{op_code, position}); + self.scopes[self.scope_index].last_instruction = + Some(EmittedInstruction { op_code, position }); } fn add_instruction(&mut self, ins: &Vec) -> usize { let pos = self.scopes[self.scope_index].instructions.len(); - self.scopes[self.scope_index].instructions.extend_from_slice(ins); - return pos; + self.scopes[self.scope_index] + .instructions + .extend_from_slice(ins); + pos } fn add_constant(&mut self, obj: Object) -> usize { self.constants.push(Rc::new(obj)); - return self.constants.len() - 1; + self.constants.len() - 1 } fn compile_program(&mut self, prog: &ast::Program) -> ::std::result::Result<(), CompileError> { @@ -234,17 +241,20 @@ impl Compiler { Ok(()) } - fn compile_statement(&mut self, stmt: &ast::Statement) -> ::std::result::Result<(), CompileError> { + fn compile_statement( + &mut self, + stmt: &ast::Statement, + ) -> ::std::result::Result<(), CompileError> { match stmt { ast::Statement::Expression(exp) => { self.compile_expression(&exp.expression)?; // expressions put their value on the stack so this should be popped off since it doesn't get reused self.emit(Op::Pop, &vec![]); - }, + } ast::Statement::Return(ret) => { - self.compile_expression(&ret.value); + self.compile_expression(&ret.value)?; self.emit(Op::ReturnValue, &vec![]); - }, + } ast::Statement::Let(stmt) => { let symbol = self.symbol_table.define(&stmt.name); self.compile_expression(&stmt.value)?; @@ -252,15 +262,22 @@ impl Compiler { match &symbol.scope { SymbolScope::Global => self.emit(Op::SetGobal, &vec![symbol.index]), SymbolScope::Local => self.emit(Op::SetLocal, &vec![symbol.index]), - SymbolScope::Builtin => return Err(CompileError{message: "can't assign to builtin function name".to_string()}), + SymbolScope::Builtin => { + return Err(CompileError { + message: "can't assign to builtin function name".to_string(), + }) + } SymbolScope::Free => panic!("free not here"), }; - }, + } } Ok(()) } - fn compile_block_statement(&mut self, stmt: &ast::BlockStatement) -> ::std::result::Result<(), CompileError> { + fn compile_block_statement( + &mut self, + stmt: &ast::BlockStatement, + ) -> ::std::result::Result<(), CompileError> { for stmt in &stmt.statements { self.compile_statement(stmt)?; } @@ -268,34 +285,37 @@ impl Compiler { Ok(()) } - fn compile_expression(&mut self, exp: &ast::Expression) -> ::std::result::Result<(), CompileError> { + fn compile_expression( + &mut self, + exp: &ast::Expression, + ) -> ::std::result::Result<(), CompileError> { match exp { ast::Expression::Integer(int) => { let int = Object::Int(*int); let operands = vec![self.add_constant(int)]; self.emit(Op::Constant, &operands); - }, + } ast::Expression::Boolean(b) => { if *b { self.emit(Op::True, &vec![]); } else { self.emit(Op::False, &vec![]); } - }, + } ast::Expression::String(s) => { let operands = vec![self.add_constant(Object::String(s.to_string()))]; self.emit(Op::Constant, &operands); - }, + } ast::Expression::Infix(exp) => { if exp.operator == Token::Lt { - self.compile_expression(&exp.right); - self.compile_expression(&exp.left); + self.compile_expression(&exp.right)?; + self.compile_expression(&exp.left)?; self.emit(Op::GreaterThan, &vec![]); return Ok(()); } - self.compile_expression(&exp.left); - self.compile_expression(&exp.right); + self.compile_expression(&exp.left)?; + self.compile_expression(&exp.right)?; match exp.operator { Token::Plus => self.emit(Op::Add, &vec![]), @@ -305,24 +325,32 @@ impl Compiler { Token::Gt => self.emit(Op::GreaterThan, &vec![]), Token::Eq => self.emit(Op::Equal, &vec![]), Token::Neq => self.emit(Op::NotEqual, &vec![]), - _ => return Err(CompileError{message: format!("unknown operator {:?}", exp.operator)}), + _ => { + return Err(CompileError { + message: format!("unknown operator {:?}", exp.operator), + }) + } }; - }, + } ast::Expression::Prefix(exp) => { - self.compile_expression(&exp.right); + self.compile_expression(&exp.right)?; match exp.operator { Token::Minus => self.emit(Op::Minus, &vec![]), Token::Bang => self.emit(Op::Bang, &vec![]), - _ => return Err(CompileError{message: format!("unknown operator {:?}", exp.operator)}), + _ => { + return Err(CompileError { + message: format!("unknown operator {:?}", exp.operator), + }) + } }; - }, + } ast::Expression::If(ifexp) => { - self.compile_expression(&ifexp.condition); + self.compile_expression(&ifexp.condition)?; let jump_not_truthy_pos = self.emit(Op::JumpNotTruthy, &vec![9999]); - self.compile_block_statement(&ifexp.consequence); + self.compile_block_statement(&ifexp.consequence)?; if self.last_instruction_is(Op::Pop) { self.remove_last_instruction(); @@ -344,36 +372,34 @@ impl Compiler { let after_alternative_pos = self.scopes[self.scope_index].instructions.len(); self.change_operand(jump_pos, after_alternative_pos); - }, - ast::Expression::Identifier(name) => { - match self.symbol_table.resolve(name) { - Some(sym) => { - self.load_symbol(&sym); - }, - _ => panic!("symbol not resolved {:?}", name) + } + ast::Expression::Identifier(name) => match self.symbol_table.resolve(name) { + Some(sym) => { + self.load_symbol(&sym); } + _ => panic!("symbol not resolved {:?}", name), }, ast::Expression::Array(array) => { for el in &array.elements { self.compile_expression(el)?; } self.emit(Op::Array, &vec![array.elements.len()]); - }, + } ast::Expression::Hash(hash) => { - let mut keys: Vec<&ast::Expression> = hash.pairs.keys().into_iter().collect(); - keys.sort_by(|a, b| (*a).string().cmp(&(*b).string())); + let mut keys: Vec<&ast::Expression> = hash.pairs.keys().collect(); + keys.sort_by_key(|a| (*a).string()); for k in &keys { - self.compile_expression(*k)?; + self.compile_expression(k)?; self.compile_expression(hash.pairs.get(*k).unwrap())?; - }; + } self.emit(Op::Hash, &vec![keys.len() * 2]); - }, + } ast::Expression::Index(idx) => { self.compile_expression(&idx.left)?; self.compile_expression(&idx.index)?; self.emit(Op::Index, &vec![]); - }, + } ast::Expression::Function(func) => { self.enter_scope(); @@ -381,7 +407,7 @@ impl Compiler { self.symbol_table.define(&arg.name); } - self.compile_block_statement(&func.body); + self.compile_block_statement(&func.body)?; if self.last_instruction_is(Op::Pop) { self.replace_last_pop_with_return(); @@ -398,20 +424,23 @@ impl Compiler { self.load_symbol(sym); } - let compiled_func = Object::CompiledFunction(Rc::new(CompiledFunction{instructions, num_locals, num_parameters: func.parameters.len()})); - let func_index= self.add_constant(compiled_func); + let compiled_func = Object::CompiledFunction(Rc::new(CompiledFunction { + instructions, + num_locals, + num_parameters: func.parameters.len(), + })); + let func_index = self.add_constant(compiled_func); self.emit(Op::Closure, &vec![func_index, free_symbols.len()]); - }, + } ast::Expression::Call(exp) => { - self.compile_expression(&exp.function); + self.compile_expression(&exp.function)?; for arg in &exp.arguments { - self.compile_expression(arg); + self.compile_expression(arg)?; } self.emit(Op::Call, &vec![exp.arguments.len()]); - }, - _ => panic!("not implemented {:?}", exp) + } } Ok(()) @@ -448,7 +477,7 @@ impl Compiler { } fn enter_scope(&mut self) { - let scope = CompilationScope{ + let scope = CompilationScope { instructions: vec![], last_instruction: None, previous_instruction: None, @@ -471,7 +500,7 @@ impl Compiler { } fn remove_last_instruction(&mut self) { - let ref mut scope = self.scopes[self.scope_index]; + let scope = &mut self.scopes[self.scope_index]; let pos = match &scope.last_instruction { Some(ins) => ins.position, _ => 0, @@ -483,7 +512,7 @@ impl Compiler { fn replace_instruction(&mut self, pos: usize, ins: &[u8]) { let mut i = 0; - let ref mut scope = self.scopes[self.scope_index]; + let scope = &mut self.scopes[self.scope_index]; while i < ins.len() { scope.instructions[pos + i] = ins[i]; i += 1; @@ -497,15 +526,17 @@ impl Compiler { } } -type Result<'a> = ::std::result::Result, CompileError>; +type CompileResult<'a> = Result, CompileError>; #[derive(Debug)] pub struct CompileError { - pub message: String, + pub message: String, } impl error::Error for CompileError { - fn description(&self) -> &str { &self.message } + fn description(&self) -> &str { + &self.message + } } impl Display for CompileError { @@ -517,6 +548,8 @@ impl Display for CompileError { #[cfg(test)] mod test { use super::*; + use crate::code::InstructionsFns; + use crate::parser::parse; struct CompilerTestCase<'a> { input: &'a str, @@ -527,8 +560,8 @@ mod test { #[test] fn integer_arithmetic() { let tests = vec![ - CompilerTestCase{ - input:"1 + 2", + CompilerTestCase { + input: "1 + 2", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), @@ -537,8 +570,8 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ - input:"1; 2", + CompilerTestCase { + input: "1; 2", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), @@ -547,7 +580,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "1 - 2", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ @@ -557,7 +590,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "1 * 2", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ @@ -567,7 +600,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "2 / 1", expected_constants: vec![Object::Int(2), Object::Int(1)], expected_instructions: vec![ @@ -577,7 +610,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "-1", expected_constants: vec![Object::Int(1)], expected_instructions: vec![ @@ -594,7 +627,7 @@ mod test { #[test] fn boolean_expressions() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "true", expected_constants: vec![], expected_instructions: vec![ @@ -602,7 +635,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "false", expected_constants: vec![], expected_instructions: vec![ @@ -610,7 +643,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "1 > 2", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ @@ -620,7 +653,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "1 < 2", expected_constants: vec![Object::Int(2), Object::Int(1)], expected_instructions: vec![ @@ -630,7 +663,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "1 == 2", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ @@ -640,7 +673,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "1 != 2", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ @@ -650,7 +683,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "true == false", expected_constants: vec![], expected_instructions: vec![ @@ -660,7 +693,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "true != false", expected_constants: vec![], expected_instructions: vec![ @@ -670,7 +703,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "!true", expected_constants: vec![], expected_instructions: vec![ @@ -687,7 +720,7 @@ mod test { #[test] fn conditionals() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "if (true) { 10 }; 3333;", expected_constants: vec![Object::Int(10), Object::Int(3333)], expected_instructions: vec![ @@ -709,7 +742,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "if (true) { 10 } else { 20 }; 3333;", expected_constants: vec![Object::Int(10), Object::Int(20), Object::Int(3333)], expected_instructions: vec![ @@ -739,7 +772,7 @@ mod test { #[test] fn test_global_let_statements() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "let one = 1; let two = 2;", expected_constants: vec![Object::Int(1), Object::Int(2)], expected_instructions: vec![ @@ -749,7 +782,7 @@ mod test { make_instruction(Op::SetGobal, &vec![1]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "let one = 1; one;", expected_constants: vec![Object::Int(1)], expected_instructions: vec![ @@ -759,7 +792,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "let one = 1; let two = one; two;", expected_constants: vec![Object::Int(1)], expected_instructions: vec![ @@ -780,13 +813,21 @@ mod test { fn define() { let mut table = SymbolTable::new(); let a = table.define("a"); - let exp_a = Rc::new(Symbol{name: "a".to_string(), scope: SymbolScope::Global, index: 0}); + let exp_a = Rc::new(Symbol { + name: "a".to_string(), + scope: SymbolScope::Global, + index: 0, + }); if a != exp_a { panic!("exp: {:?}\ngot: {:?}", exp_a, a); } let b = table.define("b"); - let exp_b = Rc::new(Symbol{name: "b".to_string(), scope: SymbolScope::Global, index: 1}); + let exp_b = Rc::new(Symbol { + name: "b".to_string(), + scope: SymbolScope::Global, + index: 1, + }); if b != exp_b { panic!("exp: {:?}\ngot: {:?}", exp_b, b); } @@ -798,15 +839,23 @@ mod test { global.define("a"); global.define("b"); - let exp_a = Rc::new(Symbol{name: "a".to_string(), scope: SymbolScope::Global, index: 0}); - let exp_b = Rc::new(Symbol{name: "b".to_string(), scope: SymbolScope::Global, index: 1}); + let exp_a = Rc::new(Symbol { + name: "a".to_string(), + scope: SymbolScope::Global, + index: 0, + }); + let exp_b = Rc::new(Symbol { + name: "b".to_string(), + scope: SymbolScope::Global, + index: 1, + }); match global.resolve("a") { Some(sym) => { if sym != exp_a { panic!("a not equal: exp: {:?} got: {:?}", exp_a, sym); } - }, + } _ => panic!("a didn't resovle"), } @@ -815,7 +864,7 @@ mod test { if sym != exp_b { panic!("b not equal: exp: {:?} got: {:?}", exp_b, sym); } - }, + } _ => panic!("b didn't resolve"), } } @@ -823,7 +872,7 @@ mod test { #[test] fn string_expressions() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "\"monkey\"", expected_constants: vec![Object::String("monkey".to_string())], expected_instructions: vec![ @@ -831,9 +880,12 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "\"mon\" + \"key\"", - expected_constants: vec![Object::String("mon".to_string()), Object::String("key".to_string())], + expected_constants: vec![ + Object::String("mon".to_string()), + Object::String("key".to_string()), + ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), make_instruction(Op::Constant, &vec![1]), @@ -849,7 +901,7 @@ mod test { #[test] fn array_literals() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "[]", expected_constants: vec![], expected_instructions: vec![ @@ -857,7 +909,7 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "[1, 2, 3]", expected_constants: vec![Object::Int(1), Object::Int(2), Object::Int(3)], expected_instructions: vec![ @@ -868,9 +920,16 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "[1 + 2, 3 - 4, 5 * 6]", - expected_constants: vec![Object::Int(1), Object::Int(2), Object::Int(3), Object::Int(4), Object::Int(5), Object::Int(6)], + expected_constants: vec![ + Object::Int(1), + Object::Int(2), + Object::Int(3), + Object::Int(4), + Object::Int(5), + Object::Int(6), + ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), make_instruction(Op::Constant, &vec![1]), @@ -893,7 +952,7 @@ mod test { #[test] fn hash_literals() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "{}", expected_constants: vec![], expected_instructions: vec![ @@ -901,9 +960,16 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "{1: 2, 3: 4, 5: 6}", - expected_constants: vec![Object::Int(1), Object::Int(2), Object::Int(3), Object::Int(4), Object::Int(5), Object::Int(6)], + expected_constants: vec![ + Object::Int(1), + Object::Int(2), + Object::Int(3), + Object::Int(4), + Object::Int(5), + Object::Int(6), + ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), make_instruction(Op::Constant, &vec![1]), @@ -915,9 +981,16 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "{1: 2 + 3, 4: 5 * 6}", - expected_constants: vec![Object::Int(1), Object::Int(2), Object::Int(3), Object::Int(4), Object::Int(5), Object::Int(6)], + expected_constants: vec![ + Object::Int(1), + Object::Int(2), + Object::Int(3), + Object::Int(4), + Object::Int(5), + Object::Int(6), + ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), make_instruction(Op::Constant, &vec![1]), @@ -939,9 +1012,15 @@ mod test { #[test] fn index_expressions() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "[1, 2, 3][1 + 1]", - expected_constants: vec![Object::Int(1), Object::Int(2), Object::Int(3), Object::Int(1), Object::Int(1)], + expected_constants: vec![ + Object::Int(1), + Object::Int(2), + Object::Int(3), + Object::Int(1), + Object::Int(1), + ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), make_instruction(Op::Constant, &vec![1]), @@ -954,9 +1033,14 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "{1: 2}[2 - 1]", - expected_constants: vec![Object::Int(1), Object::Int(2), Object::Int(2), Object::Int(1)], + expected_constants: vec![ + Object::Int(1), + Object::Int(2), + Object::Int(2), + Object::Int(1), + ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), make_instruction(Op::Constant, &vec![1]), @@ -975,25 +1059,27 @@ mod test { #[test] fn functions() { - let tests = vec![ - CompilerTestCase{ - input: "fn() { return 5 + 10 }", - expected_constants: vec![ - Object::Int(5), - Object::Int(10), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ + let tests = vec![CompilerTestCase { + input: "fn() { return 5 + 10 }", + expected_constants: vec![ + Object::Int(5), + Object::Int(10), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ make_instruction(Op::Constant, &vec![0]), make_instruction(Op::Constant, &vec![1]), make_instruction(Op::Add, &vec![]), - make_instruction(Op::ReturnValue, &vec![]) - ]), num_locals: 0, num_parameters: 0})), - ], - expected_instructions: vec![ - make_instruction(Op::Closure, &vec![2, 0]), - make_instruction(Op::Pop, &vec![]), - ], - }, - ]; + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 0, + num_parameters: 0, + })), + ], + expected_instructions: vec![ + make_instruction(Op::Closure, &vec![2, 0]), + make_instruction(Op::Pop, &vec![]), + ], + }]; run_compiler_tests(tests); } @@ -1001,14 +1087,18 @@ mod test { #[test] fn function_calls() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "fn() { 24 }();", expected_constants: vec![ Object::Int(24), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::Constant, &vec![0]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 0, num_parameters: 0})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::Constant, &vec![0]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 0, + num_parameters: 0, + })), ], expected_instructions: vec![ make_instruction(Op::Closure, &vec![1, 0]), @@ -1016,14 +1106,18 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "let noArg = fn() { 24 }; noArg();", expected_constants: vec![ Object::Int(24), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::Constant, &vec![0]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 0, num_parameters: 0})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::Constant, &vec![0]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 0, + num_parameters: 0, + })), ], expected_instructions: vec![ make_instruction(Op::Closure, &vec![1, 0]), @@ -1033,13 +1127,17 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "let oneArg = fn(a) { a }; oneArg(24);", expected_constants: vec![ - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 1})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 1, + })), Object::Int(24), ], expected_instructions: vec![ @@ -1051,17 +1149,21 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "let manyArg = fn(a, b, c) { a; b; c }; manyArg(24, 25, 26);", expected_constants: vec![ - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::Pop, &vec![]), - make_instruction(Op::GetLocal, &vec![1]), - make_instruction(Op::Pop, &vec![]), - make_instruction(Op::GetLocal, &vec![2]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 3, num_parameters: 3})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::Pop, &vec![]), + make_instruction(Op::GetLocal, &vec![1]), + make_instruction(Op::Pop, &vec![]), + make_instruction(Op::GetLocal, &vec![2]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 3, + num_parameters: 3, + })), Object::Int(24), Object::Int(25), Object::Int(26), @@ -1086,7 +1188,10 @@ mod test { fn compiler_scopes() { let mut compiler = Compiler::new(); if compiler.scope_index != 0 { - panic!("scope_index wrong, exp: {}, got: {}", 0, compiler.scope_index); + panic!( + "scope_index wrong, exp: {}, got: {}", + 0, compiler.scope_index + ); } compiler.emit(Op::Mul, &vec![]); @@ -1095,7 +1200,10 @@ mod test { compiler.enter_scope(); if compiler.scope_index != 1 { - panic!("scope_index wrong, exp: {}, got: {}", 0, compiler.scope_index); + panic!( + "scope_index wrong, exp: {}, got: {}", + 0, compiler.scope_index + ); } compiler.emit(Op::Sub, &vec![]); @@ -1106,11 +1214,9 @@ mod test { } match &compiler.scopes[compiler.scope_index].last_instruction { - Some(ins) => { - match ins.op_code { - Op::Sub => (), - _ => panic!("wrong op code {:?}", ins.op_code), - } + Some(ins) => match ins.op_code { + Op::Sub => (), + _ => panic!("wrong op code {:?}", ins.op_code), }, None => panic!("last instruction not in scope"), } @@ -1120,7 +1226,7 @@ mod test { if outer.as_ref() != &global_symbol_table { panic!("compiler did not enclose symbol table"); } - }, + } None => panic!("compiler did not enclose symbol table"), } @@ -1145,21 +1251,17 @@ mod test { } match &compiler.scopes[compiler.scope_index].last_instruction { - Some(ins) => { - match ins.op_code { - Op::Add => (), - _ => panic!("wrong op code {:?}", ins.op_code), - } + Some(ins) => match ins.op_code { + Op::Add => (), + _ => panic!("wrong op code {:?}", ins.op_code), }, None => panic!("last instruction not in scope"), } match &compiler.scopes[compiler.scope_index].previous_instruction { - Some(ins) => { - match ins.op_code { - Op::Mul => (), - _ => panic!("wrong op code {:?}", ins.op_code), - } + Some(ins) => match ins.op_code { + Op::Mul => (), + _ => panic!("wrong op code {:?}", ins.op_code), }, None => panic!("previous instruction not in scope"), } @@ -1168,14 +1270,18 @@ mod test { #[test] fn let_statement_scopes() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "let num = 55; fn() { num }", expected_constants: vec![ Object::Int(55), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::GetGlobal, &vec![0]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 0, num_parameters: 0})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::GetGlobal, &vec![0]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 0, + num_parameters: 0, + })), ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), @@ -1184,37 +1290,45 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "fn() { let num = 55; num }", expected_constants: vec![ Object::Int(55), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::Constant, &vec![0]), - make_instruction(Op::SetLocal, &vec![0]), - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 0})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::Constant, &vec![0]), + make_instruction(Op::SetLocal, &vec![0]), + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 0, + })), ], expected_instructions: vec![ make_instruction(Op::Closure, &vec![1, 0]), make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "fn() { let a = 55; let b = 77; a + b}", expected_constants: vec![ Object::Int(55), Object::Int(77), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::Constant, &vec![0]), - make_instruction(Op::SetLocal, &vec![0]), - make_instruction(Op::Constant, &vec![1]), - make_instruction(Op::SetLocal, &vec![1]), - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::GetLocal, &vec![1]), - make_instruction(Op::Add, &vec![]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 2, num_parameters: 0})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::Constant, &vec![0]), + make_instruction(Op::SetLocal, &vec![0]), + make_instruction(Op::Constant, &vec![1]), + make_instruction(Op::SetLocal, &vec![1]), + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::GetLocal, &vec![1]), + make_instruction(Op::Add, &vec![]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 2, + num_parameters: 0, + })), ], expected_instructions: vec![ make_instruction(Op::Closure, &vec![2, 0]), @@ -1247,12 +1361,18 @@ mod test { match local.resolve(name) { Some(symbol) => { if symbol.scope != scope { - panic!("expected scope {:?} on symbol {:?} but got {:?}", scope, name, symbol.scope); + panic!( + "expected scope {:?} on symbol {:?} but got {:?}", + scope, name, symbol.scope + ); } if symbol.index != index { - panic!("expected index {} on symbol {:?} but got {}", index, symbol, symbol.index); + panic!( + "expected index {} on symbol {:?} but got {}", + index, symbol, symbol.index + ); } - }, + } _ => panic!("couldn't resolve symbol: {}", name), } } @@ -1271,18 +1391,24 @@ mod test { nested_local.define("f"); let mut tests = vec![ - (local, vec![ - ("a", SymbolScope::Global, 0), - ("b", SymbolScope::Global, 1), - ("c", SymbolScope::Local, 0), - ("d", SymbolScope::Local, 1), - ]), - (nested_local, vec![ - ("a", SymbolScope::Global, 0), - ("b", SymbolScope::Global, 1), - ("e", SymbolScope::Local, 0), - ("f", SymbolScope::Local, 1), - ]), + ( + local, + vec![ + ("a", SymbolScope::Global, 0), + ("b", SymbolScope::Global, 1), + ("c", SymbolScope::Local, 0), + ("d", SymbolScope::Local, 1), + ], + ), + ( + nested_local, + vec![ + ("a", SymbolScope::Global, 0), + ("b", SymbolScope::Global, 1), + ("e", SymbolScope::Local, 0), + ("f", SymbolScope::Local, 1), + ], + ), ]; for (table, expected) in &mut tests { @@ -1290,12 +1416,18 @@ mod test { match table.resolve(name) { Some(symbol) => { if symbol.scope != *scope { - panic!("expected scope {:?} on symbol {:?} but got {:?}", scope, name, symbol.scope); + panic!( + "expected scope {:?} on symbol {:?} but got {:?}", + scope, name, symbol.scope + ); } if symbol.index != *index { - panic!("expected index {} on symbol {:?} but got {}", index, symbol, symbol.index); + panic!( + "expected index {} on symbol {:?} but got {}", + index, symbol, symbol.index + ); } - }, + } _ => panic!("couldn't resolve symbol: {}", name), } } @@ -1305,7 +1437,7 @@ mod test { #[test] fn builtins() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: "len([]); push([], 1);", expected_constants: vec![Object::Int(1)], expected_instructions: vec![ @@ -1320,21 +1452,23 @@ mod test { make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: "fn() { len([]) }", - expected_constants: vec![ - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ + expected_constants: vec![Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ make_instruction(Op::GetBuiltin, &vec![0]), make_instruction(Op::Array, &vec![0]), make_instruction(Op::Call, &vec![1]), make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 0, num_parameters: 0})), - ], + ]), + num_locals: 0, + num_parameters: 0, + }))], expected_instructions: vec![ make_instruction(Op::Closure, &vec![0, 0]), make_instruction(Op::Pop, &vec![]), ], - } + }, ]; run_compiler_tests(tests); @@ -1344,10 +1478,26 @@ mod test { fn define_resolve_builtins() { let mut global = SymbolTable::new(); let expected = vec![ - Symbol{name: "a".to_string(), scope: SymbolScope::Builtin, index: 0}, - Symbol{name: "c".to_string(), scope: SymbolScope::Builtin, index: 1}, - Symbol{name: "e".to_string(), scope: SymbolScope::Builtin, index: 2}, - Symbol{name: "f".to_string(), scope: SymbolScope::Builtin, index: 3}, + Symbol { + name: "a".to_string(), + scope: SymbolScope::Builtin, + index: 0, + }, + Symbol { + name: "c".to_string(), + scope: SymbolScope::Builtin, + index: 1, + }, + Symbol { + name: "e".to_string(), + scope: SymbolScope::Builtin, + index: 2, + }, + Symbol { + name: "f".to_string(), + scope: SymbolScope::Builtin, + index: 3, + }, ]; for sym in &expected { @@ -1360,9 +1510,11 @@ mod test { for mut table in vec![global, first_local, second_local] { for sym in &expected { match table.resolve(&sym.name) { - Some(s) => if s != Rc::new(sym.clone()) { - panic!("exp: {:?}, got: {:?}", sym, s); - }, + Some(s) => { + if s != Rc::new(sym.clone()) { + panic!("exp: {:?}, got: {:?}", sym, s); + } + } None => panic!("couldn't resolve symbol {}", sym.name), } } @@ -1372,27 +1524,29 @@ mod test { #[test] fn closures() { let tests = vec![ - CompilerTestCase{ + CompilerTestCase { input: " fn(a) { fn(b) { a + b } }", - expected_constants: vec![ - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ + expected_constants: vec![Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ make_instruction(Op::GetFree, &vec![0]), make_instruction(Op::GetLocal, &vec![0]), make_instruction(Op::Add, &vec![]), make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 1})), - ], + ]), + num_locals: 1, + num_parameters: 1, + }))], expected_instructions: vec![ make_instruction(Op::Closure, &vec![1, 0]), make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: " fn(a) { fn(b) { @@ -1402,32 +1556,44 @@ mod test { } };", expected_constants: vec![ - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::GetFree, &vec![0]), - make_instruction(Op::GetFree, &vec![1]), - make_instruction(Op::Add, &vec![]), - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::Add, &vec![]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 1})), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::GetFree, &vec![0]), - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::Closure, &vec![0, 2]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 1})), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::Closure, &vec![1, 1]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 1})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::GetFree, &vec![0]), + make_instruction(Op::GetFree, &vec![1]), + make_instruction(Op::Add, &vec![]), + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::Add, &vec![]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 1, + })), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::GetFree, &vec![0]), + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::Closure, &vec![0, 2]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 1, + })), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::Closure, &vec![1, 1]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 1, + })), ], expected_instructions: vec![ make_instruction(Op::Closure, &vec![2, 0]), make_instruction(Op::Pop, &vec![]), ], }, - CompilerTestCase{ + CompilerTestCase { input: " let global = 55; @@ -1449,33 +1615,45 @@ mod test { Object::Int(66), Object::Int(77), Object::Int(88), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::Constant, &vec![3]), - make_instruction(Op::SetLocal, &vec![0]), - make_instruction(Op::GetGlobal, &vec![0]), - make_instruction(Op::GetFree, &vec![0]), - make_instruction(Op::Add, &vec![]), - make_instruction(Op::GetFree, &vec![1]), - make_instruction(Op::Add, &vec![]), - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::Add, &vec![]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 0})), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::Constant, &vec![2]), - make_instruction(Op::SetLocal, &vec![0]), - make_instruction(Op::GetFree, &vec![0]), - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::Closure, &vec![4, 2]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 0})), - Object::CompiledFunction(Rc::new(CompiledFunction{instructions: concat_instructions(&vec![ - make_instruction(Op::Constant, &vec![1]), - make_instruction(Op::SetLocal, &vec![0]), - make_instruction(Op::GetLocal, &vec![0]), - make_instruction(Op::Closure, &vec![5, 1]), - make_instruction(Op::ReturnValue, &vec![]), - ]), num_locals: 1, num_parameters: 0})), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::Constant, &vec![3]), + make_instruction(Op::SetLocal, &vec![0]), + make_instruction(Op::GetGlobal, &vec![0]), + make_instruction(Op::GetFree, &vec![0]), + make_instruction(Op::Add, &vec![]), + make_instruction(Op::GetFree, &vec![1]), + make_instruction(Op::Add, &vec![]), + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::Add, &vec![]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 0, + })), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::Constant, &vec![2]), + make_instruction(Op::SetLocal, &vec![0]), + make_instruction(Op::GetFree, &vec![0]), + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::Closure, &vec![4, 2]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 0, + })), + Object::CompiledFunction(Rc::new(CompiledFunction { + instructions: concat_instructions(&vec![ + make_instruction(Op::Constant, &vec![1]), + make_instruction(Op::SetLocal, &vec![0]), + make_instruction(Op::GetLocal, &vec![0]), + make_instruction(Op::Closure, &vec![5, 1]), + make_instruction(Op::ReturnValue, &vec![]), + ]), + num_locals: 1, + num_parameters: 0, + })), ], expected_instructions: vec![ make_instruction(Op::Constant, &vec![0]), @@ -1504,23 +1682,79 @@ mod test { second_local.define("f"); let mut tests = vec![ - (first_local, vec![ - Symbol{name: "a".to_string(), scope: SymbolScope::Global, index: 0}, - Symbol{name: "b".to_string(), scope: SymbolScope::Global, index: 1}, - Symbol{name: "c".to_string(), scope: SymbolScope::Local, index: 0}, - Symbol{name: "d".to_string(), scope: SymbolScope::Local, index: 1}, - ], vec![]), - (second_local, vec![ - Symbol{name: "a".to_string(), scope: SymbolScope::Global, index: 0}, - Symbol{name: "b".to_string(), scope: SymbolScope::Global, index: 1}, - Symbol{name: "c".to_string(), scope: SymbolScope::Free, index: 0}, - Symbol{name: "d".to_string(), scope: SymbolScope::Free, index: 1}, - Symbol{name: "e".to_string(), scope: SymbolScope::Local, index: 0}, - Symbol{name: "f".to_string(), scope: SymbolScope::Local, index: 1}, - ], vec![ - Symbol{name: "c".to_string(), scope: SymbolScope::Local, index: 0}, - Symbol{name: "d".to_string(), scope: SymbolScope::Local, index: 1}, - ]), + ( + first_local, + vec![ + Symbol { + name: "a".to_string(), + scope: SymbolScope::Global, + index: 0, + }, + Symbol { + name: "b".to_string(), + scope: SymbolScope::Global, + index: 1, + }, + Symbol { + name: "c".to_string(), + scope: SymbolScope::Local, + index: 0, + }, + Symbol { + name: "d".to_string(), + scope: SymbolScope::Local, + index: 1, + }, + ], + vec![], + ), + ( + second_local, + vec![ + Symbol { + name: "a".to_string(), + scope: SymbolScope::Global, + index: 0, + }, + Symbol { + name: "b".to_string(), + scope: SymbolScope::Global, + index: 1, + }, + Symbol { + name: "c".to_string(), + scope: SymbolScope::Free, + index: 0, + }, + Symbol { + name: "d".to_string(), + scope: SymbolScope::Free, + index: 1, + }, + Symbol { + name: "e".to_string(), + scope: SymbolScope::Local, + index: 0, + }, + Symbol { + name: "f".to_string(), + scope: SymbolScope::Local, + index: 1, + }, + ], + vec![ + Symbol { + name: "c".to_string(), + scope: SymbolScope::Local, + index: 0, + }, + Symbol { + name: "d".to_string(), + scope: SymbolScope::Local, + index: 1, + }, + ], + ), ]; for mut t in &mut tests { @@ -1536,7 +1770,7 @@ mod test { assert_eq!(table.free_symbols.len(), expected_free_symbols.len()); let mut i = 0; for exp in expected_free_symbols { - let got = (*table.free_symbols[i]).borrow(); + let got = (table.free_symbols[i]).as_ref(); assert_eq!(*exp, *got); i += 1; } @@ -1556,15 +1790,31 @@ mod test { second_local.define("f"); let expected = vec![ - Symbol{name: "a".to_string(), scope: SymbolScope::Global, index: 0}, - Symbol{name: "c".to_string(), scope: SymbolScope::Free, index: 0}, - Symbol{name: "e".to_string(), scope: SymbolScope::Local, index: 0}, - Symbol{name: "f".to_string(), scope: SymbolScope::Local, index: 1}, + Symbol { + name: "a".to_string(), + scope: SymbolScope::Global, + index: 0, + }, + Symbol { + name: "c".to_string(), + scope: SymbolScope::Free, + index: 0, + }, + Symbol { + name: "e".to_string(), + scope: SymbolScope::Local, + index: 0, + }, + Symbol { + name: "f".to_string(), + scope: SymbolScope::Local, + index: 1, + }, ]; for exp in &expected { match second_local.resolve(&exp.name) { - Some(got) => assert_eq!(exp, got.borrow()), + Some(got) => assert_eq!(exp, got.as_ref()), None => panic!("name {} not resolvable", exp.name), } } @@ -1580,52 +1830,98 @@ mod test { for t in tests { let program = parse(t.input).unwrap(); let mut compiler = Compiler::new(); - let bytecode = compiler.compile(program).unwrap_or_else( - |err| panic!("{} error compiling on input: {}. want: {:?}", err.message, t.input, t.expected_instructions)); + let bytecode = compiler.compile(program).unwrap_or_else(|err| { + panic!( + "{} error compiling on input: {}. want: {:?}", + err.message, t.input, t.expected_instructions + ) + }); test_instructions(&t.expected_instructions, &bytecode.instructions).unwrap_or_else( - |err| panic!("{} error on instructions for: {}\nexp: {}\ngot: {}", &err.message, t.input, concat_instructions(&t.expected_instructions).string(), bytecode.instructions.string())); + |err| { + panic!( + "{} error on instructions for: {}\nexp: {}\ngot: {}", + &err.message, + t.input, + concat_instructions(&t.expected_instructions).string(), + bytecode.instructions.string() + ) + }, + ); - test_constants(&t.expected_constants, bytecode.constants.borrow()).unwrap_or_else( - |err| panic!("{} error on constants for : {}", &err.message, t.input)); + test_constants(&t.expected_constants, bytecode.constants.as_ref()).unwrap_or_else( + |err| panic!("{} error on constants for : {}", &err.message, t.input), + ); } } - - fn test_instructions(expected: &Vec, actual: &Instructions) -> ::std::result::Result<(), CompileError> { + fn test_instructions( + expected: &Vec, + actual: &Instructions, + ) -> ::std::result::Result<(), CompileError> { let concatted = concat_instructions(expected); if concatted.len() != actual.len() { - return Err(CompileError{message: format!("instruction lengths not equal\n\texp:\n{:?}\n\tgot:\n{:?}", concatted.string(), actual.string())}) + return Err(CompileError { + message: format!( + "instruction lengths not equal\n\texp:\n{:?}\n\tgot:\n{:?}", + concatted.string(), + actual.string() + ), + }); } let mut pos = 0; for (exp, got) in concatted.into_iter().zip(actual) { if exp != *got { - return Err(CompileError { message: format!("exp\n{:?} but got\n{} at position {:?}", exp, got, pos) }); + return Err(CompileError { + message: format!("exp\n{:?} but got\n{} at position {:?}", exp, got, pos), + }); } pos = pos + 1; } Ok(()) } - fn test_constants(expected: &Vec, actual: &Vec>) -> ::std::result::Result<(), CompileError> { + fn test_constants( + expected: &Vec, + actual: &Vec>, + ) -> ::std::result::Result<(), CompileError> { let mut pos = 0; for (exp, got) in expected.into_iter().zip(actual) { - let got = got.borrow(); + let got = got.as_ref(); match (exp, got) { - (Object::Int(exp), Object::Int(got)) => if *exp != *got { - return Err(CompileError{message: format!("constant {}, exp: {} got: {}", pos, exp, got)}) - }, - (Object::String(exp), Object::String(got)) => if exp != got { - return Err(CompileError{message: format!("constant {}, exp: {} got: {}", pos, exp, got)}) - }, - (Object::CompiledFunction(exp), Object::CompiledFunction(got)) => if exp != got { - return Err(CompileError{message: format!("constant {}, exp: {:?} got: {:?}, instructions exp:\n{}\ngot\n{}", pos, exp, got, exp.instructions.string(), got.instructions.string())}) - }, - _ => panic!("can't compare objects: exp: {:?} got: {:?}", exp, got) + (Object::Int(exp), Object::Int(got)) => { + if exp != got { + return Err(CompileError { + message: format!("constant {}, exp: {} got: {}", pos, exp, got), + }); + } + } + (Object::String(exp), Object::String(got)) => { + if exp != got { + return Err(CompileError { + message: format!("constant {}, exp: {} got: {}", pos, exp, got), + }); + } + } + (Object::CompiledFunction(exp), Object::CompiledFunction(got)) => { + if exp != got { + return Err(CompileError { + message: format!( + "constant {}, exp: {:?} got: {:?}, instructions exp:\n{}\ngot\n{}", + pos, + exp, + got, + exp.instructions.string(), + got.instructions.string() + ), + }); + } + } + _ => panic!("can't compare objects: exp: {:?} got: {:?}", exp, got), } pos = pos + 1; } @@ -1643,4 +1939,4 @@ mod test { concatted } -} \ No newline at end of file +} diff --git a/src/evaluator.rs b/src/evaluator.rs index b8bd841..a5372fe 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -1,13 +1,12 @@ -use std; -use std::fmt; -use std::cell::RefCell; -use std::rc::Rc; -use std::collections::HashMap; use crate::ast::*; use crate::object; -use crate::object::{Object, Environment, Function, Builtin, Array, MonkeyHash}; +use crate::object::{Array, Builtin, Environment, Function, MonkeyHash, Object}; use crate::token::Token; -use crate::parser; +use std; +use std::cell::RefCell; +use std::collections::HashMap; +use std::fmt; +use std::rc::Rc; pub type EvalResult = Result, EvalError>; @@ -30,9 +29,9 @@ impl std::error::Error for EvalError { pub fn eval(node: &Node, env: Rc>) -> EvalResult { match node { - Node::Program(prog) => eval_program(&prog, env), - Node::Statement(stmt) => eval_statement(&stmt, env), - Node::Expression(exp) => eval_expression(&exp, env), + Node::Program(prog) => eval_program(prog, env), + Node::Statement(stmt) => eval_statement(stmt, env), + Node::Expression(exp) => eval_expression(exp, env), } } @@ -44,49 +43,47 @@ fn eval_expression(exp: &Expression, env: Rc>) -> EvalResul Expression::Prefix(pre) => { let right = eval_expression(&pre.right, env)?; eval_prefix_expression(&pre.operator, right) - }, + } Expression::Infix(infix) => { let left = eval_expression(&infix.left, Rc::clone(&env))?; let right = eval_expression(&infix.right, env)?; eval_infix_expression(&infix.operator, left, right) - }, + } Expression::If(ifexp) => { let evaluated = eval_expression(&ifexp.condition, Rc::clone(&env))?; match is_truthy(&evaluated) { true => eval_block(&ifexp.consequence, env), - false => { - match &ifexp.alternative { - Some(alt) => eval_block(&alt, env), - None => Ok(Rc::new(Object::Null)), - } - } + false => match &ifexp.alternative { + Some(alt) => eval_block(alt, env), + None => Ok(Rc::new(Object::Null)), + }, } - }, - Expression::Identifier(ident) => { - eval_identifier(ident, env) - }, + } + Expression::Identifier(ident) => eval_identifier(ident, env), Expression::Function(f) => { - let func = Function{parameters: f.parameters.clone(), body: f.body.clone(), env: Rc::clone(&env)}; + let func = Function { + parameters: f.parameters.clone(), + body: f.body.clone(), + env: Rc::clone(&env), + }; Ok(Rc::new(Object::Function(Rc::new(func)))) - }, + } Expression::Call(exp) => { let function = eval_expression(&exp.function, Rc::clone(&env))?; let args = eval_expressions(&exp.arguments, env)?; apply_function(&function, &args) - }, + } Expression::Array(a) => { let elements = eval_expressions(&a.elements, Rc::clone(&env))?; - Ok(Rc::new(Object::Array(Rc::new(Array{elements})))) - }, + Ok(Rc::new(Object::Array(Rc::new(Array { elements })))) + } Expression::Index(i) => { let left = eval_expression(&i.left, Rc::clone(&env))?; let index = eval_expression(&i.index, env)?; eval_index_expression(left, index) - }, - Expression::Hash(h) => { - eval_hash_literal(&h, Rc::clone(&env)) - }, + } + Expression::Hash(h) => eval_hash_literal(h, Rc::clone(&env)), } } @@ -99,43 +96,39 @@ fn eval_hash_literal(h: &HashLiteral, env: Rc>) -> EvalResu pairs.insert(key, value); } - Ok(Rc::new(Object::Hash(Rc::new(MonkeyHash{pairs})))) + Ok(Rc::new(Object::Hash(Rc::new(MonkeyHash { pairs })))) } fn eval_index_expression(left: Rc, index: Rc) -> EvalResult { match (&*left, &*index) { - (Object::Array(a), Object::Int(i)) => { - match a.elements.get(*i as usize) { - Some(el) => Ok(Rc::clone(el)), - None => Ok(Rc::new(Object::Null)) - } + (Object::Array(a), Object::Int(i)) => match a.elements.get(*i as usize) { + Some(el) => Ok(Rc::clone(el)), + None => Ok(Rc::new(Object::Null)), }, - (Object::Hash(h), _object) => { - match &*index { - Object::String(_) | Object::Int(_) | Object::Bool(_) => { - match h.pairs.get(&*index) { - Some(obj) => Ok(Rc::clone(obj)), - None => Ok(Rc::new(Object::Null)) - } - }, - _ => Err(EvalError{message: format!("unusable as hash key: {}", index)}) - } - } - _ => Err(EvalError{message: format!("index operator not supported {}", index)}) + (Object::Hash(h), _object) => match &*index { + Object::String(_) | Object::Int(_) | Object::Bool(_) => match h.pairs.get(&*index) { + Some(obj) => Ok(Rc::clone(obj)), + None => Ok(Rc::new(Object::Null)), + }, + _ => Err(EvalError { + message: format!("unusable as hash key: {}", index), + }), + }, + _ => Err(EvalError { + message: format!("index operator not supported {}", index), + }), } } fn eval_identifier(ident: &str, env: Rc>) -> EvalResult { match env.borrow().get(ident) { - Some(obj) => { - Ok(obj.clone()) + Some(obj) => Ok(obj.clone()), + None => match Builtin::lookup(ident) { + Some(obj) => Ok(Rc::new(obj)), + None => Err(EvalError { + message: format!("identifier not found: {}", ident), + }), }, - None => { - match Builtin::lookup(ident) { - Some(obj) => Ok(Rc::new(obj)), - None => Err(EvalError{message: format!("identifier not found: {}", ident)}), - } - } } } @@ -145,21 +138,23 @@ fn apply_function(func: &Object, args: &Vec>) -> EvalResult { let extended_env = extend_function_env(f, args); let evaluated = eval_block(&f.body, extended_env)?; Ok(unwrap_return_value(evaluated)) + } + Object::Builtin(b) => match b.apply(args) { + Ok(obj) => Ok(obj), + Err(err) => Err(EvalError { message: err }), }, - Object::Builtin(b) => { - match b.apply(args) { - Ok(obj) => Ok(obj), - Err(err) => Err(EvalError{message: err}), - } - }, - f => Err(EvalError{message: format!("{:?} is not a function", f)}) + f => Err(EvalError { + message: format!("{:?} is not a function", f), + }), } } fn extend_function_env(func: &Function, args: &Vec>) -> Rc> { - let env = Rc::new(RefCell::new(Environment::new_enclosed(Rc::clone(&func.env)))); + let env = Rc::new(RefCell::new(Environment::new_enclosed(Rc::clone( + &func.env, + )))); - let mut args_iter = args.into_iter(); + let mut args_iter = args.iter(); for param in &func.parameters { let arg = args_iter.next().unwrap(); @@ -172,16 +167,19 @@ fn extend_function_env(func: &Function, args: &Vec>) -> Rc) -> Rc { if let Object::Return(ret) = &*obj { - return Rc::clone(&ret.value) + return Rc::clone(&ret.value); } obj } -fn eval_expressions(exps: &Vec, env: Rc>) -> Result>, EvalError> { +fn eval_expressions( + exps: &Vec, + env: Rc>, +) -> Result>, EvalError> { let mut objs = Vec::with_capacity(exps.len()); for e in exps { - let res = eval_expression(&e, Rc::clone(&env))?; + let res = eval_expression(e, Rc::clone(&env))?; objs.push(res); } @@ -200,15 +198,21 @@ fn eval_infix_expression(operator: &Token, left: Rc, right: Rc) match (&*left, &*right) { (Object::Int(l), Object::Int(r)) => eval_integer_infix_expression(operator, *l, *r), (Object::Bool(l), Object::Bool(r)) => eval_bool_infix_expression(operator, *l, *r), - (Object::String(l), Object::String(r)) => eval_string_infix_expression(operator, l.clone(), &*r), - _ => Err(EvalError{message: format!("type mismatch: {:?} {} {:?}", left, operator, right)}), + (Object::String(l), Object::String(r)) => { + eval_string_infix_expression(operator, l.clone(), r) + } + _ => Err(EvalError { + message: format!("type mismatch: {:?} {} {:?}", left, operator, right), + }), } } fn eval_string_infix_expression(operator: &Token, left: String, right: &str) -> EvalResult { match operator { Token::Plus => Ok(Rc::new(Object::String(left + right))), - _ => Err(EvalError{message: format!("unknown operator {} {} {}", left, operator, right)}), + _ => Err(EvalError { + message: format!("unknown operator {} {} {}", left, operator, right), + }), } } @@ -216,7 +220,9 @@ fn eval_bool_infix_expression(operator: &Token, left: bool, right: bool) -> Eval match operator { Token::Eq => Ok(Rc::new(Object::Bool(left == right))), Token::Neq => Ok(Rc::new(Object::Bool(left != right))), - _ => Err(EvalError{message: format!("unknown operator: {} {} {}", left, operator, right)}) + _ => Err(EvalError { + message: format!("unknown operator: {} {} {}", left, operator, right), + }), } } @@ -230,7 +236,9 @@ fn eval_integer_infix_expression(operator: &Token, left: i64, right: i64) -> Eva Token::Gt => Ok(Rc::new(Object::Bool(left > right))), Token::Eq => Ok(Rc::new(Object::Bool(left == right))), Token::Neq => Ok(Rc::new(Object::Bool(left != right))), - _ => Err(EvalError{message: format!("unknown operator {}", operator)}), + _ => Err(EvalError { + message: format!("unknown operator {}", operator), + }), } } @@ -254,8 +262,8 @@ fn eval_statement(stmt: &Statement, env: Rc>) -> EvalResult Statement::Expression(exp) => eval_expression(&exp.expression, env), Statement::Return(ret) => { let value = eval_expression(&ret.value, env)?; - Ok(Rc::new(Object::Return(Rc::new(object::Return{value})))) - }, + Ok(Rc::new(Object::Return(Rc::new(object::Return { value })))) + } Statement::Let(stmt) => { let exp = eval_expression(&stmt.value, Rc::clone(&env))?; let obj = Rc::clone(&exp); @@ -285,7 +293,9 @@ fn eval_prefix_expression(operator: &Token, right: Rc) -> EvalResult { match *operator { Token::Bang => eval_bang_operator_expression(right), Token::Minus => eval_minus_prefix_operator_expression(right), - _ => Err(EvalError{message:format!("unknown prefix operator {}", operator)}), + _ => Err(EvalError { + message: format!("unknown prefix operator {}", operator), + }), } } @@ -300,17 +310,17 @@ fn eval_bang_operator_expression(right: Rc) -> EvalResult { fn eval_minus_prefix_operator_expression(right: Rc) -> EvalResult { match *right { - Object::Int(val) => { - Ok(Rc::new(Object::Int(-val))) - }, - _ => Err(EvalError{message: format!("unknown operator: -{:?}", right)}), + Object::Int(val) => Ok(Rc::new(Object::Int(-val))), + _ => Err(EvalError { + message: format!("unknown operator: -{:?}", right), + }), } } #[cfg(test)] mod test { use super::*; - use crate::object::Object; + use crate::{object::Object, parser}; #[test] fn eval_integer_expression() { @@ -319,21 +329,66 @@ mod test { expected: i64, } let tests = vec![ - Test{input: "5", expected: 5}, - Test{input: "10", expected: 10}, - Test{input: "-5", expected: -5}, - Test{input: "-10", expected: -10}, - Test{input: "5 + 5 + 5 + 5 - 10", expected: 10}, - Test{input: "2 * 2 * 2 * 2 * 2", expected: 32}, - Test{input: "-50 + 100 + -50", expected: 0}, - Test{input: "5 * 2 + 10", expected: 20}, - Test{input: "5 + 2 * 10", expected: 25}, - Test{input: "20 + 2 * -10", expected: 0}, - Test{input: "50 / 2 * 2 + 10", expected: 60}, - Test{input: "2 * (5 + 10)", expected: 30}, - Test{input: "3 * 3 * 3 + 10", expected: 37}, - Test{input: "3 * (3 * 3) + 10", expected: 37}, - Test{input: "(5 + 10 * 2 + 15 / 3) * 2 + -10", expected: 50}, + Test { + input: "5", + expected: 5, + }, + Test { + input: "10", + expected: 10, + }, + Test { + input: "-5", + expected: -5, + }, + Test { + input: "-10", + expected: -10, + }, + Test { + input: "5 + 5 + 5 + 5 - 10", + expected: 10, + }, + Test { + input: "2 * 2 * 2 * 2 * 2", + expected: 32, + }, + Test { + input: "-50 + 100 + -50", + expected: 0, + }, + Test { + input: "5 * 2 + 10", + expected: 20, + }, + Test { + input: "5 + 2 * 10", + expected: 25, + }, + Test { + input: "20 + 2 * -10", + expected: 0, + }, + Test { + input: "50 / 2 * 2 + 10", + expected: 60, + }, + Test { + input: "2 * (5 + 10)", + expected: 30, + }, + Test { + input: "3 * 3 * 3 + 10", + expected: 37, + }, + Test { + input: "3 * (3 * 3) + 10", + expected: 37, + }, + Test { + input: "(5 + 10 * 2 + 15 / 3) * 2 + -10", + expected: 50, + }, ]; for t in tests { @@ -349,25 +404,82 @@ mod test { expected: bool, } let tests = vec![ - Test{input: "true", expected: true}, - Test{input: "false", expected: false}, - Test{input: "1 < 2", expected: true}, - Test{input: "1 > 2", expected: false}, - Test{input: "1 < 1", expected: false}, - Test{input: "1 > 1", expected: false}, - Test{input: "1 == 1", expected: true}, - Test{input: "1 != 1", expected: false}, - Test{input: "1 == 2", expected: false}, - Test{input: "1 != 2", expected: true}, - Test{input: "true == true", expected: true}, - Test{input: "false == false", expected: true}, - Test{input: "true == false", expected: false}, - Test{input: "true != false", expected: true}, - Test{input: "false != true", expected: true}, - Test{input: "(1 < 2) == true", expected: true}, - Test{input: "(1 < 2) == false", expected: false}, - Test{input: "(1 > 2) == true", expected: false}, - Test{input: "(1 > 2) == false", expected: true}, + Test { + input: "true", + expected: true, + }, + Test { + input: "false", + expected: false, + }, + Test { + input: "1 < 2", + expected: true, + }, + Test { + input: "1 > 2", + expected: false, + }, + Test { + input: "1 < 1", + expected: false, + }, + Test { + input: "1 > 1", + expected: false, + }, + Test { + input: "1 == 1", + expected: true, + }, + Test { + input: "1 != 1", + expected: false, + }, + Test { + input: "1 == 2", + expected: false, + }, + Test { + input: "1 != 2", + expected: true, + }, + Test { + input: "true == true", + expected: true, + }, + Test { + input: "false == false", + expected: true, + }, + Test { + input: "true == false", + expected: false, + }, + Test { + input: "true != false", + expected: true, + }, + Test { + input: "false != true", + expected: true, + }, + Test { + input: "(1 < 2) == true", + expected: true, + }, + Test { + input: "(1 < 2) == false", + expected: false, + }, + Test { + input: "(1 > 2) == true", + expected: false, + }, + Test { + input: "(1 > 2) == false", + expected: true, + }, ]; for t in tests { @@ -384,12 +496,30 @@ mod test { expected: bool, } let tests = vec![ - Test{input: "!true", expected: false}, - Test{input: "!false", expected: true}, - Test{input: "!5", expected: false}, - Test{input: "!!true", expected: true}, - Test{input: "!!false", expected: false}, - Test{input: "!!5", expected: true}, + Test { + input: "!true", + expected: false, + }, + Test { + input: "!false", + expected: true, + }, + Test { + input: "!5", + expected: false, + }, + Test { + input: "!!true", + expected: true, + }, + Test { + input: "!!false", + expected: false, + }, + Test { + input: "!!5", + expected: true, + }, ]; for t in tests { @@ -405,13 +535,34 @@ mod test { expected: Object, } let tests = vec![ - Test{input: "if (true) { 10 }", expected: Object::Int(10)}, - Test{input: "if (false) { 10 }", expected: Object::Null}, - Test{input: "if (1) { 10 }", expected: Object::Int(10)}, - Test{input: "if (1 < 2) { 10 }", expected: Object::Int(10)}, - Test{input: "if (1 > 2) { 10 }", expected: Object::Null}, - Test{input: "if (1 > 2) { 10 } else { 20 }", expected: Object::Int(20)}, - Test{input: "if (1 < 2) { 10 } else { 20 }", expected: Object::Int(10)}, + Test { + input: "if (true) { 10 }", + expected: Object::Int(10), + }, + Test { + input: "if (false) { 10 }", + expected: Object::Null, + }, + Test { + input: "if (1) { 10 }", + expected: Object::Int(10), + }, + Test { + input: "if (1 < 2) { 10 }", + expected: Object::Int(10), + }, + Test { + input: "if (1 > 2) { 10 }", + expected: Object::Null, + }, + Test { + input: "if (1 > 2) { 10 } else { 20 }", + expected: Object::Int(20), + }, + Test { + input: "if (1 < 2) { 10 } else { 20 }", + expected: Object::Int(10), + }, ]; for t in tests { @@ -431,16 +582,31 @@ mod test { expected: i64, } let tests = vec![ - Test{input: "return 10;", expected: 10}, - Test{input: "return 10; 9;", expected: 10}, - Test{input: "return 2 * 5; 9;", expected: 10}, - Test{input: "9; return 2 * 5; 9;", expected: 10}, - Test{input: "if (10 > 1) { + Test { + input: "return 10;", + expected: 10, + }, + Test { + input: "return 10; 9;", + expected: 10, + }, + Test { + input: "return 2 * 5; 9;", + expected: 10, + }, + Test { + input: "9; return 2 * 5; 9;", + expected: 10, + }, + Test { + input: "if (10 > 1) { if (10 > 1) { return 10; } return 1; - }", expected: 10}, + }", + expected: 10, + }, ]; for t in tests { @@ -456,31 +622,56 @@ mod test { expected: &'a str, } let tests = vec![ - Test{input: "5 + true;", expected: "type mismatch: Int(5) + Bool(true)"}, - Test{input: "5 + true; 5;", expected: "type mismatch: Int(5) + Bool(true)"}, - Test{input: "-true", expected: "unknown operator: -Bool(true)"}, - Test{input: "true + false", expected: "unknown operator: true + false"}, - Test{input: "5; true + false; 5", expected: "unknown operator: true + false"}, - Test{input: "if (10 > 1) { true + false; }", expected: "unknown operator: true + false"}, - Test{input: "if (10 > 1) { + Test { + input: "5 + true;", + expected: "type mismatch: Int(5) + Bool(true)", + }, + Test { + input: "5 + true; 5;", + expected: "type mismatch: Int(5) + Bool(true)", + }, + Test { + input: "-true", + expected: "unknown operator: -Bool(true)", + }, + Test { + input: "true + false", + expected: "unknown operator: true + false", + }, + Test { + input: "5; true + false; 5", + expected: "unknown operator: true + false", + }, + Test { + input: "if (10 > 1) { true + false; }", + expected: "unknown operator: true + false", + }, + Test { + input: "if (10 > 1) { if (10 > 1) { return true + false; } return 1; - }", expected: "unknown operator: true + false"}, - Test{input: "foobar", expected: "identifier not found: foobar"}, - Test{input: r#" {"name": "Monkey"}[fn(x) { x }]; "#, expected: "unusable as hash key: fn(x) {\nx\n}"}, + }", + expected: "unknown operator: true + false", + }, + Test { + input: "foobar", + expected: "identifier not found: foobar", + }, + Test { + input: r#" {"name": "Monkey"}[fn(x) { x }]; "#, + expected: "unusable as hash key: fn(x) {\nx\n}", + }, ]; for t in tests { let env = Rc::new(RefCell::new(Environment::new())); match parser::parse(t.input) { - Ok(node) => { - match eval(&node, env) { - Err(e) => assert_eq!(e.message, t.expected), - n => panic!("expected error {} but got {:?}", t.expected, n) - } + Ok(node) => match eval(&node, env) { + Err(e) => assert_eq!(e.message, t.expected), + n => panic!("expected error {} but got {:?}", t.expected, n), }, Err(e) => panic!("error {:?} on input {}", e, t.input), } @@ -494,10 +685,22 @@ mod test { expected: i64, } let tests = vec![ - Test{input: "let a = 5; a;", expected: 5}, - Test{input: "let a = 5 * 5; a;", expected: 25}, - Test{input: "let a = 5; let b = a; b;", expected: 5}, - Test{input: "let a = 5; let b = a; let c = a + b + 5; c;", expected: 15}, + Test { + input: "let a = 5; a;", + expected: 5, + }, + Test { + input: "let a = 5 * 5; a;", + expected: 25, + }, + Test { + input: "let a = 5; let b = a; b;", + expected: 5, + }, + Test { + input: "let a = 5; let b = a; let c = a + b + 5; c;", + expected: 15, + }, ]; for t in tests { @@ -516,8 +719,8 @@ mod test { assert_eq!(f.parameters.len(), 1); assert_eq!(f.parameters.first().unwrap().name, "x"); assert_eq!(f.body.to_string(), "(x + 2)"); - }, - _ => panic!("expected function object but got {:?}", evaluated) + } + _ => panic!("expected function object but got {:?}", evaluated), } } #[test] @@ -527,12 +730,30 @@ mod test { expected: i64, } let tests = vec![ - Test{input: "let identity = fn(x) { x; }; identity(5);", expected: 5}, - Test{input: "let identity = fn(x) { return x; }; identity(5);", expected: 5}, - Test{input: "let double = fn(x) { x * 2; }; double(5);", expected: 10}, - Test{input: "let add = fn(x, y) { x + y; }; add(5, 5);", expected: 10}, - Test{input: "let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", expected: 20}, - Test{input: "fn(x) { x; }(5)", expected: 5}, + Test { + input: "let identity = fn(x) { x; }; identity(5);", + expected: 5, + }, + Test { + input: "let identity = fn(x) { return x; }; identity(5);", + expected: 5, + }, + Test { + input: "let double = fn(x) { x * 2; }; double(5);", + expected: 10, + }, + Test { + input: "let add = fn(x, y) { x + y; }; add(5, 5);", + expected: 10, + }, + Test { + input: "let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", + expected: 20, + }, + Test { + input: "fn(x) { x; }(5)", + expected: 5, + }, ]; for t in tests { @@ -557,9 +778,8 @@ addTwo(2);"; match &*test_eval(input) { Object::String(s) => assert_eq!(s, "Hello World!"), - obj => panic!(format!("expected string but got {:?}", obj)) + obj => panic!("expected string but got {:?}", obj), } - } #[test] @@ -568,7 +788,7 @@ addTwo(2);"; match &*test_eval(input) { Object::String(s) => assert_eq!(s, "Hello World!"), - obj => panic!(format!("expected string but got {:?}", obj)) + obj => panic!("expected string but got {:?}", obj), } } @@ -579,26 +799,70 @@ addTwo(2);"; expected: Object, } let tests = vec![ - Test{input: r#"len("")"#, expected: Object::Int(0)}, - Test{input: r#"len("four")"#, expected: Object::Int(4)}, - Test{input: r#"len("hello world")"#, expected: Object::Int(11)}, - Test{input: "len([1, 2, 3])", expected: Object::Int(3)}, - Test{input: "len([])", expected: Object::Int(0)}, - Test{input: "first([1, 2, 3])", expected: Object::Int(1)}, - Test{input: "first([])", expected: Object::Null}, - Test{input: "last([1, 2, 3])", expected: Object::Int(3)}, - Test{input: "last([])", expected: Object::Null}, - Test{input: "rest([1, 2, 3])", expected: Object::Array(Rc::new(Array{elements: vec![Rc::new(Object::Int(2)), Rc::new(Object::Int(3))]}))}, - Test{input: "rest([])", expected: Object::Array(Rc::new(Array{elements: vec![]}))}, - Test{input: "push([], 1)", expected: Object::Array(Rc::new(Array{elements: vec![Rc::new(Object::Int(1))]}))}, + Test { + input: r#"len("")"#, + expected: Object::Int(0), + }, + Test { + input: r#"len("four")"#, + expected: Object::Int(4), + }, + Test { + input: r#"len("hello world")"#, + expected: Object::Int(11), + }, + Test { + input: "len([1, 2, 3])", + expected: Object::Int(3), + }, + Test { + input: "len([])", + expected: Object::Int(0), + }, + Test { + input: "first([1, 2, 3])", + expected: Object::Int(1), + }, + Test { + input: "first([])", + expected: Object::Null, + }, + Test { + input: "last([1, 2, 3])", + expected: Object::Int(3), + }, + Test { + input: "last([])", + expected: Object::Null, + }, + Test { + input: "rest([1, 2, 3])", + expected: Object::Array(Rc::new(Array { + elements: vec![Rc::new(Object::Int(2)), Rc::new(Object::Int(3))], + })), + }, + Test { + input: "rest([])", + expected: Object::Array(Rc::new(Array { elements: vec![] })), + }, + Test { + input: "push([], 1)", + expected: Object::Array(Rc::new(Array { + elements: vec![Rc::new(Object::Int(1))], + })), + }, ]; for t in tests { let obj = test_eval(t.input); match (&t.expected, &*obj) { - (Object::Int(exp), Object::Int(got)) => assert_eq!(*exp, *got, "on input {} expected {} but got {}", t.input, exp, got), - (Object::Null, Object::Null) => {}, + (Object::Int(exp), Object::Int(got)) => assert_eq!( + *exp, *got, + "on input {} expected {} but got {}", + t.input, exp, got + ), + (Object::Null, Object::Null) => {} (Object::Array(ex), Object::Array(got)) => { assert_eq!(ex.elements.len(), got.elements.len()); let mut got_iter = (&got.elements).into_iter(); @@ -606,11 +870,14 @@ addTwo(2);"; let got_obj = Rc::clone(got_iter.next().unwrap()); match (&*Rc::clone(obj), &*got_obj) { (Object::Int(exi), Object::Int(goti)) => assert_eq!(*exi, *goti), - _ => panic!("{:?} not same type as {:?}", got_obj, obj) + _ => panic!("{:?} not same type as {:?}", got_obj, obj), } } } - _ => panic!("on input {} expected {:?} but got {:?}", t.input, t.expected, obj) + _ => panic!( + "on input {} expected {:?} but got {:?}", + t.input, t.expected, obj + ), } } } @@ -622,19 +889,30 @@ addTwo(2);"; expected: &'a str, } let tests = vec![ - Test{input: r#"len(1)"#, expected: "object Int(1) not supported as an argument for len"}, - Test{input: r#"len("one", "two")"#, expected: "len takes only 1 array or string argument"}, - Test{input: r#"first(1)"#, expected: "object Int(1) not supported as an argument for first"}, + Test { + input: r#"len(1)"#, + expected: "object Int(1) not supported as an argument for len", + }, + Test { + input: r#"len("one", "two")"#, + expected: "len takes only 1 array or string argument", + }, + Test { + input: r#"first(1)"#, + expected: "object Int(1) not supported as an argument for first", + }, ]; for t in tests { let env = Rc::new(RefCell::new(Environment::new())); match parser::parse(t.input) { - Ok(node) => { - match eval(&node, env) { - Ok(obj) => panic!("expected error on input {} but got {:?}", t.input, obj), - Err(err) => assert_eq!(t.expected, err.message, "on input {} expected error {} but got {}", t.input, t.expected, err.message) - } + Ok(node) => match eval(&node, env) { + Ok(obj) => panic!("expected error on input {} but got {:?}", t.input, obj), + Err(err) => assert_eq!( + t.expected, err.message, + "on input {} expected error {} but got {}", + t.input, t.expected, err.message + ), }, Err(e) => panic!("error {:?} on input {}", e, t.input), } @@ -645,13 +923,13 @@ addTwo(2);"; fn array_literals() { let input = "[1, 2 * 2, 3 + 3]"; let obj = test_eval(input); - match &*obj { + match &*obj { Object::Array(a) => { test_integer_object(a.elements.get(0).unwrap(), 1); test_integer_object(a.elements.get(1).unwrap(), 4); test_integer_object(a.elements.get(2).unwrap(), 6); - }, - _ => panic!("expected array but got {:?}", obj) + } + _ => panic!("expected array but got {:?}", obj), } } @@ -662,37 +940,58 @@ addTwo(2);"; expected: i64, } let tests = vec![ - Test{input: "[1, 2, 3][0]", expected: 1}, - Test{input: "[1, 2, 3][1]", expected: 2}, - Test{input: "[1, 2, 3][2]", expected: 3}, - Test{input: "let i = 0; [1][i];", expected: 1}, - Test{input: "[1, 2, 3][1 + 1];", expected: 3}, - Test{input: "let myArray = [1, 2, 3]; myArray[2];", expected: 3}, - Test{input: "let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];", expected: 6}, - Test{input: "let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]", expected: 2}, + Test { + input: "[1, 2, 3][0]", + expected: 1, + }, + Test { + input: "[1, 2, 3][1]", + expected: 2, + }, + Test { + input: "[1, 2, 3][2]", + expected: 3, + }, + Test { + input: "let i = 0; [1][i];", + expected: 1, + }, + Test { + input: "[1, 2, 3][1 + 1];", + expected: 3, + }, + Test { + input: "let myArray = [1, 2, 3]; myArray[2];", + expected: 3, + }, + Test { + input: "let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];", + expected: 6, + }, + Test { + input: "let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]", + expected: 2, + }, ]; for t in tests { let obj = test_eval(t.input); match &*obj { Object::Int(i) => assert_eq!(*i, t.expected), - _ => panic!("expected int obj but got {:?}", obj) + _ => panic!("expected int obj but got {:?}", obj), } } } #[test] fn invalid_array_index() { - let inputs = vec![ - "[1, 2, 3][3]", - "[1, 2, 3][-1]", - ]; + let inputs = vec!["[1, 2, 3][3]", "[1, 2, 3][-1]"]; for input in inputs { let obj = test_eval(input); match &*obj { - Object::Null => {}, - _ => panic!("expected null object, but got {:?}", obj) + Object::Null => {} + _ => panic!("expected null object, but got {:?}", obj), } } } @@ -717,13 +1016,11 @@ addTwo(2);"; for (key, value) in &h.pairs { match (&*Rc::clone(key), &*Rc::clone(value)) { - (Object::String(k), Object::Int(val)) => { - match k.as_str() { - "one" => assert_eq!(*val, 1), - "two" => assert_eq!(*val, 2), - "three" => assert_eq!(*val, 3), - _ => panic!("unexpected string key {}", k) - } + (Object::String(k), Object::Int(val)) => match k.as_str() { + "one" => assert_eq!(*val, 1), + "two" => assert_eq!(*val, 2), + "three" => assert_eq!(*val, 3), + _ => panic!("unexpected string key {}", k), }, (Object::Bool(b), Object::Int(val)) => { if *b { @@ -731,13 +1028,13 @@ addTwo(2);"; } else { assert_eq!(*val, 6) } - }, + } (Object::Int(k), Object::Int(val)) => assert_eq!(k, val), - _ => panic!("unexpected key value pair {:?} {:?}", key, value) + _ => panic!("unexpected key value pair {:?} {:?}", key, value), } } - }, - _ => panic!("expected hash object, but got {:?}", obj) + } + _ => panic!("expected hash object, but got {:?}", obj), } } @@ -748,22 +1045,50 @@ addTwo(2);"; expected: Object, } let tests = vec![ - Test{input: r#" {"foo":5}["foo"] "#, expected: Object::Int(5)}, - Test{input: r#" {"foo":5}["bar"] "#, expected: Object::Null}, - Test{input: r#" let key = "foo"; {"foo":5}[key] "#, expected: Object::Int(5)}, - Test{input: r#" {}["foo"] "#, expected: Object::Null}, - Test{input: r#" {5: 5}[5] "#, expected: Object::Int(5)}, - Test{input: r#" {true: 5}[true] "#, expected: Object::Int(5)}, - Test{input: r#" {false: 5}[false] "#, expected: Object::Int(5)}, + Test { + input: r#" {"foo":5}["foo"] "#, + expected: Object::Int(5), + }, + Test { + input: r#" {"foo":5}["bar"] "#, + expected: Object::Null, + }, + Test { + input: r#" let key = "foo"; {"foo":5}[key] "#, + expected: Object::Int(5), + }, + Test { + input: r#" {}["foo"] "#, + expected: Object::Null, + }, + Test { + input: r#" {5: 5}[5] "#, + expected: Object::Int(5), + }, + Test { + input: r#" {true: 5}[true] "#, + expected: Object::Int(5), + }, + Test { + input: r#" {false: 5}[false] "#, + expected: Object::Int(5), + }, ]; for t in tests { let obj = test_eval(t.input); match (&t.expected, &*obj) { - (Object::Int(exp), Object::Int(got)) => assert_eq!(*exp, *got, "on input {} expected {} but got {}", t.input, exp, got), - (Object::Null, Object::Null) => {}, - _ => panic!("on input {} expected {:?} but got {:?}", t.input, t.expected, obj) + (Object::Int(exp), Object::Int(got)) => assert_eq!( + *exp, *got, + "on input {} expected {} but got {}", + t.input, exp, got + ), + (Object::Null, Object::Null) => {} + _ => panic!( + "on input {} expected {:?} but got {:?}", + t.input, t.expected, obj + ), } } } @@ -771,10 +1096,7 @@ addTwo(2);"; fn test_eval(input: &str) -> Rc { let env = Rc::new(RefCell::new(Environment::new())); match parser::parse(input) { - Ok(node) => { - eval(&node, env).expect(input) - - }, + Ok(node) => eval(&node, env).expect(input), Err(e) => panic!("error {:?} on input {}", e, input), } } @@ -795,8 +1117,8 @@ addTwo(2);"; fn test_null_object(obj: &Object) { match obj { - Object::Null => {}, + Object::Null => {} _ => panic!("expected null but got {:?}", obj), } } -} \ No newline at end of file +} diff --git a/src/lexer.rs b/src/lexer.rs index 0e09449..2b932f5 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -8,8 +8,8 @@ pub struct Lexer<'a> { } impl<'a> Lexer<'a> { - pub fn new(input: &'a str) -> Lexer<'_> { - Lexer{ + pub fn new(input: &'a str) -> Lexer<'a> { + Lexer { input: input.chars().peekable(), } } @@ -24,7 +24,7 @@ impl<'a> Lexer<'a> { } else { Token::Assign } - }, + } Some('+') => Token::Plus, Some('(') => Token::Lparen, Some(')') => Token::Rparen, @@ -43,7 +43,7 @@ impl<'a> Lexer<'a> { } else { Token::Bang } - }, + } Some('*') => Token::Asterisk, Some('/') => Token::Slash, Some('<') => Token::Lt, @@ -51,16 +51,16 @@ impl<'a> Lexer<'a> { Some(ch) => { if is_letter(ch) { let ident = self.read_identifier(ch); - let tok = token::lookup_ident(ident); - tok - } else if ch.is_digit(10) { + + token::lookup_ident(ident) + } else if ch.is_ascii_digit() { Token::Int(self.read_int(ch)) } else if ch == '"' { Token::String(self.read_string()) } else { Token::Illegal } - }, + } None => Token::EOF, } } @@ -99,7 +99,7 @@ impl<'a> Lexer<'a> { s.push(ch); while let Some(&ch) = self.peek_char() { - if ch.is_digit(10) { + if ch.is_ascii_digit() { s.push(self.read_char().unwrap()); } else { break; @@ -142,8 +142,8 @@ fn is_letter(ch: char) -> bool { #[cfg(test)] mod test { - use crate::token::Token; use super::*; + use crate::token::Token; #[test] fn next_token() { @@ -269,4 +269,4 @@ if (5 < 10) { assert_eq!(*t, tok, "expected {} token but got {}", t, tok) } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index e30849c..bf86e1d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,13 +33,13 @@ //! } //! ``` -mod token; mod ast; -pub mod object; -pub mod lexer; -pub mod repl; -pub mod parser; -pub mod evaluator; pub mod code; pub mod compiler; -pub mod vm; \ No newline at end of file +pub mod evaluator; +pub mod lexer; +pub mod object; +pub mod parser; +pub mod repl; +mod token; +pub mod vm; diff --git a/src/main.rs b/src/main.rs index c161d1e..1ab40d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,15 @@ +use monkey::compiler::Compiler; +use monkey::evaluator::eval; +use monkey::object::Environment; +use monkey::parser::parse; +use monkey::repl; +use monkey::vm::VM; +use std::cell::RefCell; use std::env; -use std::process::exit; use std::io; -use std::error::Error; -use std::time::Instant; +use std::process::exit; use std::rc::Rc; -use std::cell::RefCell; -use monkey::repl; -use monkey::parser::parse; -use monkey::compiler::Compiler; -use monkey::vm::VM; -use monkey::object::Environment; -use monkey::evaluator::eval; +use std::time::Instant; const HELP: &str = "requires one of the following arguments:\n\trepl - starts the repl\n\tvm - benchmarks the vm fibonacci\n\teval - bencharks the interpreter fibonacci"; const PROGRAM: &str = "\ @@ -28,7 +27,7 @@ let fibonacci = fn(x) { fibonacci(35); "; -fn main() -> io::Result<()> { +fn main() -> Result<(), String> { let args: Vec = env::args().collect(); if args.len() == 1 { @@ -43,41 +42,30 @@ fn main() -> io::Result<()> { println!("Welcome to the Monkey REPL!"); let input = io::stdin(); let output = io::stdout(); - repl::start(input.lock(), output.lock()) - }, + repl::start(input.lock(), output.lock()).map_err(|err| format!("io error: {}", err))?; + } "vm" => { let mut compiler = Compiler::new(); - let bytecode = compiler.compile(program).unwrap(); + let bytecode = compiler.compile(program).map_err(|err| err.to_string())?; let mut machine = VM::new(bytecode.constants, bytecode.instructions.to_vec()); - let now = Instant::now(); - - { - machine.run(); - } - + machine.run(); let elapsed = now.elapsed(); let sec = (elapsed.as_secs() as f64) + (elapsed.subsec_nanos() as f64 / 1000_000_000.0); println!("VM time seconds: {}", sec); - Ok(()) - }, + } "eval" => { - let mut env = Rc::new(RefCell::new(Environment::new())); - + let env = Rc::new(RefCell::new(Environment::new())); let now = Instant::now(); - - { - eval(&program, env).unwrap(); - } - + eval(&program, env).map_err(|err| err.to_string())?; let elapsed = now.elapsed(); let sec = (elapsed.as_secs() as f64) + (elapsed.subsec_nanos() as f64 / 1000_000_000.0); println!("Eval time seconds: {}", sec); - Ok(()) - }, + } arg => { println!("Unsupported argument '{}': {}", arg, HELP); exit(1); } } + Ok(()) } diff --git a/src/object.rs b/src/object.rs index ca84036..709189f 100644 --- a/src/object.rs +++ b/src/object.rs @@ -1,12 +1,11 @@ -use std::fmt; -use std::collections::HashMap; -use std::hash::{Hash,Hasher}; -use std::cell::RefCell; -use std::rc::Rc; use crate::ast; -use crate::code; -use code::InstructionsFns; +use crate::code::{Instructions, InstructionsFns}; use enum_iterator::IntoEnumIterator; +use std::cell::RefCell; +use std::collections::HashMap; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::rc::Rc; #[derive(Hash, Eq, PartialEq, Clone, Debug)] pub enum Object { @@ -48,11 +47,14 @@ impl fmt::Display for Object { #[derive(Eq, PartialEq, Clone, Debug)] pub struct MonkeyHash { - pub pairs: HashMap,Rc>, + pub pairs: HashMap, Rc>, } impl MonkeyHash { fn inspect(&self) -> String { - let pairs: Vec = (&self.pairs).into_iter().map(|(key, value)| format!("{}: {}", key.inspect(), value.inspect())).collect(); + let pairs: Vec = (&self.pairs) + .iter() + .map(|(key, value)| format!("{}: {}", key.inspect(), value.inspect())) + .collect(); format!("{{{}}}", pairs.join(", ")) } } @@ -70,7 +72,7 @@ pub struct Array { impl Array { fn inspect(&self) -> String { - let elements: Vec = (&self.elements).into_iter().map(|e| e.to_string()).collect(); + let elements: Vec = (&self.elements).iter().map(|e| e.to_string()).collect(); format!("[{}]", elements.join(", ")) } } @@ -110,70 +112,78 @@ impl Builtin { match self { Builtin::Len => { if args.len() != 1 { - return Err("len takes only 1 array or string argument".to_string()) + return Err("len takes only 1 array or string argument".to_string()); } let arg = &*Rc::clone(args.first().unwrap()); match arg { Object::String(s) => Ok(Rc::new(Object::Int(s.len() as i64))), Object::Array(a) => Ok(Rc::new(Object::Int(a.elements.len() as i64))), - obj => Err(format!("object {:?} not supported as an argument for len", obj)) + obj => Err(format!( + "object {:?} not supported as an argument for len", + obj + )), } - }, + } Builtin::First => { if args.len() != 1 { - return Err("first takes only 1 array argument".to_string()) + return Err("first takes only 1 array argument".to_string()); } - let arg = &*Rc::clone( args.first().unwrap()); + let arg = &*Rc::clone(args.first().unwrap()); match arg { - Object::Array(a) => { - match a.elements.first() { - Some(el) => Ok(Rc::clone(el)), - None => Ok(Rc::new(Object::Null)), - } + Object::Array(a) => match a.elements.first() { + Some(el) => Ok(Rc::clone(el)), + None => Ok(Rc::new(Object::Null)), }, - obj => Err(format!("object {:?} not supported as an argument for first", obj)) + obj => Err(format!( + "object {:?} not supported as an argument for first", + obj + )), } - }, + } Builtin::Last => { if args.len() != 1 { - return Err("last takes only 1 array argument".to_string()) + return Err("last takes only 1 array argument".to_string()); } let arg = &*Rc::clone(args.first().unwrap()); match arg { - Object::Array(a) => { - match a.elements.last() { - Some(el) => Ok(Rc::clone(el)), - None => Ok(Rc::new(Object::Null)), - } + Object::Array(a) => match a.elements.last() { + Some(el) => Ok(Rc::clone(el)), + None => Ok(Rc::new(Object::Null)), }, - obj => Err(format!("object {:?} not supported as an argument for last", obj)) + obj => Err(format!( + "object {:?} not supported as an argument for last", + obj + )), } - }, + } Builtin::Rest => { if args.len() != 1 { - return Err("rest takes only 1 array argument".to_string()) + return Err("rest takes only 1 array argument".to_string()); } let arg = &*Rc::clone(args.first().unwrap()); match arg { Object::Array(a) => { if a.elements.len() <= 1 { - Ok(Rc::new(Object::Array(Rc::new(Array{elements: vec![]})))) + Ok(Rc::new(Object::Array(Rc::new(Array { elements: vec![] })))) } else { let mut elements = a.elements.clone(); elements.remove(0); - Ok(Rc::new(Object::Array(Rc::new(Array{elements})))) + Ok(Rc::new(Object::Array(Rc::new(Array { elements })))) } - }, - obj => Err(format!("object {:?} is not supported as an argument for rest", obj)) + } + obj => Err(format!( + "object {:?} is not supported as an argument for rest", + obj + )), } - }, + } Builtin::Push => { if args.len() != 2 { - return Err("push takes an array and an object".to_string()) + return Err("push takes an array and an object".to_string()); } let array = &*Rc::clone(args.first().unwrap()); @@ -184,11 +194,11 @@ impl Builtin { Object::Array(a) => { let mut elements = a.elements.clone(); elements.push(obj); - Ok(Rc::new(Object::Array(Rc::new(Array{elements})))) - }, - _ => Err("first argument to push must be an array".to_string()) + Ok(Rc::new(Object::Array(Rc::new(Array { elements })))) + } + _ => Err("first argument to push must be an array".to_string()), } - }, + } Builtin::Puts => { for arg in args { println!("{}", arg.inspect()) @@ -223,8 +233,8 @@ pub struct Function { impl Function { fn inspect(&self) -> String { - let params: Vec = (&self.parameters).into_iter().map(|p| p.to_string()).collect(); - format!("fn({}) {{\n{}\n}}", params.join(", "), self.body.to_string()) + let params: Vec = (&self.parameters).iter().map(|p| p.to_string()).collect(); + format!("fn({}) {{\n{}\n}}", params.join(", "), self.body) } } impl PartialEq for Function { @@ -243,7 +253,7 @@ impl Hash for Function { #[derive(Eq, PartialEq, Clone, Debug)] pub struct CompiledFunction { - pub instructions: code::Instructions, + pub instructions: Instructions, pub num_locals: usize, pub num_parameters: usize, } @@ -265,7 +275,9 @@ pub struct Closure { pub free: Vec>, } impl Closure { - fn inspect(&self) -> String { format!("Closure[{:?}]", self) } + fn inspect(&self) -> String { + format!("Closure[{:?}]", self) + } } impl Hash for Closure { fn hash(&self, _state: &mut H) { @@ -297,25 +309,33 @@ pub struct Environment { pub outer: Option>>, } +impl Default for Environment { + fn default() -> Self { + Self::new() + } +} + impl Environment { pub fn new() -> Environment { - Environment{store: HashMap::new(), outer: None} + Environment { + store: HashMap::new(), + outer: None, + } } pub fn new_enclosed(env: Rc>) -> Environment { - Environment{store: HashMap::new(), outer: Some(Rc::clone(&env))} + Environment { + store: HashMap::new(), + outer: Some(Rc::clone(&env)), + } } pub fn get(&self, name: &str) -> Option> { match self.store.get(name) { - Some(obj) => { - Some(Rc::clone(obj)) - }, - None => { - match &self.outer { - Some(o) => o.borrow().get(name), - _ => None, - } + Some(obj) => Some(Rc::clone(obj)), + None => match &self.outer { + Some(o) => o.borrow().get(name), + _ => None, }, } } @@ -356,4 +376,4 @@ mod test { diff1.hash(&mut hasher2); assert_ne!(hasher1.finish(), hasher2.finish()); } -} \ No newline at end of file +} diff --git a/src/parser.rs b/src/parser.rs index ac2a5c0..32e588c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,7 @@ use crate::ast::*; -use std::collections::HashMap; -use crate::token::Token; use crate::lexer::Lexer; +use crate::token::Token; +use std::collections::HashMap; type ParseError = String; type ParseErrors = Vec; @@ -80,7 +80,7 @@ impl<'a> Parser<'a> { tok = self.cur_token.clone(); } - if errors.len() > 0 { + if !errors.is_empty() { return Err(errors); } @@ -102,7 +102,9 @@ impl<'a> Parser<'a> { self.next_token(); } - Ok(Statement::Expression(Box::new(ExpressionStatement { expression }))) + Ok(Statement::Expression(Box::new(ExpressionStatement { + expression, + }))) } fn parse_expression(&mut self, precedence: Precedence) -> ParseResult { @@ -111,7 +113,10 @@ impl<'a> Parser<'a> { if let Some(f) = self.prefix_fn() { left_exp = f(self)?; } else { - return Err(format!("no prefix parse function for {} found", self.cur_token)); + return Err(format!( + "no prefix parse function for {} found", + self.cur_token + )); } while !self.peek_token_is(&Token::Semicolon) && precedence < self.peek_precedence() { @@ -144,7 +149,7 @@ impl<'a> Parser<'a> { } fn parse_hash_literal(parser: &mut Parser<'_>) -> ParseResult { - let mut pairs: HashMap = HashMap::new(); + let mut pairs: HashMap = HashMap::new(); while !parser.peek_token_is(&Token::Rbrace) { parser.next_token(); @@ -163,12 +168,12 @@ impl<'a> Parser<'a> { parser.expect_peek(Token::Rbrace)?; - Ok(Expression::Hash(Box::new(HashLiteral{pairs}))) + Ok(Expression::Hash(Box::new(HashLiteral { pairs }))) } fn parse_array_literal(parser: &mut Parser<'_>) -> ParseResult { let elements = parser.parse_expression_list(Token::Rbracket)?; - Ok(Expression::Array(Box::new(ArrayLiteral{elements}))) + Ok(Expression::Array(Box::new(ArrayLiteral { elements }))) } fn parse_expression_list(&mut self, end: Token) -> ParseResult> { @@ -176,7 +181,7 @@ impl<'a> Parser<'a> { if self.peek_token_is(&end) { self.next_token(); - return Ok(list) + return Ok(list); } self.next_token(); @@ -200,7 +205,10 @@ impl<'a> Parser<'a> { let right = parser.parse_expression(Precedence::Prefix)?; - Ok(Expression::Prefix(Box::new(PrefixExpression { operator, right }))) + Ok(Expression::Prefix(Box::new(PrefixExpression { + operator, + right, + }))) } fn parse_if_expression(parser: &mut Parser<'_>) -> ParseResult { @@ -225,7 +233,11 @@ impl<'a> Parser<'a> { None }; - Ok(Expression::If(Box::new(IfExpression { condition, consequence, alternative }))) + Ok(Expression::If(Box::new(IfExpression { + condition, + consequence, + alternative, + }))) } fn parse_block_statement(&mut self) -> ParseResult { @@ -252,7 +264,10 @@ impl<'a> Parser<'a> { let body = parser.parse_block_statement()?; - Ok(Expression::Function(Box::new(FunctionLiteral{parameters,body}))) + Ok(Expression::Function(Box::new(FunctionLiteral { + parameters, + body, + }))) } fn parse_function_parameters(&mut self) -> Result, ParseError> { @@ -260,7 +275,7 @@ impl<'a> Parser<'a> { if self.peek_token_is(&Token::Rparen) { self.next_token(); - return Ok(identifiers) + return Ok(identifiers); } self.next_token(); @@ -290,29 +305,51 @@ impl<'a> Parser<'a> { fn infix_fn(&mut self) -> Option { match self.peek_token { - Token::Plus | Token::Minus | Token::Slash | Token::Asterisk | Token::Eq | Token::Neq | Token::Lt | Token::Gt => Some(Parser::parse_infix_expression), + Token::Plus + | Token::Minus + | Token::Slash + | Token::Asterisk + | Token::Eq + | Token::Neq + | Token::Lt + | Token::Gt => Some(Parser::parse_infix_expression), Token::Lparen => Some(Parser::parse_call_expression), Token::Lbracket => Some(Parser::parse_index_expression), _ => None, } } - fn parse_index_expression(parser: &mut Parser<'_>, left: Expression) -> ParseResult { + fn parse_index_expression( + parser: &mut Parser<'_>, + left: Expression, + ) -> ParseResult { parser.next_token(); - let exp = IndexExpression{left, index: parser.parse_expression(Precedence::Lowest)?}; + let exp = IndexExpression { + left, + index: parser.parse_expression(Precedence::Lowest)?, + }; parser.expect_peek(Token::Rbracket)?; Ok(Expression::Index(Box::new(exp))) } - fn parse_call_expression(parser: &mut Parser<'_>, function: Expression) -> ParseResult { + fn parse_call_expression( + parser: &mut Parser<'_>, + function: Expression, + ) -> ParseResult { let arguments = parser.parse_expression_list(Token::Rparen)?; - Ok(Expression::Call(Box::new(CallExpression{function, arguments}))) + Ok(Expression::Call(Box::new(CallExpression { + function, + arguments, + }))) } - fn parse_infix_expression(parser: &mut Parser<'_>, left: Expression) -> ParseResult { + fn parse_infix_expression( + parser: &mut Parser<'_>, + left: Expression, + ) -> ParseResult { let operator = parser.cur_token.clone(); let precedence = parser.cur_precedence(); @@ -320,7 +357,11 @@ impl<'a> Parser<'a> { let right = parser.parse_expression(precedence)?; - Ok(Expression::Infix(Box::new(InfixExpression { operator, left, right }))) + Ok(Expression::Infix(Box::new(InfixExpression { + operator, + left, + right, + }))) } fn parse_boolean(parser: &mut Parser<'_>) -> ParseResult { @@ -328,16 +369,21 @@ impl<'a> Parser<'a> { Token::True => Ok(Expression::Boolean(true)), Token::False => Ok(Expression::Boolean(false)), // we should never hit this since this function is only handed out for tokens matched as boolean - _ => panic!("couldn't parse {:?} to boolean", parser.cur_token) + _ => panic!("couldn't parse {:?} to boolean", parser.cur_token), } } fn parse_identifier_into_identifier_expression(&mut self) -> ParseResult { if let Token::Ident(ref name) = self.cur_token { - return Ok(IdentifierExpression { name: name.to_string() }); + return Ok(IdentifierExpression { + name: name.to_string(), + }); } - Err(format!("unexpected error on identifier parse with {}", self.cur_token)) + Err(format!( + "unexpected error on identifier parse with {}", + self.cur_token + )) } fn parse_identifier(parser: &mut Parser<'_>) -> ParseResult { @@ -345,7 +391,10 @@ impl<'a> Parser<'a> { return Ok(Expression::Identifier(name.to_string())); } - Err(format!("unexpected error on identifier parse with {}", parser.cur_token)) + Err(format!( + "unexpected error on identifier parse with {}", + parser.cur_token + )) } fn parse_string_literal(parser: &mut Parser<'_>) -> ParseResult { @@ -353,7 +402,10 @@ impl<'a> Parser<'a> { return Ok(Expression::String(s.to_string())); } - Err(format!("unexpected error on string parse with {}", parser.cur_token)) + Err(format!( + "unexpected error on string parse with {}", + parser.cur_token + )) } fn parse_integer_literal(parser: &mut Parser<'_>) -> ParseResult { @@ -361,7 +413,10 @@ impl<'a> Parser<'a> { return Ok(Expression::Integer(value)); } - Err(format!("error parsing integer literal {}", parser.cur_token)) + Err(format!( + "error parsing integer literal {}", + parser.cur_token + )) } fn parse_return_statement(&mut self) -> ParseResult { @@ -422,7 +477,10 @@ impl<'a> Parser<'a> { self.next_token(); Ok(()) } - false => Err(format!("expected next token to be {} got {} instead", tok, self.peek_token)) + false => Err(format!( + "expected next token to be {} got {} instead", + tok, self.peek_token + )), } } @@ -456,11 +514,7 @@ let foobar = 838383;"; let prog = setup(input, 3); - let tests = vec![ - "x", - "y", - "foobar", - ]; + let tests = vec!["x", "y", "foobar"]; let mut itr = prog.statements.iter(); @@ -468,9 +522,8 @@ let foobar = 838383;"; match itr.next().unwrap() { Statement::Let(ref l) => { assert_eq!(l.name, t); - - }, - _ => panic!("unknown node") + } + _ => panic!("unknown node"), } } } @@ -481,7 +534,7 @@ let foobar = 838383;"; let exp = let_statement_parse_and_verify(&prog, "y"); match exp { Expression::Boolean(b) => assert_eq!(b, &true), - _ => panic!("expected boolean expression") + _ => panic!("expected boolean expression"), } } @@ -491,18 +544,21 @@ let foobar = 838383;"; let exp = let_statement_parse_and_verify(&prog, "foobar"); match exp { Expression::Identifier(_) => test_identifier(&exp, "y"), - _ => panic!("expected identifier expression") + _ => panic!("expected identifier expression"), } } - fn let_statement_parse_and_verify<'a>(prog: &'a Program, expected_ident: &str) -> &'a Expression { + fn let_statement_parse_and_verify<'a>( + prog: &'a Program, + expected_ident: &str, + ) -> &'a Expression { let stmt = prog.statements.first().unwrap(); match stmt { Statement::Let(stmt) => { assert_eq!(stmt.name.as_str(), expected_ident); - return &stmt.value - }, - stmt => panic!("expected let statement but got {:?}", stmt) + return &stmt.value; + } + stmt => panic!("expected let statement but got {:?}", stmt), } } @@ -528,14 +584,18 @@ let 23432"; "expected next token to be = got 5 instead", "invalid identifier =", "no prefix parse function for = found", - "invalid identifier 23432" + "invalid identifier 23432", ]; let mut itr = errors.iter(); for err in expected_errors { let message = itr.next().unwrap(); - assert_eq!(message, err, "expected error '{}' but got '{}'", err, message) + assert_eq!( + message, err, + "expected error '{}' but got '{}'", + err, message + ) } } } @@ -564,7 +624,7 @@ return 993322;"; let exp = return_statement_parse_and_verify(&prog); match exp { Expression::Boolean(b) => assert_eq!(b, &true), - _ => panic!("expected boolean expression") + _ => panic!("expected boolean expression"), } } @@ -574,17 +634,15 @@ return 993322;"; let exp = return_statement_parse_and_verify(&prog); match exp { Expression::Identifier(_) => test_identifier(&exp, "foobar"), - _ => panic!("expected identifier expression") + _ => panic!("expected identifier expression"), } } fn return_statement_parse_and_verify<'a>(prog: &'a Program) -> &'a Expression { let stmt = prog.statements.first().unwrap(); match stmt { - Statement::Return(stmt) => { - return &stmt.value - }, - stmt => panic!("expected return statement but got {:?}", stmt) + Statement::Return(stmt) => return &stmt.value, + stmt => panic!("expected return statement but got {:?}", stmt), } } @@ -606,8 +664,10 @@ return 993322;"; let exp = unwrap_expression(&prog); match exp { - Expression::Integer(int) => assert_eq!(*int, 5, "expected value to be 5 but got {}", int), - exp => panic!("expected integer literal expression but got {:?}", exp) + Expression::Integer(int) => { + assert_eq!(*int, 5, "expected value to be 5 but got {}", int) + } + exp => panic!("expected integer literal expression but got {:?}", exp), } } @@ -621,8 +681,16 @@ return 993322;"; // TODO: add tests for boolean prefix expressions like !true; and !false; let tests = vec![ - Test { input: "!5;", operator: Token::Bang, value: 5 }, - Test { input: "-15;", operator: Token::Minus, value: 15 }, + Test { + input: "!5;", + operator: Token::Bang, + value: 5, + }, + Test { + input: "-15;", + operator: Token::Minus, + value: 15, + }, ]; for t in tests { @@ -631,10 +699,14 @@ return 993322;"; match exp { Expression::Prefix(prefix) => { - assert_eq!(t.operator, prefix.operator, "expected {} operator but got {}", t.operator, prefix.operator); + assert_eq!( + t.operator, prefix.operator, + "expected {} operator but got {}", + t.operator, prefix.operator + ); test_integer_literal(&prefix.right, t.value); } - exp => panic!("expected prefix expression but got {:?}", exp) + exp => panic!("expected prefix expression but got {:?}", exp), } } } @@ -649,14 +721,54 @@ return 993322;"; } let tests = vec![ - Test { input: "5 + 5;", left_value: 5, operator: Token::Plus, right_value: 5 }, - Test { input: "5 - 5;", left_value: 5, operator: Token::Minus, right_value: 5 }, - Test { input: "5 * 5;", left_value: 5, operator: Token::Asterisk, right_value: 5 }, - Test { input: "5 / 5;", left_value: 5, operator: Token::Slash, right_value: 5 }, - Test { input: "5 > 5;", left_value: 5, operator: Token::Gt, right_value: 5 }, - Test { input: "5 < 5;", left_value: 5, operator: Token::Lt, right_value: 5 }, - Test { input: "5 == 5;", left_value: 5, operator: Token::Eq, right_value: 5 }, - Test { input: "5 != 5;", left_value: 5, operator: Token::Neq, right_value: 5 }, + Test { + input: "5 + 5;", + left_value: 5, + operator: Token::Plus, + right_value: 5, + }, + Test { + input: "5 - 5;", + left_value: 5, + operator: Token::Minus, + right_value: 5, + }, + Test { + input: "5 * 5;", + left_value: 5, + operator: Token::Asterisk, + right_value: 5, + }, + Test { + input: "5 / 5;", + left_value: 5, + operator: Token::Slash, + right_value: 5, + }, + Test { + input: "5 > 5;", + left_value: 5, + operator: Token::Gt, + right_value: 5, + }, + Test { + input: "5 < 5;", + left_value: 5, + operator: Token::Lt, + right_value: 5, + }, + Test { + input: "5 == 5;", + left_value: 5, + operator: Token::Eq, + right_value: 5, + }, + Test { + input: "5 != 5;", + left_value: 5, + operator: Token::Neq, + right_value: 5, + }, ]; for t in tests { @@ -665,11 +777,15 @@ return 993322;"; match exp { Expression::Infix(infix) => { - assert_eq!(t.operator, infix.operator, "expected {} operator but got {}", t.operator, infix.operator); + assert_eq!( + t.operator, infix.operator, + "expected {} operator but got {}", + t.operator, infix.operator + ); test_integer_literal(&infix.left, t.left_value); test_integer_literal(&infix.right, t.right_value); } - exp => panic!("expected prefix expression but got {:?}", exp) + exp => panic!("expected prefix expression but got {:?}", exp), } } } @@ -684,9 +800,24 @@ return 993322;"; } let tests = vec![ - Test { input: "true == true", left_value: true, operator: Token::Eq, right_value: true }, - Test { input: "true != false", left_value: true, operator: Token::Neq, right_value: false }, - Test { input: "false == false", left_value: false, operator: Token::Eq, right_value: false }, + Test { + input: "true == true", + left_value: true, + operator: Token::Eq, + right_value: true, + }, + Test { + input: "true != false", + left_value: true, + operator: Token::Neq, + right_value: false, + }, + Test { + input: "false == false", + left_value: false, + operator: Token::Eq, + right_value: false, + }, ]; for t in tests { @@ -695,11 +826,15 @@ return 993322;"; match exp { Expression::Infix(infix) => { - assert_eq!(t.operator, infix.operator, "expected {} operator but got {}", t.operator, infix.operator); + assert_eq!( + t.operator, infix.operator, + "expected {} operator but got {}", + t.operator, infix.operator + ); test_boolean_literal(&infix.left, t.left_value); test_boolean_literal(&infix.right, t.right_value); } - exp => panic!("expected infix expression but got {:?}", exp) + exp => panic!("expected infix expression but got {:?}", exp), } } } @@ -712,38 +847,120 @@ return 993322;"; } let tests = vec![ - Test { input: "-a * b", expected: "((-a) * b)" }, - Test { input: "!-a", expected: "(!(-a))" }, - Test { input: "a + b + c", expected: "((a + b) + c)" }, - Test { input: "a + b - c", expected: "((a + b) - c)" }, - Test { input: "a * b * c", expected: "((a * b) * c)" }, - Test { input: "a * b / c", expected: "((a * b) / c)" }, - Test { input: "a + b / c", expected: "(a + (b / c))" }, - Test { input: "a + b * c + d / e - f", expected: "(((a + (b * c)) + (d / e)) - f)" }, - Test { input: "3 + 4; -5 * 5", expected: "(3 + 4)((-5) * 5)" }, - Test { input: "5 > 4 == 3 < 4", expected: "((5 > 4) == (3 < 4))" }, - Test { input: "5 < 4 != 3 > 4", expected: "((5 < 4) != (3 > 4))" }, - Test { input: "3 + 4 * 5 == 3 * 1 + 4 * 5", expected: "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))" }, - Test { input: "true", expected: "true" }, - Test { input: "false", expected: "false" }, - Test { input: "3 > 5 == false", expected: "((3 > 5) == false)" }, - Test { input: "3 < 5 == true", expected: "((3 < 5) == true)" }, - Test { input: "1 + (2 + 3) + 4", expected: "((1 + (2 + 3)) + 4)" }, - Test { input: "(5 + 5) * 2", expected: "((5 + 5) * 2)" }, - Test { input: "2 / (5 + 5)", expected: "(2 / (5 + 5))" }, - Test { input: "-(5 + 5)", expected: "(-(5 + 5))" }, - Test { input: "!(true == true)", expected: "(!(true == true))" }, - Test { input: "a + add(b * c) + d", expected: "((a + add((b * c))) + d)" }, - Test { input: "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))", expected: "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))" }, - Test { input: "add(a + b + c * d / f + g)", expected: "add((((a + b) + ((c * d) / f)) + g))" }, - Test { input: "a * [1, 2, 3, 4][b * c] * d", expected: "((a * ([1, 2, 3, 4][(b * c)])) * d)" }, - Test { input: "add(a * b[2], b[1], 2 * [1, 2][1])", expected: "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))" }, + Test { + input: "-a * b", + expected: "((-a) * b)", + }, + Test { + input: "!-a", + expected: "(!(-a))", + }, + Test { + input: "a + b + c", + expected: "((a + b) + c)", + }, + Test { + input: "a + b - c", + expected: "((a + b) - c)", + }, + Test { + input: "a * b * c", + expected: "((a * b) * c)", + }, + Test { + input: "a * b / c", + expected: "((a * b) / c)", + }, + Test { + input: "a + b / c", + expected: "(a + (b / c))", + }, + Test { + input: "a + b * c + d / e - f", + expected: "(((a + (b * c)) + (d / e)) - f)", + }, + Test { + input: "3 + 4; -5 * 5", + expected: "(3 + 4)((-5) * 5)", + }, + Test { + input: "5 > 4 == 3 < 4", + expected: "((5 > 4) == (3 < 4))", + }, + Test { + input: "5 < 4 != 3 > 4", + expected: "((5 < 4) != (3 > 4))", + }, + Test { + input: "3 + 4 * 5 == 3 * 1 + 4 * 5", + expected: "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))", + }, + Test { + input: "true", + expected: "true", + }, + Test { + input: "false", + expected: "false", + }, + Test { + input: "3 > 5 == false", + expected: "((3 > 5) == false)", + }, + Test { + input: "3 < 5 == true", + expected: "((3 < 5) == true)", + }, + Test { + input: "1 + (2 + 3) + 4", + expected: "((1 + (2 + 3)) + 4)", + }, + Test { + input: "(5 + 5) * 2", + expected: "((5 + 5) * 2)", + }, + Test { + input: "2 / (5 + 5)", + expected: "(2 / (5 + 5))", + }, + Test { + input: "-(5 + 5)", + expected: "(-(5 + 5))", + }, + Test { + input: "!(true == true)", + expected: "(!(true == true))", + }, + Test { + input: "a + add(b * c) + d", + expected: "((a + add((b * c))) + d)", + }, + Test { + input: "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))", + expected: "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))", + }, + Test { + input: "add(a + b + c * d / f + g)", + expected: "add((((a + b) + ((c * d) / f)) + g))", + }, + Test { + input: "a * [1, 2, 3, 4][b * c] * d", + expected: "((a * ([1, 2, 3, 4][(b * c)])) * d)", + }, + Test { + input: "add(a * b[2], b[1], 2 * [1, 2][1])", + expected: "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))", + }, ]; for t in tests { let prog = setup(t.input, 0).to_string(); - assert_eq!(t.expected, prog, "expected '{}' but got '{}'", t.expected, prog) + assert_eq!( + t.expected, prog, + "expected '{}' but got '{}'", + t.expected, prog + ) } } @@ -755,8 +972,14 @@ return 993322;"; } let tests = vec![ - Test { input: "true;", expected: true }, - Test { input: "false;", expected: false }, + Test { + input: "true;", + expected: true, + }, + Test { + input: "false;", + expected: false, + }, ]; for t in tests { @@ -778,16 +1001,20 @@ return 993322;"; Expression::If(ifexpr) => { test_if_condition(&ifexpr.condition, Token::Lt, "x", "y"); - assert_eq!(ifexpr.consequence.statements.len(), 1, "expected only 1 statement"); + assert_eq!( + ifexpr.consequence.statements.len(), + 1, + "expected only 1 statement" + ); match ifexpr.consequence.statements.first().unwrap() { Statement::Expression(stmt) => test_identifier(&stmt.expression, "x"), - stmt => panic!("expected expression statement but got {:?}", stmt) + stmt => panic!("expected expression statement but got {:?}", stmt), } if let Some(stmt) = &ifexpr.alternative { panic!("expected alternative to be None but got {:?}", stmt) } } - _ => panic!("expected if expression but got {:?}", exp) + _ => panic!("expected if expression but got {:?}", exp), } } @@ -805,20 +1032,20 @@ return 993322;"; assert_eq!(ifexpr.consequence.statements.len(), 1); match &ifexpr.consequence.statements.first().unwrap() { Statement::Expression(stmt) => test_identifier(&stmt.expression, "x"), - stmt => panic!("expected expression statement but got {:?}", stmt) + stmt => panic!("expected expression statement but got {:?}", stmt), } if let Some(stmt) = &ifexpr.alternative { assert_eq!(stmt.statements.len(), 1); match stmt.statements.first().unwrap() { Statement::Expression(stmt) => test_identifier(&stmt.expression, "y"), - stmt => panic!("expected expression statement but got {:?}", stmt) + stmt => panic!("expected expression statement but got {:?}", stmt), } } else { panic!("expected alternative block") } } - _ => panic!("expected if expression but got {:?}", exp) + _ => panic!("expected if expression but got {:?}", exp), } } @@ -830,24 +1057,39 @@ return 993322;"; match exp { Expression::Function(func) => { - assert_eq!(2, func.parameters.len(), "expected 2 parameters but got {:?}", func.parameters); + assert_eq!( + 2, + func.parameters.len(), + "expected 2 parameters but got {:?}", + func.parameters + ); assert_eq!(func.parameters.first().unwrap().name, "x"); assert_eq!(func.parameters.last().unwrap().name, "y"); - assert_eq!(1, func.body.statements.len(), "expecte 1 body statement but got {:?}", func.body.statements); + assert_eq!( + 1, + func.body.statements.len(), + "expecte 1 body statement but got {:?}", + func.body.statements + ); match func.body.statements.first().unwrap() { Statement::Expression(stmt) => match &stmt.expression { Expression::Infix(infix) => { - assert_eq!(infix.operator, Token::Plus, "expected + but got {}", infix.operator); + assert_eq!( + infix.operator, + Token::Plus, + "expected + but got {}", + infix.operator + ); test_identifier(&infix.left, "x"); test_identifier(&infix.right, "y"); - }, - _ => panic!("expected infix expression but got {:?}", stmt.expression) + } + _ => panic!("expected infix expression but got {:?}", stmt.expression), }, - stmt => panic!("expected expression statement but got {:?}", stmt) + stmt => panic!("expected expression statement but got {:?}", stmt), } - }, - _ => panic!("{} is not a function literal", exp) + } + _ => panic!("{} is not a function literal", exp), } } @@ -859,9 +1101,18 @@ return 993322;"; } let tests = vec![ - Test{input: "fn() {};", expected_params: vec![]}, - Test{input: "fn(x) {};", expected_params: vec!["x"]}, - Test{input: "fn(x, y, z) {};", expected_params: vec!["x", "y", "z"]}, + Test { + input: "fn() {};", + expected_params: vec![], + }, + Test { + input: "fn(x) {};", + expected_params: vec!["x"], + }, + Test { + input: "fn(x, y, z) {};", + expected_params: vec!["x", "y", "z"], + }, ]; for t in tests { @@ -876,8 +1127,8 @@ return 993322;"; let expected_param = params.next().unwrap(); assert_eq!(expected_param, param.name.as_str()); } - }, - _ => panic!("{:?} not a function literal", exp) + } + _ => panic!("{:?} not a function literal", exp), } } } @@ -896,8 +1147,8 @@ return 993322;"; test_integer_literal(&args.next().unwrap(), 1); test_infix(&args.next().unwrap(), 2, Token::Asterisk, 3); test_infix(&args.next().unwrap(), 4, Token::Plus, 5) - }, - _ => panic!("{} is not a call expression", exp) + } + _ => panic!("{} is not a call expression", exp), } } @@ -910,9 +1161,21 @@ return 993322;"; } let tests = vec![ - Test{input: "add();", expected_ident: "add", expected_args: vec![]}, - Test{input: "add(1);", expected_ident: "add", expected_args: vec!["1"]}, - Test{input: "add(1, 2 * 3, 4 + 5);", expected_ident: "add", expected_args: vec!["1", "(2 * 3)", "(4 + 5)"]}, + Test { + input: "add();", + expected_ident: "add", + expected_args: vec![], + }, + Test { + input: "add(1);", + expected_ident: "add", + expected_args: vec!["1"], + }, + Test { + input: "add(1, 2 * 3, 4 + 5);", + expected_ident: "add", + expected_args: vec!["1", "(2 * 3)", "(4 + 5)"], + }, ]; for t in tests { @@ -927,8 +1190,8 @@ return 993322;"; for a in t.expected_args { assert_eq!(a.to_string(), args.next().unwrap().to_string()); } - }, - _ => panic!("{:?} is not a call expression", exp) + } + _ => panic!("{:?} is not a call expression", exp), } } } @@ -941,7 +1204,7 @@ return 993322;"; match exp { Expression::String(s) => assert_eq!(s, "hello world"), - _ => panic!("expected string literal but got {:?}", exp) + _ => panic!("expected string literal but got {:?}", exp), } } @@ -956,8 +1219,8 @@ return 993322;"; test_integer_literal(a.elements.first().unwrap(), 1); test_infix(a.elements.get(1).unwrap(), 2, Token::Asterisk, 2); test_infix(a.elements.last().unwrap(), 3, Token::Plus, 3); - }, - _ => panic!("expected array literal but got {:?}", exp) + } + _ => panic!("expected array literal but got {:?}", exp), } } @@ -971,8 +1234,8 @@ return 993322;"; Expression::Index(i) => { test_identifier(&i.left, "myArray"); test_infix(&i.index, 1, Token::Plus, 1); - }, - _ => panic!("expected an index expression but got {:?}", exp) + } + _ => panic!("expected an index expression but got {:?}", exp), } } @@ -1006,8 +1269,8 @@ return 993322;"; _ => panic!("expected key to be a string and value to be an int but got {:?} and {:?}", k, v) } } - }, - _ => panic!("expected a hash literal but got {:?}", exp) + } + _ => panic!("expected a hash literal but got {:?}", exp), } } @@ -1034,8 +1297,8 @@ return 993322;"; _ => panic!("expected key to be a string and value to be an infix expression but got {:?} and {:?}", k, v) } } - }, - _ => panic!("expected a hash literal but got {:?}", exp) + } + _ => panic!("expected a hash literal but got {:?}", exp), } } @@ -1048,19 +1311,23 @@ return 993322;"; match exp { Expression::Hash(h) => { assert_eq!(h.pairs.len(), 0) - }, - _ => panic!("expected a hash literal but got {:?}", exp) + } + _ => panic!("expected a hash literal but got {:?}", exp), } } fn test_infix(exp: &Expression, left: i64, op: Token, right: i64) { match exp { Expression::Infix(infix) => { - assert_eq!(op, infix.operator, "expected {} operator but got {}", op, infix.operator); + assert_eq!( + op, infix.operator, + "expected {} operator but got {}", + op, infix.operator + ); test_integer_literal(&infix.left, left); test_integer_literal(&infix.right, right); } - exp => panic!("expected prefix expression but got {:?}", exp) + exp => panic!("expected prefix expression but got {:?}", exp), } } @@ -1073,7 +1340,7 @@ return 993322;"; panic!("expected {} operator but got {}", operator, infix.operator) } } - _ => panic!("expected infix expression but got {:?}", exp) + _ => panic!("expected infix expression but got {:?}", exp), } } @@ -1083,7 +1350,10 @@ return 993322;"; let prog = p.parse_program().unwrap(); if stmt_count != 0 && prog.statements.len() != stmt_count { - panic!("expected 1 statement for '{}' but got {:?}", input, prog.statements) + panic!( + "expected 1 statement for '{}' but got {:?}", + input, prog.statements + ) } prog @@ -1092,29 +1362,34 @@ return 993322;"; fn unwrap_expression(prog: &Program) -> &Expression { match prog.statements.first().unwrap() { Statement::Expression(stmt) => &stmt.expression, - stmt => panic!("{:?} isn't an expression statement", stmt) + stmt => panic!("{:?} isn't an expression statement", stmt), } } fn test_integer_literal(exp: &Expression, value: i64) { match exp { - Expression::Integer(int) => assert_eq!(value, *int, "expected {} but got {}", value, int), - _ => panic!("expected integer literal {} but got {:?}", value, exp) + Expression::Integer(int) => { + assert_eq!(value, *int, "expected {} but got {}", value, int) + } + _ => panic!("expected integer literal {} but got {:?}", value, exp), } } fn test_boolean_literal(exp: &Expression, value: bool) { match exp { - Expression::Boolean(val) => assert_eq!(value, *val, "expected {} but got {}", value, val), - _ => panic!("expected boolean literal {} but got {:?}", value, exp) + Expression::Boolean(val) => { + assert_eq!(value, *val, "expected {} but got {}", value, val) + } + _ => panic!("expected boolean literal {} but got {:?}", value, exp), } } fn test_identifier(exp: &Expression, value: &str) { match exp { - Expression::Identifier(ident) => assert_eq!(value, ident, "expected {} but got {}", value, ident), - _ => panic!("expected identifier expression but got {:?}", exp) + Expression::Identifier(ident) => { + assert_eq!(value, ident, "expected {} but got {}", value, ident) + } + _ => panic!("expected identifier expression but got {:?}", exp), } } } - diff --git a/src/repl.rs b/src/repl.rs index b62d99c..287c6f2 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,18 +1,17 @@ -use std::io; -use crate::parser; use crate::compiler::{Compiler, SymbolTable}; +use crate::parser; use crate::vm; +use std::io; pub fn start(mut reader: R, mut writer: W) -> io::Result<()> { - #![allow(warnings)] let mut constants = vec![]; let mut globals = vm::VM::new_globals(); let mut symbol_table = SymbolTable::new(); symbol_table.load_builtins(); loop { - writer.write(b"> "); - writer.flush(); + writer.write(b"> ")?; + writer.flush()?; let mut line = String::new(); reader.read_line(&mut line)?; @@ -21,26 +20,33 @@ pub fn start(mut reader: R, mut writer: W) -> io:: let mut compiler = Compiler::new_with_state(symbol_table, constants); match compiler.compile(node) { - Ok(bytecode) => { - let mut vm = vm::VM::new_with_global_store(&compiler.constants, compiler.current_instructions().clone(), globals); + Ok(_bytecode) => { + let mut vm = vm::VM::new_with_global_store( + &compiler.constants, + compiler.current_instructions().to_vec(), + globals, + ); vm.run(); - write!(writer, "{:?}\n", vm.last_popped_stack_elem().unwrap().inspect()); + write!( + writer, + "{:?}\n", + vm.last_popped_stack_elem().unwrap().inspect() + )?; globals = vm.globals; - }, + } Err(e) => { - write!(writer, "error: {}\n", e.message); - }, + write!(writer, "error: {}\n", e.message)?; + } } symbol_table = compiler.symbol_table; constants = compiler.constants; - }, + } Err(errors) => { for err in errors { - write!(writer, "parse errors:\n{}\n", err.to_string()); - }; - }, + write!(writer, "parse errors:\n{}\n", err.to_string())?; + } + } } } - Ok(()) } diff --git a/src/token.rs b/src/token.rs index 7407c74..af79102 100644 --- a/src/token.rs +++ b/src/token.rs @@ -102,4 +102,4 @@ impl Token { _ => false, } } -} \ No newline at end of file +} diff --git a/src/vm.rs b/src/vm.rs index b1efdc1..59bdd2d 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,12 +1,10 @@ -use crate::compiler::Compiler; -use crate::object::{Object, Array, MonkeyHash, CompiledFunction, Builtin, Closure}; -use crate::parser::parse; +use self::byteorder::{BigEndian, ByteOrder}; use crate::code::{Instructions, Op}; +use crate::object::{Array, Builtin, Closure, CompiledFunction, MonkeyHash, Object}; use byteorder; -use self::byteorder::{ByteOrder, BigEndian, ReadBytesExt}; -use std::rc::Rc; use std::borrow::Borrow; use std::collections::HashMap; +use std::rc::Rc; const STACK_SIZE: usize = 2048; const GLOBAL_SIZE: usize = 65536; @@ -43,25 +41,38 @@ impl<'a> VM<'a> { stack.resize(STACK_SIZE, Rc::new(Object::Null)); let mut frames = Vec::with_capacity(MAX_FRAMES); - let empty_frame = Frame{ - cl: Rc::new(Closure{ - func: Rc::new(CompiledFunction{ + let empty_frame = Frame { + cl: Rc::new(Closure { + func: Rc::new(CompiledFunction { instructions: vec![], num_locals: 0, - num_parameters: 0}), - free: vec![]}), + num_parameters: 0, + }), + free: vec![], + }), ip: 24, - base_pointer: 0 + base_pointer: 0, }; frames.resize(MAX_FRAMES, empty_frame); - let main_func = Rc::new(CompiledFunction{instructions, num_locals: 0, num_parameters: 0}); - let main_closure = Rc::new(Closure{func: main_func, free: vec![]}); - let main_frame = Frame{cl: main_closure, ip: 0, base_pointer: 0}; + let main_func = Rc::new(CompiledFunction { + instructions, + num_locals: 0, + num_parameters: 0, + }); + let main_closure = Rc::new(Closure { + func: main_func, + free: vec![], + }); + let main_frame = Frame { + cl: main_closure, + ip: 0, + base_pointer: 0, + }; frames[0] = main_frame; - VM{ + VM { constants, stack, sp: 0, @@ -74,16 +85,20 @@ impl<'a> VM<'a> { pub fn new_globals() -> Vec> { let mut globals = Vec::with_capacity(GLOBAL_SIZE); globals.resize(GLOBAL_SIZE, Rc::new(Object::Null)); - return globals; + globals } - pub fn new_with_global_store(constants: &'a Vec>, instructions: Instructions, globals: Vec>) -> VM<'a> { + pub fn new_with_global_store( + constants: &'a Vec>, + instructions: Instructions, + globals: Vec>, + ) -> VM<'a> { let mut vm = VM::new(constants, instructions); vm.globals = globals; - return vm; + vm } - pub fn last_popped_stack_elem(&self, ) -> Option> { + pub fn last_popped_stack_elem(&self) -> Option> { match self.stack.get(self.sp) { Some(o) => Some(Rc::clone(o)), None => None, @@ -92,7 +107,7 @@ impl<'a> VM<'a> { fn continue_current_frame(&self) -> bool { let frame = &self.frames[self.frames_index]; - return frame.ip < frame.cl.func.instructions.len(); + frame.ip < frame.cl.func.instructions.len() } fn current_ip(&self) -> usize { @@ -116,12 +131,12 @@ impl<'a> VM<'a> { fn read_usize_at(&self, ip: usize) -> usize { let ins = &self.frames[self.frames_index].cl.func.instructions; - BigEndian::read_u16(&ins[ip..ip+2]) as usize + BigEndian::read_u16(&ins[ip..ip + 2]) as usize } fn read_u8_at(&self, ip: usize) -> u8 { let ins = &self.frames[self.frames_index].cl.func.instructions; - *&ins[ip] + ins[ip] } fn set_ip(&mut self, ip: usize) { @@ -129,10 +144,8 @@ impl<'a> VM<'a> { } pub fn run(&mut self) { - let mut ip = 0; - while self.continue_current_frame() { - ip = self.current_ip(); + let ip = self.current_ip(); let op = self.read_op_at(ip); self.set_ip(ip + 1); @@ -143,12 +156,12 @@ impl<'a> VM<'a> { let c = Rc::clone(&self.constants[const_index]); self.push(c) - }, + } Op::Add | Op::Sub | Op::Mul | Op::Div => self.execute_binary_operation(op), Op::GreaterThan | Op::Equal | Op::NotEqual => self.execute_comparison(op), Op::Pop => { self.pop(); - }, + } Op::True => self.push(Rc::new(Object::Bool(true))), Op::False => self.push(Rc::new(Object::Bool(false))), Op::Bang => self.execute_bang_operator(), @@ -156,7 +169,7 @@ impl<'a> VM<'a> { Op::Jump => { let pos = self.read_usize_at(ip + 1); self.set_ip(pos); - }, + } Op::JumpNotTruthy => { let pos = self.read_usize_at(ip + 1); self.set_ip(ip + 3); @@ -165,27 +178,27 @@ impl<'a> VM<'a> { if !is_truthy(&condition) { self.set_ip(pos); } - }, + } Op::Null => self.push(Rc::new(Object::Null)), Op::SetGobal => { let global_index = self.read_usize_at(ip + 1); self.set_ip(ip + 3); self.globals[global_index] = self.pop(); - }, + } Op::SetLocal => { let local_index = self.read_u8_at(ip + 1) as usize; self.set_ip(ip + 2); let base = self.frames[self.frames_index].base_pointer; self.stack[base + local_index] = self.pop(); - }, + } Op::GetGlobal => { let global_index = self.read_usize_at(ip + 1); self.set_ip(ip + 3); self.push(Rc::clone(&self.globals[global_index])); - }, + } Op::GetLocal => { let local_index = self.read_u8_at(ip + 1) as usize; self.set_ip(ip + 2); @@ -201,7 +214,7 @@ impl<'a> VM<'a> { self.sp -= num_elements; self.push(Rc::new(array)); - }, + } Op::Hash => { let num_elements = self.read_usize_at(ip + 1); self.set_ip(ip + 3); @@ -210,48 +223,47 @@ impl<'a> VM<'a> { self.sp -= num_elements; self.push(Rc::new(hash)); - }, + } Op::Index => { let index = self.pop(); let left = self.pop(); self.execute_index_expression(left, index); - }, + } Op::Call => { let num_args = self.read_u8_at(ip + 1) as usize; self.set_ip(ip + 2); self.execute_call(num_args); - }, + } Op::ReturnValue => { let return_value = self.pop(); let base_pointer = self.pop_frame(); self.sp = base_pointer - 1; self.push(return_value); - }, + } Op::Return => { let base_pointer = self.pop_frame(); self.sp = base_pointer - 1; self.push(Rc::new(Object::Null)); - }, + } Op::GetBuiltin => { let builtin_index = self.read_u8_at(ip + 1); let builtin: Builtin = unsafe { ::std::mem::transmute(builtin_index) }; self.set_ip(ip + 2); self.push(Rc::new(Object::Builtin(builtin))); - }, + } Op::Closure => { let const_index = self.read_usize_at(ip + 1); let num_free = self.read_u8_at(ip + 3) as usize; self.set_ip(ip + 4); self.push_closure(const_index, num_free); - }, + } Op::GetFree => { let free_index = self.read_u8_at(ip + 1) as usize; self.set_ip(ip + 2); let obj = &self.frames[self.frames_index].cl.free[free_index]; self.push(Rc::clone(obj)); - }, - _ => panic!("unsupported op {:?}", op), + } } } } @@ -260,34 +272,48 @@ impl<'a> VM<'a> { match &*self.constants[const_index] { Object::CompiledFunction(func) => { let mut free = Vec::with_capacity(num_free); - for obj in &self.stack[self.sp-num_free..self.sp] { + for obj in &self.stack[self.sp - num_free..self.sp] { free.push(Rc::clone(obj)); } - self.sp = self.sp-num_free; - self.push(Rc::new(Object::Closure(Rc::new(Closure{func: Rc::clone(&func), free})))); - }, + self.sp -= num_free; + self.push(Rc::new(Object::Closure(Rc::new(Closure { + func: Rc::clone(func), + free, + })))); + } obj => panic!("not a function: {:?}", obj), } - } fn execute_call(&mut self, num_args: usize) { if let Some(frame) = match &*self.stack[self.sp - 1 - num_args] { - Object::Closure(ref cl) => { - Some(Frame{cl: Rc::clone(cl), ip: 0, base_pointer: self.sp - num_args}) - }, + Object::Closure(ref cl) => Some(Frame { + cl: Rc::clone(cl), + ip: 0, + base_pointer: self.sp - num_args, + }), _ => None, - } { self.call_closure(frame, num_args); } else if let Some(builtin) = match &*self.stack[self.sp - 1 - num_args] { + } { + self.call_closure(frame, num_args); + } else if let Some(builtin) = match &*self.stack[self.sp - 1 - num_args] { Object::Builtin(builtin) => Some(builtin), _ => None, - } { self.call_builtin(*builtin, num_args) } else { - panic!("called non-function {:?}", self.stack[self.sp - 1 - num_args]); + } { + self.call_builtin(*builtin, num_args) + } else { + panic!( + "called non-function {:?}", + self.stack[self.sp - 1 - num_args] + ); } } fn call_closure(&mut self, frame: Frame, num_args: usize) { if num_args != frame.cl.func.num_parameters { - panic!("function expects {} arguments but got {}", frame.cl.func.num_parameters, num_args); + panic!( + "function expects {} arguments but got {}", + frame.cl.func.num_parameters, num_args + ); } let sp = frame.base_pointer + frame.cl.func.num_locals; @@ -308,7 +334,7 @@ impl<'a> VM<'a> { fn execute_index_expression(&mut self, left: Rc, index: Rc) { match (&*left, &*index) { - (Object::Array(arr), Object::Int(idx)) => self.execute_array_index(&arr, *idx), + (Object::Array(arr), Object::Int(idx)) => self.execute_array_index(arr, *idx), (Object::Hash(hash), _) => self.execute_hash_index(hash, index), _ => panic!("index not supported on: {:?} {:?}", left, index), } @@ -323,13 +349,11 @@ impl<'a> VM<'a> { fn execute_hash_index(&mut self, hash: &Rc, index: Rc) { match &*index { - Object::String(_) | Object::Int(_) | Object::Bool(_) => { - match hash.pairs.get(&*index) { - Some(obj) => self.push(Rc::clone(obj)), - None => self.push(Rc::new(Object::Null)), - } + Object::String(_) | Object::Int(_) | Object::Bool(_) => match hash.pairs.get(&*index) { + Some(obj) => self.push(Rc::clone(obj)), + None => self.push(Rc::new(Object::Null)), }, - _ => panic!("unusable as hash key: {}", index) + _ => panic!("unusable as hash key: {}", index), } } @@ -339,11 +363,11 @@ impl<'a> VM<'a> { let mut i = start_index; while i < end_index { - elements[i-start_index] = self.stack[i].clone(); + elements[i - start_index] = self.stack[i].clone(); i += 1; } - Object::Array(Rc::new(Array{elements})) + Object::Array(Rc::new(Array { elements })) } fn build_hash(&mut self, start_index: usize, end_index: usize) -> Object { @@ -358,7 +382,7 @@ impl<'a> VM<'a> { i += 2; } - Object::Hash(Rc::new(MonkeyHash{pairs: hash})) + Object::Hash(Rc::new(MonkeyHash { pairs: hash })) } fn execute_binary_operation(&mut self, op: Op) { @@ -372,20 +396,20 @@ impl<'a> VM<'a> { Op::Sub => left - right, Op::Mul => left * right, Op::Div => left / right, - _ => panic!("unsupported operator in integer binary operation {:?}", op) + _ => panic!("unsupported operator in integer binary operation {:?}", op), }; self.push(Rc::new(Object::Int(result))); - }, + } (Object::String(left), Object::String(right)) => { let mut result = left.clone(); match op { - Op::Add => result.push_str(&right), - _ => panic!("unsupported operator in string binary operation {:?}", op) + Op::Add => result.push_str(right), + _ => panic!("unsupported operator in string binary operation {:?}", op), }; self.push(Rc::new(Object::String(result))); - }, + } _ => panic!("unable to {:?} {:?} and {:?}", op, left, right), } } @@ -400,20 +424,20 @@ impl<'a> VM<'a> { Op::Equal => left == right, Op::NotEqual => left != right, Op::GreaterThan => left > right, - _ => panic!("unsupported operator in comparison {:?}", op) + _ => panic!("unsupported operator in comparison {:?}", op), }; self.push(Rc::new(Object::Bool(result))); - }, + } (Object::Bool(left), Object::Bool(right)) => { let result = match op { Op::Equal => left == right, Op::NotEqual => left != right, - _ => panic!("unsupported operator in comparison {:?}", op) + _ => panic!("unsupported operator in comparison {:?}", op), }; self.push(Rc::new(Object::Bool(result))); - }, + } _ => panic!("unable to {:?} {:?} and {:?}", op, left, right), } } @@ -434,7 +458,7 @@ impl<'a> VM<'a> { match op.borrow() { Object::Int(int) => self.push(Rc::new(Object::Int(-*int))), - _ => panic!("unsupported type for negation {:?}", op) + _ => panic!("unsupported type for negation {:?}", op), } } @@ -463,9 +487,12 @@ fn is_truthy(obj: &Rc) -> bool { #[cfg(test)] mod test { + use crate::compiler::Compiler; + use crate::parser::parse; + use super::*; - use std::rc::Rc; use std::collections::HashMap; + use std::rc::Rc; struct VMTestCase<'a> { input: &'a str, @@ -475,23 +502,74 @@ mod test { #[test] fn integer_arithmetic() { let tests = vec![ - VMTestCase{input: "1", expected: Object::Int(1)}, - VMTestCase{input: "2", expected: Object::Int(2)}, - VMTestCase{input: "1 + 2", expected: Object::Int(3)}, - VMTestCase{input: "1 - 2", expected: Object::Int(-1)}, - VMTestCase{input: "1 * 2", expected: Object::Int(2)}, - VMTestCase{input: "4 / 2", expected: Object::Int(2)}, - VMTestCase{input: "50 / 2 * 2 + 10 - 5", expected: Object::Int(55)}, - VMTestCase{input: "5 * (2 + 10)", expected: Object::Int(60)}, - VMTestCase{input: "5 + 5 + 5 + 5 - 10", expected: Object::Int(10)}, - VMTestCase{input: "2 * 2 * 2 * 2 * 2", expected: Object::Int(32)}, - VMTestCase{input: "5 * 2 + 10", expected: Object::Int(20)}, - VMTestCase{input: "5 + 2 * 10", expected: Object::Int(25)}, - VMTestCase{input: "5 * (2 + 10)", expected: Object::Int(60)}, - VMTestCase{input: "-5", expected: Object::Int(-5)}, - VMTestCase{input: "-10", expected: Object::Int(-10)}, - VMTestCase{input: "-50 + 100 + -50", expected: Object::Int(0)}, - VMTestCase{input: "(5 + 10 * 2 + 15 / 3) * 2 + -10", expected: Object::Int(50)}, + VMTestCase { + input: "1", + expected: Object::Int(1), + }, + VMTestCase { + input: "2", + expected: Object::Int(2), + }, + VMTestCase { + input: "1 + 2", + expected: Object::Int(3), + }, + VMTestCase { + input: "1 - 2", + expected: Object::Int(-1), + }, + VMTestCase { + input: "1 * 2", + expected: Object::Int(2), + }, + VMTestCase { + input: "4 / 2", + expected: Object::Int(2), + }, + VMTestCase { + input: "50 / 2 * 2 + 10 - 5", + expected: Object::Int(55), + }, + VMTestCase { + input: "5 * (2 + 10)", + expected: Object::Int(60), + }, + VMTestCase { + input: "5 + 5 + 5 + 5 - 10", + expected: Object::Int(10), + }, + VMTestCase { + input: "2 * 2 * 2 * 2 * 2", + expected: Object::Int(32), + }, + VMTestCase { + input: "5 * 2 + 10", + expected: Object::Int(20), + }, + VMTestCase { + input: "5 + 2 * 10", + expected: Object::Int(25), + }, + VMTestCase { + input: "5 * (2 + 10)", + expected: Object::Int(60), + }, + VMTestCase { + input: "-5", + expected: Object::Int(-5), + }, + VMTestCase { + input: "-10", + expected: Object::Int(-10), + }, + VMTestCase { + input: "-50 + 100 + -50", + expected: Object::Int(0), + }, + VMTestCase { + input: "(5 + 10 * 2 + 15 / 3) * 2 + -10", + expected: Object::Int(50), + }, ]; run_vm_tests(tests); @@ -500,32 +578,110 @@ mod test { #[test] fn boolean_expressions() { let tests = vec![ - VMTestCase{input: "true", expected: Object::Bool(true)}, - VMTestCase{input: "false", expected: Object::Bool(false)}, - VMTestCase{input: "1 < 2", expected: Object::Bool(true)}, - VMTestCase{input: "1 > 2", expected: Object::Bool(false)}, - VMTestCase{input: "1 < 1", expected: Object::Bool(false)}, - VMTestCase{input: "1 > 1", expected: Object::Bool(false)}, - VMTestCase{input: "1 == 1", expected: Object::Bool(true)}, - VMTestCase{input: "1 != 1", expected: Object::Bool(false)}, - VMTestCase{input: "1 == 2", expected: Object::Bool(false)}, - VMTestCase{input: "1 != 2", expected: Object::Bool(true)}, - VMTestCase{input: "true == true", expected: Object::Bool(true)}, - VMTestCase{input: "false == false", expected: Object::Bool(true)}, - VMTestCase{input: "true == false", expected: Object::Bool(false)}, - VMTestCase{input: "true != false", expected: Object::Bool(true)}, - VMTestCase{input: "false != true", expected: Object::Bool(true)}, - VMTestCase{input: "(1 < 2) == true", expected: Object::Bool(true)}, - VMTestCase{input: "(1 < 2) == false", expected: Object::Bool(false)}, - VMTestCase{input: "(1 > 2) == true", expected: Object::Bool(false)}, - VMTestCase{input: "(1 > 2) == false", expected: Object::Bool(true)}, - VMTestCase{input: "!true", expected: Object::Bool(false)}, - VMTestCase{input: "!false", expected: Object::Bool(true)}, - VMTestCase{input: "!5", expected: Object::Bool(false)}, - VMTestCase{input: "!!true", expected: Object::Bool(true)}, - VMTestCase{input: "!!false", expected: Object::Bool(false)}, - VMTestCase{input: "!!5", expected: Object::Bool(true)}, - VMTestCase{input: "!(if (false) { 5; })", expected: Object::Bool(true)}, + VMTestCase { + input: "true", + expected: Object::Bool(true), + }, + VMTestCase { + input: "false", + expected: Object::Bool(false), + }, + VMTestCase { + input: "1 < 2", + expected: Object::Bool(true), + }, + VMTestCase { + input: "1 > 2", + expected: Object::Bool(false), + }, + VMTestCase { + input: "1 < 1", + expected: Object::Bool(false), + }, + VMTestCase { + input: "1 > 1", + expected: Object::Bool(false), + }, + VMTestCase { + input: "1 == 1", + expected: Object::Bool(true), + }, + VMTestCase { + input: "1 != 1", + expected: Object::Bool(false), + }, + VMTestCase { + input: "1 == 2", + expected: Object::Bool(false), + }, + VMTestCase { + input: "1 != 2", + expected: Object::Bool(true), + }, + VMTestCase { + input: "true == true", + expected: Object::Bool(true), + }, + VMTestCase { + input: "false == false", + expected: Object::Bool(true), + }, + VMTestCase { + input: "true == false", + expected: Object::Bool(false), + }, + VMTestCase { + input: "true != false", + expected: Object::Bool(true), + }, + VMTestCase { + input: "false != true", + expected: Object::Bool(true), + }, + VMTestCase { + input: "(1 < 2) == true", + expected: Object::Bool(true), + }, + VMTestCase { + input: "(1 < 2) == false", + expected: Object::Bool(false), + }, + VMTestCase { + input: "(1 > 2) == true", + expected: Object::Bool(false), + }, + VMTestCase { + input: "(1 > 2) == false", + expected: Object::Bool(true), + }, + VMTestCase { + input: "!true", + expected: Object::Bool(false), + }, + VMTestCase { + input: "!false", + expected: Object::Bool(true), + }, + VMTestCase { + input: "!5", + expected: Object::Bool(false), + }, + VMTestCase { + input: "!!true", + expected: Object::Bool(true), + }, + VMTestCase { + input: "!!false", + expected: Object::Bool(false), + }, + VMTestCase { + input: "!!5", + expected: Object::Bool(true), + }, + VMTestCase { + input: "!(if (false) { 5; })", + expected: Object::Bool(true), + }, ]; run_vm_tests(tests); @@ -534,16 +690,46 @@ mod test { #[test] fn conditionals() { let tests = vec![ - VMTestCase{input: "if (true) { 10 }", expected: Object::Int(10)}, - VMTestCase{input: "if (true) { 10 } else { 20 }", expected: Object::Int(10)}, - VMTestCase{input: "if (false) { 10 } else { 20 }", expected: Object::Int(20)}, - VMTestCase{input: "if (1) { 10 }", expected: Object::Int(10)}, - VMTestCase{input: "if (1 < 2) { 10 }", expected: Object::Int(10)}, - VMTestCase{input: "if (1 < 2) { 10 } else { 20 }", expected: Object::Int(10)}, - VMTestCase{input: "if (1 > 2) { 10 } else { 20 }", expected: Object::Int(20)}, - VMTestCase{input: "if (1 > 2) { 10 }", expected: Object::Null}, - VMTestCase{input: "if (false) { 10 }", expected: Object::Null}, - VMTestCase{input: "if ((if (false) { 10 })) { 10 } else { 20 }", expected: Object::Int(20)}, + VMTestCase { + input: "if (true) { 10 }", + expected: Object::Int(10), + }, + VMTestCase { + input: "if (true) { 10 } else { 20 }", + expected: Object::Int(10), + }, + VMTestCase { + input: "if (false) { 10 } else { 20 }", + expected: Object::Int(20), + }, + VMTestCase { + input: "if (1) { 10 }", + expected: Object::Int(10), + }, + VMTestCase { + input: "if (1 < 2) { 10 }", + expected: Object::Int(10), + }, + VMTestCase { + input: "if (1 < 2) { 10 } else { 20 }", + expected: Object::Int(10), + }, + VMTestCase { + input: "if (1 > 2) { 10 } else { 20 }", + expected: Object::Int(20), + }, + VMTestCase { + input: "if (1 > 2) { 10 }", + expected: Object::Null, + }, + VMTestCase { + input: "if (false) { 10 }", + expected: Object::Null, + }, + VMTestCase { + input: "if ((if (false) { 10 })) { 10 } else { 20 }", + expected: Object::Int(20), + }, ]; run_vm_tests(tests); @@ -552,9 +738,18 @@ mod test { #[test] fn global_let_statements() { let tests = vec![ - VMTestCase{input: "let one = 1; one", expected: Object::Int(1)}, - VMTestCase{input: "let one = 1; let two = 2; one + two", expected: Object::Int(3)}, - VMTestCase{input: "let one = 1; let two = one + one; one + two", expected: Object::Int(3)}, + VMTestCase { + input: "let one = 1; one", + expected: Object::Int(1), + }, + VMTestCase { + input: "let one = 1; let two = 2; one + two", + expected: Object::Int(3), + }, + VMTestCase { + input: "let one = 1; let two = one + one; one + two", + expected: Object::Int(3), + }, ]; run_vm_tests(tests); @@ -563,9 +758,18 @@ mod test { #[test] fn string_expressions() { let tests = vec![ - VMTestCase{input: "\"monkey\"", expected: Object::String("monkey".to_string())}, - VMTestCase{input: "\"mon\" + \"key\"", expected: Object::String("monkey".to_string())}, - VMTestCase{input: "\"mon\" + \"key\" + \"banana\"", expected: Object::String("monkeybanana".to_string())}, + VMTestCase { + input: "\"monkey\"", + expected: Object::String("monkey".to_string()), + }, + VMTestCase { + input: "\"mon\" + \"key\"", + expected: Object::String("monkey".to_string()), + }, + VMTestCase { + input: "\"mon\" + \"key\" + \"banana\"", + expected: Object::String("monkeybanana".to_string()), + }, ]; run_vm_tests(tests); @@ -574,15 +778,13 @@ mod test { #[test] fn array_literals() { let tests = vec![ - VMTestCase{ + VMTestCase { input: "[]", - expected: Object::Array(Rc::new(Array{ - elements: vec![], - })), + expected: Object::Array(Rc::new(Array { elements: vec![] })), }, - VMTestCase{ + VMTestCase { input: "[1, 2, 3]", - expected: Object::Array(Rc::new(Array{ + expected: Object::Array(Rc::new(Array { elements: vec![ Rc::new(Object::Int(1)), Rc::new(Object::Int(2)), @@ -590,9 +792,9 @@ mod test { ], })), }, - VMTestCase{ + VMTestCase { input: "[1 + 2, 3 * 4, 5 + 6]", - expected: Object::Array(Rc::new(Array{ + expected: Object::Array(Rc::new(Array { elements: vec![ Rc::new(Object::Int(3)), Rc::new(Object::Int(12)), @@ -620,17 +822,17 @@ mod test { ); let tests = vec![ - VMTestCase{ + VMTestCase { input: "{}", expected: hash_to_object(HashMap::new()), }, - VMTestCase{ + VMTestCase { input: "{1: 2, 2: 3}", - expected: hash_to_object(map!{1 => 2, 2 => 3}), + expected: hash_to_object(map! {1 => 2, 2 => 3}), }, - VMTestCase{ + VMTestCase { input: "{1 + 1: 2 * 2, 3 + 3: 4 * 4}", - expected: hash_to_object(map!{2 => 4, 6 => 16}), + expected: hash_to_object(map! {2 => 4, 6 => 16}), }, ]; @@ -640,16 +842,46 @@ mod test { #[test] fn index_expressions() { let tests = vec![ - VMTestCase{input: "[1, 2, 3][1]", expected: Object::Int(2)}, - VMTestCase{input: "[1, 2, 3][0 + 2]", expected: Object::Int(3)}, - VMTestCase{input: "[[1, 1, 1]][0][0]", expected: Object::Int(1)}, - VMTestCase{input: "[][0]", expected: Object::Null}, - VMTestCase{input: "[1, 2, 3][99]", expected: Object::Null}, - VMTestCase{input: "[1][-1]", expected: Object::Null}, - VMTestCase{input: "{1: 1, 2: 2}[1]", expected: Object::Int(1)}, - VMTestCase{input: "{1: 1, 2: 2}[2]", expected: Object::Int(2)}, - VMTestCase{input: "{1: 1}[0]", expected: Object::Null}, - VMTestCase{input: "{}[0]", expected: Object::Null}, + VMTestCase { + input: "[1, 2, 3][1]", + expected: Object::Int(2), + }, + VMTestCase { + input: "[1, 2, 3][0 + 2]", + expected: Object::Int(3), + }, + VMTestCase { + input: "[[1, 1, 1]][0][0]", + expected: Object::Int(1), + }, + VMTestCase { + input: "[][0]", + expected: Object::Null, + }, + VMTestCase { + input: "[1, 2, 3][99]", + expected: Object::Null, + }, + VMTestCase { + input: "[1][-1]", + expected: Object::Null, + }, + VMTestCase { + input: "{1: 1, 2: 2}[1]", + expected: Object::Int(1), + }, + VMTestCase { + input: "{1: 1, 2: 2}[2]", + expected: Object::Int(2), + }, + VMTestCase { + input: "{1: 1}[0]", + expected: Object::Null, + }, + VMTestCase { + input: "{}[0]", + expected: Object::Null, + }, ]; run_vm_tests(tests); @@ -658,16 +890,17 @@ mod test { #[test] fn calling_functions_without_arguments() { let tests = vec![ - VMTestCase{ + VMTestCase { input: "let fivePlusTen= fn() { 5 + 10; }; fivePlusTen();", - expected: Object::Int(15) + expected: Object::Int(15), }, - VMTestCase{ + VMTestCase { input: "let one = fn() { 1; }; let two = fn() { 2; }; one() + two();", expected: Object::Int(3), }, - VMTestCase{ - input: "let a = fn() { 1 }; let b = fn() { a() + 1 }; let c = fn() { b() + 1 }; c();", + VMTestCase { + input: + "let a = fn() { 1 }; let b = fn() { a() + 1 }; let c = fn() { b() + 1 }; c();", expected: Object::Int(3), }, ]; @@ -678,14 +911,14 @@ mod test { #[test] fn functions_with_return_statement() { let tests = vec![ - VMTestCase{ + VMTestCase { input: "let earlyExit = fn() { return 99; 100; }; earlyExit();", expected: Object::Int(99), }, - VMTestCase{ + VMTestCase { input: "let earlyExit = fn() { return 99; return 100; }; earlyExit();", expected: Object::Int(99), - } + }, ]; run_vm_tests(tests); @@ -722,27 +955,28 @@ mod test { #[test] fn calling_functions_with_bindings() { let tests = vec![ - VMTestCase{ + VMTestCase { input: "let one = fn() { let one = 1; one }; one();", expected: Object::Int(1), }, - VMTestCase{ - input: "let oneAndTwo = fn() { let one = 1; let two = 2; one + two; }; oneAndTwo();", + VMTestCase { + input: + "let oneAndTwo = fn() { let one = 1; let two = 2; one + two; }; oneAndTwo();", expected: Object::Int(3), }, - VMTestCase{ + VMTestCase { input: "let oneAndTwo = fn() { let one = 1; let two = 2; one + two; }; let threeAndFour = fn() { let three = 3; let four = 4; three + four; }; oneAndTwo() + threeAndFour();", expected: Object::Int(10), }, - VMTestCase{ + VMTestCase { input: "let firstFoobar = fn() { let foobar = 50; foobar; }; let secondFoobar = fn() { let foobar = 100; foobar; }; firstFoobar() + secondFoobar();", expected: Object::Int(150), }, - VMTestCase{ + VMTestCase { input: "let globalSeed = 50; let minusOne = fn() { let num = 1; @@ -754,7 +988,7 @@ mod test { } minusOne() + minusTwo();", expected: Object::Int(97), - } + }, ]; run_vm_tests(tests); @@ -763,15 +997,15 @@ mod test { #[test] fn calling_functions_with_arguments_and_bindings() { let tests = vec![ - VMTestCase{ + VMTestCase { input: "let identity = fn(a) { a; }; identity(4);", expected: Object::Int(4), }, - VMTestCase{ + VMTestCase { input: "let sum = fn(a, b) { a + b; }; sum(1, 2);", expected: Object::Int(3), }, - VMTestCase{ + VMTestCase { input: "let sum = fn(a, b) { let c = a + b; c; @@ -779,7 +1013,7 @@ mod test { sum(1, 2);", expected: Object::Int(3), }, - VMTestCase{ + VMTestCase { input: "let sum = fn(a, b) { let c = a + b; c; @@ -787,7 +1021,7 @@ mod test { sum(1, 2) + sum(3, 4);", expected: Object::Int(10), }, - VMTestCase{ + VMTestCase { input: "let sum = fn(a, b) { let c = a + b; c; @@ -798,7 +1032,7 @@ mod test { outer();", expected: Object::Int(10), }, - VMTestCase{ + VMTestCase { input: "let globalNum = 10; let sum = fn(a, b) { @@ -812,7 +1046,7 @@ mod test { outer() + globalNum;", expected: Object::Int(50), - } + }, ]; run_vm_tests(tests); @@ -821,19 +1055,62 @@ mod test { #[test] fn builtin_functions() { let tests = vec![ - VMTestCase{input: r#"len("")"#, expected: Object::Int(0)}, - VMTestCase{input: r#"len("four")"#, expected: Object::Int(4)}, - VMTestCase{input: r#"len("hello world")"#, expected: Object::Int(11)}, - VMTestCase{input: "len([1, 2, 3])", expected: Object::Int(3)}, - VMTestCase{input: "len([])", expected: Object::Int(0)}, - VMTestCase{input: r#"puts("hello", "world!")"#, expected: Object::Null}, - VMTestCase{input: "first([1, 2, 3])", expected: Object::Int(1)}, - VMTestCase{input: "first([])", expected: Object::Null}, - VMTestCase{input: "last([1, 2, 3])", expected: Object::Int(3)}, - VMTestCase{input: "last([])", expected: Object::Null}, - VMTestCase{input: "rest([1, 2, 3])", expected: Object::Array(Rc::new(Array{elements: vec![Rc::new(Object::Int(2)), Rc::new(Object::Int(3))]}))}, - VMTestCase{input: "rest([])", expected: Object::Array(Rc::new(Array{elements: vec![]}))}, - VMTestCase{input: "push([], 1)", expected: Object::Array(Rc::new(Array{elements: vec![Rc::new(Object::Int(1))]}))}, + VMTestCase { + input: r#"len("")"#, + expected: Object::Int(0), + }, + VMTestCase { + input: r#"len("four")"#, + expected: Object::Int(4), + }, + VMTestCase { + input: r#"len("hello world")"#, + expected: Object::Int(11), + }, + VMTestCase { + input: "len([1, 2, 3])", + expected: Object::Int(3), + }, + VMTestCase { + input: "len([])", + expected: Object::Int(0), + }, + VMTestCase { + input: r#"puts("hello", "world!")"#, + expected: Object::Null, + }, + VMTestCase { + input: "first([1, 2, 3])", + expected: Object::Int(1), + }, + VMTestCase { + input: "first([])", + expected: Object::Null, + }, + VMTestCase { + input: "last([1, 2, 3])", + expected: Object::Int(3), + }, + VMTestCase { + input: "last([])", + expected: Object::Null, + }, + VMTestCase { + input: "rest([1, 2, 3])", + expected: Object::Array(Rc::new(Array { + elements: vec![Rc::new(Object::Int(2)), Rc::new(Object::Int(3))], + })), + }, + VMTestCase { + input: "rest([])", + expected: Object::Array(Rc::new(Array { elements: vec![] })), + }, + VMTestCase { + input: "push([], 1)", + expected: Object::Array(Rc::new(Array { + elements: vec![Rc::new(Object::Int(1))], + })), + }, ]; run_vm_tests(tests); @@ -842,7 +1119,7 @@ mod test { #[test] fn closures() { let tests = vec![ - VMTestCase{ + VMTestCase { input: " let newClosure = fn(a) { fn() { a; }; @@ -851,7 +1128,7 @@ mod test { closure();", expected: Object::Int(99), }, - VMTestCase{ + VMTestCase { input: " let newAdder = fn(a, b) { fn(c) { a + b + c }; @@ -860,7 +1137,7 @@ mod test { adder(8);", expected: Object::Int(11), }, - VMTestCase{ + VMTestCase { input: " let newAdder = fn(a, b) { let c = a + b; @@ -870,7 +1147,7 @@ mod test { adder(8);", expected: Object::Int(11), }, - VMTestCase{ + VMTestCase { input: " let newAdderOuter = fn(a, b) { let c = a + b; @@ -883,7 +1160,7 @@ mod test { let adder = newAdderInner(3); adder(8);", expected: Object::Int(14), - } + }, ]; run_vm_tests(tests); @@ -891,9 +1168,8 @@ mod test { #[test] fn recursive_fibonacci() { - let tests = vec![ - VMTestCase{ - input: " + let tests = vec![VMTestCase { + input: " let fibonacci = fn(x) { if (x == 0) { return 0; @@ -906,33 +1182,33 @@ mod test { } }; fibonacci(15);", - expected: Object::Int(610), - }, - ]; + expected: Object::Int(610), + }]; run_vm_tests(tests); } // commented this out because the should_panic macro doesn't work with the CLION test runner -// #[test] -// #[should_panic(expected = "function expects 0 arguments but got 1")] -// fn calling_functions_with_wrong_arguments() { -// let tests = vec![ -// VMTestCase{ -// input: "fn() { 1; }(1);", -// expected: Object::String("asdf".to_string()), -// }, -// ]; -// -// run_vm_tests(tests); -// } - - fn hash_to_object(h: HashMap) -> Object { + // #[test] + // #[should_panic(expected = "function expects 0 arguments but got 1")] + // fn calling_functions_with_wrong_arguments() { + // let tests = vec![ + // VMTestCase{ + // input: "fn() { 1; }(1);", + // expected: Object::String("asdf".to_string()), + // }, + // ]; + // + // run_vm_tests(tests); + // } + + fn hash_to_object(h: HashMap) -> Object { let hash = HashMap::new(); - let mut mh = MonkeyHash{pairs: hash}; + let mut mh = MonkeyHash { pairs: hash }; for (h, k) in h { - mh.pairs.insert(Rc::new(Object::Int(h)), Rc::new(Object::Int(k))); + mh.pairs + .insert(Rc::new(Object::Int(h)), Rc::new(Object::Int(k))); } Object::Hash(Rc::new(mh)) @@ -955,23 +1231,33 @@ mod test { fn test_object(exp: &Object, got: &Object) { match (&exp, &got) { - (Object::Int(exp), Object::Int(got)) => if exp != got { - panic!("ints not equal: exp: {}, got: {}", exp, got) - }, - (Object::Bool(exp), Object::Bool(got)) => if exp != got { - panic!("bools not equal: exp: {}, got: {}", exp, got) - }, - (Object::String(exp), Object::String(got)) => if exp != got { - panic!("strings not equal: exp: {}, got: {}", exp, got) - }, - (Object::Array(exp), Object::Array(got)) => if exp != got { - panic!("arrays not equal: exp: {:?}, got: {:?}", exp, got) - }, - (Object::Hash(exp), Object::Hash(got)) => if exp != got { - panic!("hashes not equal: exp: {:?}, got: {:?}", exp, got) - }, + (Object::Int(exp), Object::Int(got)) => { + if exp != got { + panic!("ints not equal: exp: {}, got: {}", exp, got) + } + } + (Object::Bool(exp), Object::Bool(got)) => { + if exp != got { + panic!("bools not equal: exp: {}, got: {}", exp, got) + } + } + (Object::String(exp), Object::String(got)) => { + if exp != got { + panic!("strings not equal: exp: {}, got: {}", exp, got) + } + } + (Object::Array(exp), Object::Array(got)) => { + if exp != got { + panic!("arrays not equal: exp: {:?}, got: {:?}", exp, got) + } + } + (Object::Hash(exp), Object::Hash(got)) => { + if exp != got { + panic!("hashes not equal: exp: {:?}, got: {:?}", exp, got) + } + } (Object::Null, Object::Null) => (), - _ => panic!("can't compare objects: exp: {:?}, got: {:?}", exp, got) + _ => panic!("can't compare objects: exp: {:?}, got: {:?}", exp, got), } } -} \ No newline at end of file +} From 78f9f3be9059202422911b13d8b1a7225a591a1c Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 15 Mar 2025 23:50:48 +0800 Subject: [PATCH 2/3] use 2024 edition --- Cargo.toml | 2 +- src/compiler.rs | 8 ++++---- src/lexer.rs | 6 +----- src/parser.rs | 40 +++++++++++++++++++++------------------- src/vm.rs | 22 +++++++++------------- 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9304f8..68268cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Paul Dix "] description = "A rust implementation of Thorsten Ball's Monkey programming language." license = "MIT" -edition = "2018" +edition = "2024" [dependencies] byteorder = "1" diff --git a/src/compiler.rs b/src/compiler.rs index fbebaf2..0f5c521 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,5 +1,5 @@ use crate::ast; -use crate::code::{make_instruction, Instructions, Op}; +use crate::code::{Instructions, Op, make_instruction}; use crate::object::{Builtin, CompiledFunction, Object}; use crate::token::Token; use enum_iterator::IntoEnumIterator; @@ -265,7 +265,7 @@ impl Compiler { SymbolScope::Builtin => { return Err(CompileError { message: "can't assign to builtin function name".to_string(), - }) + }); } SymbolScope::Free => panic!("free not here"), }; @@ -328,7 +328,7 @@ impl Compiler { _ => { return Err(CompileError { message: format!("unknown operator {:?}", exp.operator), - }) + }); } }; } @@ -341,7 +341,7 @@ impl Compiler { _ => { return Err(CompileError { message: format!("unknown operator {:?}", exp.operator), - }) + }); } }; } diff --git a/src/lexer.rs b/src/lexer.rs index 2b932f5..9b144dd 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -128,11 +128,7 @@ impl<'a> Iterator for Lexer<'a> { fn next(&mut self) -> Option { let tok = self.next_token(); - if tok == Token::EOF { - None - } else { - Some(tok) - } + if tok == Token::EOF { None } else { Some(tok) } } } diff --git a/src/parser.rs b/src/parser.rs index 32e588c..c83003e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -520,7 +520,7 @@ let foobar = 838383;"; for t in tests { match itr.next().unwrap() { - Statement::Let(ref l) => { + Statement::Let(l) => { assert_eq!(l.name, t); } _ => panic!("unknown node"), @@ -1251,22 +1251,23 @@ return 993322;"; for (k, v) in &h.pairs { match (&k, &v) { - (Expression::String(key), Expression::Integer(int)) => { - match key.as_str() { - "one" => assert_eq!(1, *int), - "two" => assert_eq!(2, *int), - "three" => assert_eq!(3, *int), - _ => panic!("unexpected key {}", k) - } + (Expression::String(key), Expression::Integer(int)) => match key.as_str() { + "one" => assert_eq!(1, *int), + "two" => assert_eq!(2, *int), + "three" => assert_eq!(3, *int), + _ => panic!("unexpected key {}", k), }, (Expression::Integer(key), Expression::Integer(int)) => { assert_eq!(*key, *int); assert_eq!(*int, 4); - }, + } (Expression::Boolean(key), Expression::Boolean(val)) => { assert_eq!(key, val) - }, - _ => panic!("expected key to be a string and value to be an int but got {:?} and {:?}", k, v) + } + _ => panic!( + "expected key to be a string and value to be an int but got {:?} and {:?}", + k, v + ), } } } @@ -1286,15 +1287,16 @@ return 993322;"; for (k, v) in &h.pairs { match (&k, &v) { - (Expression::String(key), Expression::Infix(_)) => { - match key.as_str() { - "one" => test_infix(v, 0, Token::Plus, 1), - "two" => test_infix(v, 10, Token::Minus, 8), - "three" => test_infix(v, 15, Token::Slash, 5), - _ => panic!("unexpected key {}", key) - } + (Expression::String(key), Expression::Infix(_)) => match key.as_str() { + "one" => test_infix(v, 0, Token::Plus, 1), + "two" => test_infix(v, 10, Token::Minus, 8), + "three" => test_infix(v, 15, Token::Slash, 5), + _ => panic!("unexpected key {}", key), }, - _ => panic!("expected key to be a string and value to be an infix expression but got {:?} and {:?}", k, v) + _ => panic!( + "expected key to be a string and value to be an infix expression but got {:?} and {:?}", + k, v + ), } } } diff --git a/src/vm.rs b/src/vm.rs index 59bdd2d..d7908d9 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -287,7 +287,7 @@ impl<'a> VM<'a> { fn execute_call(&mut self, num_args: usize) { if let Some(frame) = match &*self.stack[self.sp - 1 - num_args] { - Object::Closure(ref cl) => Some(Frame { + Object::Closure(cl) => Some(Frame { cl: Rc::clone(cl), ip: 0, base_pointer: self.sp - num_args, @@ -899,8 +899,7 @@ mod test { expected: Object::Int(3), }, VMTestCase { - input: - "let a = fn() { 1 }; let b = fn() { a() + 1 }; let c = fn() { b() + 1 }; c();", + input: "let a = fn() { 1 }; let b = fn() { a() + 1 }; let c = fn() { b() + 1 }; c();", expected: Object::Int(3), }, ]; @@ -927,11 +926,11 @@ mod test { #[test] fn functions_without_return_value() { let tests = vec![ - VMTestCase{ + VMTestCase { input: "let noReturn = fn() { }; noReturn();", expected: Object::Null, }, - VMTestCase{ + VMTestCase { input: "let noReturn = fn() { }; let noReturnTwo = fn() { noReturn(); }; noReturn(); noReturnTwo();", expected: Object::Null, }, @@ -942,12 +941,10 @@ mod test { #[test] fn first_class_functions() { - let tests = vec![ - VMTestCase{ - input: "let returnsOne = fn() { 1; }; let returnsOneReturner = fn() { returnsOne; }; returnsOneReturner()();", - expected: Object::Int(1), - } - ]; + let tests = vec![VMTestCase { + input: "let returnsOne = fn() { 1; }; let returnsOneReturner = fn() { returnsOne; }; returnsOneReturner()();", + expected: Object::Int(1), + }]; run_vm_tests(tests); } @@ -960,8 +957,7 @@ mod test { expected: Object::Int(1), }, VMTestCase { - input: - "let oneAndTwo = fn() { let one = 1; let two = 2; one + two; }; oneAndTwo();", + input: "let oneAndTwo = fn() { let one = 1; let two = 2; one + two; }; oneAndTwo();", expected: Object::Int(3), }, VMTestCase { From e3dffa1d48463bacd92781d8c88fb2e92c8b321c Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 15 Mar 2025 23:55:19 +0800 Subject: [PATCH 3/3] add test --- .github/workflows/ci.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..e33873a --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,27 @@ +name: CI +on: + push: + branches: + - master + pull_request: + types: [opened, reopened, synchronize] + +jobs: + test: + name: ${{matrix.name}} + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - uses: actions/checkout@master + + - name: Install rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Test everything + uses: actions-rs/cargo@v1 + with: + command: test \ No newline at end of file