diff --git a/src/ast_compiler.rs b/src/ast_compiler.rs index 2c2b03c..78f7435 100644 --- a/src/ast_compiler.rs +++ b/src/ast_compiler.rs @@ -1,12 +1,18 @@ use crate::ast_compiler::Expression::{ - FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable, + Assignment, FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable, }; use crate::errors::CompilerError::{ self, Expected, ParseError, TooManyParameters, UnexpectedIndent, UninitializedVariable, }; use crate::errors::CompilerErrorAtLine; use crate::symbol_builder::{Symbol, calculate_type, infer_type}; -use crate::tokens::TokenType::{Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater, Identifier, If, Indent, Integer, LeftBrace, LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightBrace, RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, True, U32, U64, Unknown, Else}; +use crate::tokens::TokenType::{ + Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For, + Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace, + LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, + Print, Range, RightBrace, RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, + True, U32, U64, Unknown, +}; use crate::tokens::{Token, TokenType}; use crate::value::Value; use crate::{Expr, Stmt, SymbolTable}; @@ -66,6 +72,9 @@ impl AstCompiler { if !self.had_error { let mut statements = vec![]; while !self.is_at_end() { + if self.match_token(&[Eol]) { + continue; + } let statement = self.indent(symbol_table)?; if let Some(statement) = statement { statements.push(statement); @@ -316,11 +325,29 @@ impl AstCompiler { self.print_statement(symbol_table) } else if self.match_token(&[If]) { self.if_statement(symbol_table) + } else if self.match_token(&[For]) { + self.for_statement(symbol_table) } else { self.expr_statement(symbol_table) } } + fn for_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt { + let loop_var = self.consume(&Identifier, Expected("loop variable name."))?; + self.consume(&In, Expected("'in' after loop variable name."))?; + let range = self.expression(symbol_table)?; + self.consume(&Colon, Expected("colon after range expression"))?; + self.consume(&Eol, Expected("end of line after for expression."))?; + self.inc_indent(); + let body = self.compile(symbol_table)?; + + Ok(Statement::ForStatement { + loop_var, + range, + body, + }) + } + fn if_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt { let condition = self.expression(symbol_table)?; self.consume(&Colon, Expected("':' after if condition."))?; @@ -337,7 +364,11 @@ impl AstCompiler { } else { None }; - Ok(Statement::IfStatement {condition, then_branch, else_branch}) + Ok(Statement::IfStatement { + condition, + then_branch, + else_branch, + }) } fn inc_indent(&mut self) { @@ -389,7 +420,21 @@ impl AstCompiler { fn assignment(&mut self, symbol_table: &mut SymbolTable) -> Expr { let expr = self.equality(symbol_table)?; - self.binary(&[TokenType::Equal], expr, symbol_table) + if self.match_token(&[Equal]) { + let operator = self.previous().clone(); + let right = self.comparison(symbol_table)?; + if let Variable { name, .. } = expr { + Ok(Assignment { + line: operator.line, + variable_name: name.to_string(), + value: Box::new(right), + }) + } else { + Err(self.raise(CompilerError::Failure)) + } + } else { + Ok(expr) + } } fn equality(&mut self, symbol_table: &mut SymbolTable) -> Expr { @@ -402,7 +447,7 @@ impl AstCompiler { } fn comparison(&mut self, symbol_table: &mut SymbolTable) -> Expr { - let expr = self.bitshift(symbol_table)?; + let expr = self.range(symbol_table)?; self.binary( &[Greater, GreaterEqual, Less, LessEqual], expr, @@ -410,6 +455,20 @@ impl AstCompiler { ) } + fn range(&mut self, symbol_table: &mut SymbolTable) -> Expr { + let mut expr = self.bitshift(symbol_table)?; + if self.match_token(&[Range]) { + let operator = self.previous().clone(); + let right = self.expression(symbol_table)?; + expr = Expression::Range { + line: operator.line, + lower: Box::new(expr), + upper: Box::new(right), + }; + } + Ok(expr) + } + fn bitshift(&mut self, symbol_table: &mut SymbolTable) -> Expr { let expr = self.term(symbol_table)?; self.binary(&[GreaterGreater, LessLess], expr, symbol_table) @@ -805,11 +864,16 @@ pub enum Statement { if_expr: Expression, then_expr: Expression, }, - IfStatement{ + IfStatement { condition: Expression, then_branch: Vec, - else_branch: Option> - } + else_branch: Option>, + }, + ForStatement { + loop_var: Token, + range: Expression, + body: Vec, + }, } impl Statement { @@ -822,6 +886,7 @@ impl Statement { Statement::ObjectStmt { name, .. } => name.line, Statement::GuardStatement { if_expr, .. } => if_expr.line(), Statement::IfStatement { condition, .. } => condition.line(), + Statement::ForStatement { loop_var, .. } => loop_var.line, } } } @@ -867,6 +932,11 @@ pub enum Expression { literaltype: TokenType, value: Value, }, + Range { + line: usize, + lower: Box, + upper: Box, + }, List { line: usize, literaltype: TokenType, @@ -882,6 +952,11 @@ pub enum Expression { name: String, var_type: TokenType, }, + Assignment { + line: usize, + variable_name: String, + value: Box, + }, FunctionCall { line: usize, name: String, @@ -922,9 +997,11 @@ impl Expression { Self::Unary { line, .. } => *line, Self::Grouping { line, .. } => *line, Self::Literal { line, .. } => *line, + Self::Range { line, .. } => *line, Self::List { line, .. } => *line, Self::Map { line, .. } => *line, Variable { line, .. } => *line, + Assignment { line, .. } => *line, FunctionCall { line, .. } => *line, MethodCall { line, .. } => *line, Stop { line } => *line, diff --git a/src/bytecode_compiler.rs b/src/bytecode_compiler.rs index c0988c2..2f0833f 100644 --- a/src/bytecode_compiler.rs +++ b/src/bytecode_compiler.rs @@ -7,10 +7,10 @@ use crate::errors::{CompilerError, CompilerErrorAtLine}; use crate::symbol_builder::{Symbol, calculate_type, infer_type}; use crate::tokens::TokenType; use crate::tokens::TokenType::Unknown; -use crate::value::{Value}; +use crate::value::Value; use crate::vm::{ OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CALL_BUILTIN, - OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, + OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GOTO_IF, OP_GREATER, OP_GREATER_EQUAL, OP_IF, OP_IF_ELSE, OP_LESS, OP_LESS_EQUAL, OP_LIST_GET, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, }; @@ -76,6 +76,7 @@ impl Compiler { } } + /// compile the entire AST into a chunk, adding a RETURN OP pub(crate) fn compile( &mut self, ast: &Vec, @@ -83,20 +84,28 @@ impl Compiler { registry: &mut Registry, namespace: &str, ) -> Result { - for statement in ast { - self.compile_statement(statement, symbols, registry, namespace)?; - } - + self.compile_statements(ast, symbols, registry, namespace)?; self.emit_byte(OP_RETURN); let chunk = self.chunk.clone(); self.chunk.code.clear(); // in case the compiler is reused, clear it for the next compilation. This is for the REPL Ok(chunk) } - fn raise(&self, error: CompilerError) -> CompilerErrorAtLine { - CompilerErrorAtLine::raise(error, self.current_line) + /// compile the entire AST into a chunk + fn compile_statements( + &mut self, + ast: &Vec, + symbols: &SymbolTable, + registry: &mut Registry, + namespace: &str, + ) -> Result<(), CompilerErrorAtLine> { + for statement in ast { + self.compile_statement(statement, symbols, registry, namespace)?; + } + Ok(()) } + /// compile a single statement fn compile_statement( &mut self, statement: &Statement, @@ -174,6 +183,38 @@ impl Compiler { )?; } } + Statement::ForStatement { + loop_var, + range, + body, + } => { + // 1. step var index + let step_const_index = self.emit_constant(Value::I64(1)); + // 2. range expression + self.compile_expression(namespace, range, symbols, registry)?; + //save the constants for lower and upper bounds of the range + let start_index = self.chunk.constants.len() - 1; + let end_index = self.chunk.constants.len() - 2; + + let name = loop_var.lexeme.as_str(); + let loop_var_name_index = self.chunk.add_var(&loop_var.token_type, name); + self.vars.insert(name.to_string(), loop_var_name_index); + + // 3. start index + self.emit_bytes(OP_CONSTANT, start_index as u16); + self.emit_bytes(OP_ASSIGN, loop_var_name_index as u16); + + let return_addr = self.chunk.code.len(); + self.compile_statements(body, symbols, registry, namespace)?; + self.emit_bytes(OP_GET, loop_var_name_index as u16); + self.emit_bytes(OP_CONSTANT, step_const_index); + self.emit_byte(OP_ADD); + self.emit_bytes(OP_ASSIGN, loop_var_name_index as u16); + self.emit_bytes(OP_CONSTANT, end_index as u16); + self.emit_bytes(OP_GET, loop_var_name_index as u16); + self.emit_byte(OP_GREATER_EQUAL); + self.emit_bytes(OP_GOTO_IF, return_addr as u16); + } } Ok(()) } @@ -262,6 +303,15 @@ impl Compiler { return Err(self.raise(UndeclaredVariable(name.to_string()))); } } + Expression::Assignment { variable_name, value, .. } => { + self.compile_expression(namespace, value, symbols, registry)?; + let name_index = self.vars.get(variable_name); + if let Some(name_index) = name_index { + self.emit_bytes(OP_ASSIGN, *name_index as u16); + } else { + return Err(self.raise(UndeclaredVariable(variable_name.to_string()))); + } + } Expression::Literal { value, .. } => { self.emit_constant(value.clone()); } @@ -343,6 +393,11 @@ impl Compiler { } Expression::MapGet { .. } => {} Expression::FieldGet { .. } => {} + Expression::Range { lower, upper, .. } => { + // opposite order, because we have to assign last one first to the loop variable + self.compile_expression(namespace, upper, symbols, registry)?; + self.compile_expression(namespace, lower, symbols, registry)?; + } } Ok(()) } @@ -393,4 +448,8 @@ impl Compiler { self.emit_bytes(OP_CONSTANT, index); index } + + fn raise(&self, error: CompilerError) -> CompilerErrorAtLine { + CompilerErrorAtLine::raise(error, self.current_line) + } } diff --git a/src/chunk.rs b/src/chunk.rs index 22df4f1..79dd59a 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -5,7 +5,7 @@ use crate::vm::{ OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_STRING, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, - OP_NEGATE, OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, + OP_NEGATE, OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,OP_ASSIGN,OP_GOTO_IF }; use std::collections::HashMap; @@ -117,9 +117,11 @@ impl Chunk { OP_DEF_F64 => self.constant_inst("DEFF64", offset), OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset), OP_CALL => self.call_inst("CALL", offset), - OP_GET => self.constant_inst("GET", offset), + OP_GET => self.assign_inst("GET", offset), OP_DEF_LIST => self.new_inst("DEFLIST", offset), OP_DEF_MAP => self.new_inst("DEFMAP", offset), + OP_ASSIGN => self.assign_inst("ASSIGN", offset), + OP_GOTO_IF => self.constant_inst("GOTO_IF", offset), _ => { println!("Unknown instruction {}", instruction); offset + 1 @@ -132,6 +134,13 @@ impl Chunk { offset + 1 } + fn assign_inst(&self, op: &str, offset: usize) -> usize { + let constant = self.code[offset + 1]; + print!("{} {}:", op, constant); + println!("{}",self.vars.get(constant as usize).unwrap().1); + offset + 2 + } + fn call_inst(&self, op: &str, offset: usize) -> usize { let constant = self.code[offset + 1]; let num_args = self.code[offset + 2]; diff --git a/src/compiler_tests.rs b/src/compiler_tests.rs index 14ad19a..efdabe6 100644 --- a/src/compiler_tests.rs +++ b/src/compiler_tests.rs @@ -156,7 +156,7 @@ object Person: let p = Person(name: 0x42) p"#); - assert!(r.is_err()); + // assert!(r.is_err()); assert_eq!( r#"Compilation failed: error at line 5, Expected string, found integer"#, format!("{}", r.unwrap_err().to_string()) @@ -386,6 +386,24 @@ else: ); } + #[test] + fn inline_comment(){ + assert_eq!(run(r#"// this is a comment"#), Ok(Value::Void)); + } + + #[test] + fn range_loop() { + assert_eq!( + run(r#" +let sum=0 +for a in 1..4: + sum = sum + a +sum +"#), + Ok(Value::I64(10)) + ); + } + // #[test] // fn package() { // assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48))); diff --git a/src/keywords.rs b/src/keywords.rs index 691abf5..59e1c33 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -13,6 +13,7 @@ pub(crate) fn get_keyword(lexeme: &str) -> Option { "fn" => Some(TokenType::Fn), "for" => Some(TokenType::For), "if" => Some(TokenType::If), + "in" => Some(TokenType::In), "i32" => Some(TokenType::I32), "i64" => Some(TokenType::I64), "let" => Some(TokenType::Let), diff --git a/src/lib.rs b/src/lib.rs index a6237ab..bc3f1b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ use crate::symbol_builder::Symbol; use std::collections::HashMap; use std::fs; use walkdir::WalkDir; +use crate::value::Value::Void; pub mod ast_compiler; mod builtins; @@ -92,6 +93,7 @@ pub(crate) fn run(src: &str) -> Result { symbol_builder::build("", &ast, &mut symbol_table); let mut registry = HashMap::new(); bytecode_compiler::compile(None, &ast, &symbol_table, &mut registry)?; + let registry = arc_swap::ArcSwap::from(std::sync::Arc::new(registry)); vm::interpret(registry.load(), "main").map_err(CrudLangError::from) } diff --git a/src/repl.rs b/src/repl.rs index f5c06e1..5a4ba4e 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -2,7 +2,7 @@ use crate::chunk::Chunk; use crate::errors::CrudLangError; use crate::scanner::scan; use crate::vm::Vm; -use crate::{ast_compiler, bytecode_compiler, map_underlying, recompile, symbol_builder}; +use crate::{ast_compiler, bytecode_compiler, map_underlying, symbol_builder}; use arc_swap::ArcSwap; use std::collections::HashMap; use std::io; diff --git a/src/scanner.rs b/src/scanner.rs index 57340ac..3a5a357 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -142,7 +142,7 @@ impl Scanner { if c == '0' && self.peek() == 'x' { self.hex_number()?; } else if c.is_ascii_digit() { - self.number(); + self.number_or_range(); } else if is_alpha(c) { self.identifier(); } else { @@ -181,7 +181,7 @@ impl Scanner { Ok(()) } - fn number(&mut self) { + fn number_or_range(&mut self) { while self.peek().is_ascii_digit() { self.advance(); } @@ -190,12 +190,29 @@ impl Scanner { has_dot = true; self.advance(); } + if self.peek() == '.' && self.peek_next() == '.' { + self.range_expression() + } else { + while is_digit_or_scientific(self.peek()) { + self.advance(); + } + let value: String = self.chars[self.start..self.current].iter().collect(); + self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value); + } + } - while is_digit_or_scientific(self.peek()) { + fn range_expression(&mut self) { + let lower: String = self.chars[self.start..self.current].iter().collect(); + self.match_next('.'); + self.match_next('.'); + self.add_token_with_value(Integer, lower); + self.add_token(TokenType::Range); + self.start = self.current; + while self.peek().is_ascii_digit() { self.advance(); } - let value: String = self.chars[self.start..self.current].iter().collect(); - self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value); + let upper: String = self.chars[self.start..self.current].iter().collect(); + self.add_token_with_value(Integer, upper); } fn char(&mut self) -> Result<(), CompilerErrorAtLine> { @@ -265,11 +282,15 @@ impl Scanner { } fn peek_next(&self) -> char { - self.chars[self.current + 1] + if self.current + 1 >= self.chars.len() { + '\0' + } else { + self.chars[self.current + 1] + } } fn match_next(&mut self, expected: char) -> bool { - if self.is_at_end() || self.chars[self.current] != expected{ + if self.is_at_end() || self.chars[self.current] != expected { false } else { self.current += 1; @@ -304,13 +325,12 @@ struct Scanner { new_line: bool, } - fn is_digit_or_scientific(c: char) -> bool { c.is_ascii_digit() || c == 'e' || c == 'E' } fn is_alphanumeric(c: char) -> bool { - is_alpha(c) || c.is_ascii_digit() + is_alpha(c) || c.is_ascii_digit() } fn is_alpha(c: char) -> bool { diff --git a/src/symbol_builder.rs b/src/symbol_builder.rs index 786a073..0c7d6c6 100644 --- a/src/symbol_builder.rs +++ b/src/symbol_builder.rs @@ -1,5 +1,5 @@ use crate::ast_compiler::{Expression, Parameter, Statement}; -use crate::builtins::{Signature, lookup}; +use crate::builtins::lookup; use crate::errors::CompilerError; use crate::errors::CompilerError::IncompatibleTypes; use crate::tokens::TokenType::{ @@ -186,6 +186,7 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap) -> Token } } Expression::Variable { var_type, .. } => var_type.clone(), + Expression::Assignment { value, .. } => infer_type(value, symbols), Expression::FunctionCall { name, .. } => { let symbol = symbols.get(name); match symbol { @@ -203,7 +204,7 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap) -> Token if let Ok(signature) = lookup(&value.to_string(), method_name) { signature.return_type.clone() } else { - unreachable!()//? + unreachable!() //? } } else { infer_type(receiver, symbols) @@ -215,5 +216,6 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap) -> Token Expression::ListGet { .. } => TokenType::Unknown, Expression::MapGet { .. } => TokenType::Unknown, Expression::FieldGet { .. } => TokenType::Unknown, + Expression::Range { lower, .. } => infer_type(lower, symbols), } } diff --git a/src/tokens.rs b/src/tokens.rs index 02e477e..872c2c5 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -51,6 +51,7 @@ pub enum TokenType { I64, Identifier, If, + In, Indent, Integer, SignedInteger, @@ -73,6 +74,7 @@ pub enum TokenType { Plus, Print, Question, + Range, Return, RightParen, RightBrace, @@ -132,6 +134,7 @@ impl fmt::Display for TokenType { TokenType::Hex => write!(f, "0x"), TokenType::If => write!(f, "if"), TokenType::Identifier => write!(f, "identifier"), + TokenType::In => write!(f, "in"), TokenType::Indent => write!(f, "indent"), TokenType::Integer => write!(f, "integer"), TokenType::LeftBrace => write!(f, "{{"), @@ -150,6 +153,7 @@ impl fmt::Display for TokenType { TokenType::Plus => write!(f, "+"), TokenType::Print => write!(f, "print"), TokenType::Question => write!(f, "?"), + TokenType::Range => write!(f, ".."), TokenType::Return => write!(f, "return"), TokenType::RightParen => write!(f, ")"), TokenType::RightBrace => write!(f, "}}"), diff --git a/src/vm.rs b/src/vm.rs index ec8e0d2..a4d2cb1 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,3 +1,4 @@ +use crate::Registry; use crate::chunk::Chunk; use crate::errors::RuntimeError::Something; use crate::errors::{RuntimeError, ValueError}; @@ -7,7 +8,6 @@ use arc_swap::Guard; use std::collections::HashMap; use std::sync::Arc; use tracing::debug; -use crate::Registry; pub struct Vm { ip: usize, @@ -17,14 +17,15 @@ pub struct Vm { pub(crate) registry: Arc>, } - pub fn interpret( registry: Guard>>, function: &str, ) -> Result { let chunk = registry.get(function).unwrap().clone(); + // chunk.disassemble(); let mut vm = Vm::new(®istry); vm.run(&get_context(function), &chunk) + // Ok(Value::Void) } pub async fn interpret_async( @@ -57,7 +58,7 @@ fn value_map(strings: HashMap) -> HashMap { } pub fn interpret_function(chunk: &Chunk, args: Vec) -> Result { - let mut vm = Vm::new(& Arc::new(HashMap::new())); + let mut vm = Vm::new(&Arc::new(HashMap::new())); vm.run_function(chunk, args) } @@ -157,7 +158,7 @@ impl Vm { let (var_type, name) = chunk.vars.get(index).unwrap(); let value = self.pop(); let value = Self::number(var_type, value)?; - self.local_vars.insert(name.to_string(), value); + self.local_vars.insert(name.to_string(), value); //insert or update } OP_DEF_MAP => { let len = self.read(chunk); @@ -172,7 +173,7 @@ impl Vm { OP_GET => { let var_index = self.read(chunk); let (_, name_index) = chunk.vars.get(var_index).unwrap(); - let value = self.local_vars.remove(name_index).unwrap(); + let value = self.local_vars.get(name_index).unwrap().clone(); self.push(value); } OP_LIST_GET => { @@ -196,10 +197,11 @@ impl Vm { } args.reverse(); let receiver = self.pop(); - let return_value = crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?; + let return_value = + crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?; self.push(return_value); } - OP_POP =>{ + OP_POP => { self.pop(); // discards the value } OP_CALL => { @@ -232,12 +234,9 @@ impl Vm { } let mut fields = vec![]; - params - .iter() - .zip(args) - .for_each(|(param, arg)| { - fields.push((param.name.lexeme.clone(), arg)) - }); + params.iter().zip(args).for_each(|(param, arg)| { + fields.push((param.name.lexeme.clone(), arg)) + }); let new_instance = Value::ObjectType(Box::new(Object { definition: function_name, fields, @@ -254,7 +253,7 @@ impl Vm { OP_IF => { let condition = self.pop(); if condition == Value::Bool(true) { - if let Some(then) = self.registry.get(&format!("{}.?",chunk.name)){ + if let Some(then) = self.registry.get(&format!("{}.?", chunk.name)) { let result = interpret_function(then, vec![])?; self.push(result); } @@ -263,19 +262,26 @@ impl Vm { OP_IF_ELSE => { let condition = self.pop(); self.push(if condition == Value::Bool(true) { - if let Some(then) = self.registry.get(&format!("{}.?",chunk.name)){ + if let Some(then) = self.registry.get(&format!("{}.?", chunk.name)) { interpret_function(then, vec![])? } else { return Err(Something); } } else { - if let Some(then) = self.registry.get(&format!("{}.:",chunk.name)){ + if let Some(then) = self.registry.get(&format!("{}.:", chunk.name)) { interpret_function(then, vec![])? } else { return Err(Something); } }); } + OP_GOTO_IF => { + let b = self.pop(); + let goto_addr = self.read(chunk); + if b == Value::Bool(true) { + self.ip = goto_addr; + } + } _ => {} } } @@ -389,3 +395,4 @@ pub const OP_LIST_GET: u16 = 42; pub const OP_CALL_BUILTIN: u16 = 43; pub const OP_IF: u16 = 44; pub const OP_IF_ELSE: u16 = 45; +pub const OP_GOTO_IF: u16 = 46;