use crate::{ast::{ASTType, BinOp, Lit, Name, UnOp, AST}, codegen_binary, codegen_unary, extension::Extension, opcodes::OpCode, vm::{Function, Unit, Value, VM}}; pub struct Codegen { pub vm: VM, pub scopes: Vec<(ScopeType, usize)>, // number of variables declared in the scope break_stack: Vec>, continue_stack: Vec>, } impl Codegen { pub fn new(text: String) -> Self { Codegen { vm: VM::new(Vec::new(), Vec::new(), Vec::new(), text), scopes: vec![(ScopeType::Main, 0)], break_stack: Vec::new(), continue_stack: Vec::new(), } } pub fn visit(&mut self, ast: AST) { use crate::ast::ASTType::*; match ast.ast_type { Program(asts) => { for ast in asts { self.visit(ast); } } Function(name, args, block) => { self.visit_function(name, args, *block); } Return(ref expr) => { self.visit_return(&ast, *expr.clone()); } While(condition, block) => { self.visit_while(*condition, *block); } For(expr1, expr2, expr3, block) => { self.visit_for(*expr1, *expr2, *expr3, *block); } Break => { self.visit_break(&ast); } Continue => { self.visit_continue(&ast); } If(condition, block, else_expr) => { self.visit_if(*condition, *block, *else_expr); } Block(ref stmts) => { self.visit_block(&ast, stmts.clone(), ScopeType::Other); } Decl(ref name, ref expr) => { self.visit_decl(&ast, name.clone(), *expr.clone()); } Expr(ref expr) => { self.visit_expr_stmt(&ast, *expr.clone()); } // REMOVE LATER Print(ref expr) => { self.visit_print(&ast, *expr.clone()); } Assign(ref name, ref expr) => { self.visit_assign(&ast, name.clone(), *expr.clone()); } Binary(left, oper, right) => { self.visit_binary(*left, oper, *right); } Unary(oper, right) => { self.visit_unary(oper, *right); } Lit(ref lit) => { self.visit_lit(&ast, lit.clone()); } Call(ref name, ref args) => { self.visit_call(&ast, *name.clone(), args.clone()); } _ => {} } } pub fn visit_function(&mut self, _name: Name, args: Vec, block: AST) { // create new compilation unit self.vm.units.push(Unit::new_empty()); // add function to constant pool and add a load expression in its place let new_unit_ptr = self.vm.units.len()-1; self.vm.units[self.vm.unit_ptr].pool.push(Value::Fun(Function { unit_ptr: new_unit_ptr, arity: args.len(), })); self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; self.vm.units[self.vm.unit_ptr].code.push(loc); if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } self.scopes.last_mut().expect("codegen scopes vec was empty").1 += 1; let old_unit_ptr = self.vm.unit_ptr; let old_ip = self.vm.ip; // set unit to write to self.vm.unit_ptr = new_unit_ptr; self.vm.ip = 0; self.vm.units[self.vm.unit_ptr].pool.push(Value::Fun(Function { unit_ptr: new_unit_ptr, arity: args.len(), })); self.scopes.last_mut().expect("codegen scopes vec was empty").1 += args.len(); if let ASTType::Block(ref stmts) = block.ast_type { self.visit_block(&block, stmts.clone(), ScopeType::Function); } match self.vm.units[self.vm.unit_ptr].code.last() { Some(instr) => { if *instr != >::into(OpCode::RET) { self.vm.units[self.vm.unit_ptr].pool.push(Value::Null); self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; self.vm.units[self.vm.unit_ptr].code.push(loc); self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into()); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } } } None => { self.vm.units[self.vm.unit_ptr].pool.push(Value::Null); self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; self.vm.units[self.vm.unit_ptr].code.push(loc); self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into()); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } } }; self.vm.unit_ptr = old_unit_ptr; self.vm.ip = old_ip; } pub fn visit_return(&mut self, ast: &AST, expr: Option) { let mut scopes = self.scopes.clone(); let mut pop_count = 0; /*loop { let (scope_type, scope) = scopes.pop().expect("scopes empty in return"); pop_count += scope; if scope_type == ScopeType::Function { break; } }*/ self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into()); self.vm.units[self.vm.unit_ptr].code.push(pop_count as u8); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } if let Some(expr) = expr { self.visit(expr); } else { self.vm.units[self.vm.unit_ptr].pool.push(Value::Null); self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; self.vm.units[self.vm.unit_ptr].code.push(loc); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into()); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 1)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1; } } pub fn visit_while(&mut self, condition: AST, block: AST) { self.break_stack.push(Vec::new()); self.continue_stack.push(Vec::new()); let end_jmp = self.vm.units[self.vm.unit_ptr].code.len(); self.visit(condition.clone()); self.vm.units[self.vm.unit_ptr].code.push(OpCode::JNE.into()); self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder let start_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2; if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != condition.end_line { self.vm.units[self.vm.unit_ptr].lines.push((condition.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } if let ASTType::Block(ref stmts) = block.ast_type { self.visit_block(&block, stmts.clone(), ScopeType::Loop); } self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP_UP.into()); let current = self.vm.units[self.vm.unit_ptr].code.len()+2; let current_to_start = current - end_jmp; self.vm.units[self.vm.unit_ptr].code.push(((current_to_start >> 8) & 0xFF) as u8); self.vm.units[self.vm.unit_ptr].code.push((current_to_start & 0xFF) as u8); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } self.patch_jump(start_jmp_loc, 0); let breaks = self.break_stack.pop().expect("break stack empty on pop"); for loc in breaks { self.patch_jump(loc, 0); } let continues = self.continue_stack.pop().expect("continue stack empty on pop"); for loc in continues { let jump = loc - end_jmp + 2; self.vm.units[self.vm.unit_ptr].code[loc] = ((jump >> 8) & 0xFF) as u8; self.vm.units[self.vm.unit_ptr].code[loc + 1] = (jump & 0xFF) as u8; } } pub fn visit_for(&mut self, expr1: Option, expr2: Option, expr3: Option, block: AST) { self.scopes.push((ScopeType::Other, 0)); self.break_stack.push(Vec::new()); self.continue_stack.push(Vec::new()); if let Some(expr1) = expr1 { self.visit(expr1); } let end_jmp = self.vm.units[self.vm.unit_ptr].code.len(); let mut start_jmp_loc = None; if let Some(expr2) = expr2 { self.visit(expr2.clone()); self.vm.units[self.vm.unit_ptr].code.push(OpCode::JNE.into()); self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder start_jmp_loc = Some(self.vm.units[self.vm.unit_ptr].code.len()-2); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != expr2.end_line { self.vm.units[self.vm.unit_ptr].lines.push((expr2.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } } if let ASTType::Block(ref stmts) = block.ast_type { self.visit_block(&block, stmts.clone(), ScopeType::Loop); } let continues = self.continue_stack.pop().expect("continue stack empty on pop"); for loc in continues { /*let jump = loc - end_jmp + 2; self.vm.units[self.vm.unit_ptr].code[loc] = ((jump >> 8) & 0xFF) as u8; self.vm.units[self.vm.unit_ptr].code[loc + 1] = (jump & 0xFF) as u8;*/ self.patch_jump(loc, 0); } if let Some(expr3) = expr3 { self.visit(expr3); self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into()); self.vm.units[self.vm.unit_ptr].code.push(0x01); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } // write instruction to loop self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP_UP.into()); let current = self.vm.units[self.vm.unit_ptr].code.len()+2; let current_to_start = current - end_jmp; self.vm.units[self.vm.unit_ptr].code.push(((current_to_start >> 8) & 0xFF) as u8); self.vm.units[self.vm.unit_ptr].code.push((current_to_start & 0xFF) as u8); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } let breaks = self.break_stack.pop().expect("break stack empty on pop"); for loc in breaks { self.patch_jump(loc, 0); } if let Some(loc) = start_jmp_loc { self.patch_jump(loc, 0); } let (_scope_type, variables) = self.scopes.pop().expect("popped scope in block"); self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into()); self.vm.units[self.vm.unit_ptr].code.push(variables as u8); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } pub fn visit_break(&mut self, ast: &AST) { let mut scopes = self.scopes.clone(); let mut pop_count = 0; loop { let (scope_type, scope) = scopes.pop().expect("scopes empty in break"); pop_count += scope; if scope_type == ScopeType::Loop { break; } } self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into()); self.vm.units[self.vm.unit_ptr].code.push(pop_count as u8); self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into()); self.vm.units[self.vm.unit_ptr].code.push(0xFF); self.vm.units[self.vm.unit_ptr].code.push(0xFF); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 5)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 5; } self.break_stack.last_mut().expect("break not in a loop").push(self.vm.units[self.vm.unit_ptr].code.len()-2); } pub fn visit_continue(&mut self, ast: &AST) { let mut scopes = self.scopes.clone(); let mut pop_count = 0; loop { let (scope_type, scope) = scopes.pop().expect("scopes empty in break"); pop_count += scope; if scope_type == ScopeType::Loop { break; } } self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into()); self.vm.units[self.vm.unit_ptr].code.push(pop_count as u8); self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into()); self.vm.units[self.vm.unit_ptr].code.push(0xFF); self.vm.units[self.vm.unit_ptr].code.push(0xFF); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 5)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 5; } self.continue_stack.last_mut().expect("continue not in a loop").push(self.vm.units[self.vm.unit_ptr].code.len()-2); } pub fn visit_if(&mut self, condition: AST, block: AST, else_expr: Option) { self.visit(condition.clone()); self.vm.units[self.vm.unit_ptr].code.push(OpCode::JNE.into()); self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder let start_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2; if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != condition.end_line { self.vm.units[self.vm.unit_ptr].lines.push((condition.end_line, 5)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 5; } if let ASTType::Block(ref stmts) = block.ast_type { self.visit_block(&block, stmts.clone(), ScopeType::If); } if let Some(ast) = else_expr { match ast.ast_type { ASTType::If(_, _, _) => { self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into()); self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder let end_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2; if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } self.patch_jump(start_jmp_loc, 0); self.visit(ast); self.patch_jump(end_jmp_loc, 0); } ASTType::Block(_) => { self.vm.units[self.vm.unit_ptr].code.push(OpCode::JMP.into()); self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder self.vm.units[self.vm.unit_ptr].code.push(0xFF); // placeholder if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 3)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; } let end_jmp_loc = self.vm.units[self.vm.unit_ptr].code.len()-2; self.patch_jump(start_jmp_loc, 0); // jmp to else if let ASTType::Block(ref stmts) = ast.ast_type { self.visit_block(&ast, stmts.clone(), ScopeType::If); } self.patch_jump(end_jmp_loc, 0); // jmp to after else } _ => { println!("unimplemented"); } } } else { self.patch_jump(start_jmp_loc, 0); } } pub fn visit_block(&mut self, ast: &AST, stmts: Vec, scope_type: ScopeType) { self.scopes.push((scope_type, 0)); for stmt in stmts { self.visit(stmt); } let (_scope_type, variables) = self.scopes.pop().expect("popped scope in block"); self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into()); self.vm.units[self.vm.unit_ptr].code.push(variables as u8); match self.vm.units[self.vm.unit_ptr].lines.last() { Some(last) => if last.0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } None => self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)) } } pub fn visit_decl(&mut self, ast: &AST, _name: Name, expr: AST) { self.visit(expr); #[allow(irrefutable_let_patterns)] if let Extension::Resolution(_scope, _ptr) = ast.extensions[0] { self.scopes.last_mut().expect("codegen scopes vec was empty").1 += 1; } } pub fn visit_expr_stmt(&mut self, ast: &AST, expr: AST) { self.visit(expr); self.vm.units[self.vm.unit_ptr].code.push(OpCode::POP.into()); self.vm.units[self.vm.unit_ptr].code.push(0x01); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } // REMOVE LATER pub fn visit_print(&mut self, ast: &AST, expr: AST) { self.visit(expr); self.vm.units[self.vm.unit_ptr].code.push(OpCode::PRINT.into()); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 1)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1; } } pub fn visit_assign(&mut self, ast: &AST, _name: Name, expr: AST) { self.visit(expr); // pop stack to get value. then find variable in stack. set variable to value. self.vm.units[self.vm.unit_ptr].code.push(OpCode::ASSIGN.into()); #[allow(irrefutable_let_patterns)] if let Extension::Resolution(_scope, ptr) = ast.extensions[0] { self.vm.units[self.vm.unit_ptr].code.push(ptr as u8); if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 1)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1; } } if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 1)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1; } } pub fn visit_binary(&mut self, left: AST, oper: BinOp, right: AST) { use crate::ast::BinOp::*; codegen_binary!(self, left, right, oper, Add, ADD, Sub, SUB, Mul, MUL, Div, DIV, Mod, MOD, BitAnd, BITAND, BitXor, BITXOR, BitOr, BITOR, Eq, EQ, Ne, NE, Gr, GR, Ge, GE, Ls, LS, Le, LE, Or, OR, And, AND); } pub fn visit_unary(&mut self, oper: UnOp, right: AST) { use crate::ast::UnOp::*; codegen_unary!(self, right, oper, Not, NOT, Neg, NEG); } pub fn visit_lit(&mut self, ast: &AST, lit: Lit) { match lit { Lit::Num(value) => { self.vm.units[self.vm.unit_ptr].pool.push(Value::Num(value)); self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; self.vm.units[self.vm.unit_ptr].code.push(loc); if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } Lit::Str(value) => { self.vm.units[self.vm.unit_ptr].pool.push(Value::Str(value.into())); self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; self.vm.units[self.vm.unit_ptr].code.push(loc); if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } Lit::Bool(value) => { self.vm.units[self.vm.unit_ptr].pool.push(Value::Bool(value)); self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; self.vm.units[self.vm.unit_ptr].code.push(loc); if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } Lit::Ident(_name) => { self.vm.units[self.vm.unit_ptr].code.push(OpCode::VAR.into()); #[allow(irrefutable_let_patterns)] if let Extension::Resolution(_scope, ptr) = ast.extensions[0] { /*println!("line: {} ptr: {} locals: {:?}", ast.end_line, ptr, self.locals); let (_scope, slot) = self.locals.get(ptr).unwrap(); println!("slot: {}", *slot); self.vm.units[self.vm.unit_ptr].code.push(*slot);*/ self.vm.units[self.vm.unit_ptr].code.push(ptr as u8); if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } } _ => {} } } pub fn visit_call(&mut self, ast: &AST, ident: AST, args: Vec) { for arg in args.iter().rev() { self.visit(arg.clone()); } self.visit(ident); self.vm.units[self.vm.unit_ptr].code.push(OpCode::CALL.into()); self.vm.units[self.vm.unit_ptr].code.push(args.len() as u8); if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); } else { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } pub fn patch_jump(&mut self, loc: usize, offset: i32) { let jump = ((self.vm.units[self.vm.unit_ptr].code.len() - loc - 2) as i32) + offset; self.vm.units[self.vm.unit_ptr].code[loc] = ((jump >> 8) & 0xFF) as u8; self.vm.units[self.vm.unit_ptr].code[loc + 1] = (jump & 0xFF) as u8; } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum ScopeType { Main, Function, Loop, If, Other, }