@@ 3,6 3,8 @@ use crate::{ast::{ASTType, BinOp, Lit, Name, UnOp, AST}, codegen_binary, codegen
pub struct Codegen {
pub vm: VM,
pub scopes: Vec<usize>, // number of variables declared in the scope
+ break_stack: Vec<Vec<usize>>,
+ continue_stack: Vec<Vec<usize>>,
}
impl Codegen {
@@ 11,6 13,8 @@ impl Codegen {
vm: VM::new(Vec::new(), Vec::new(),
Vec::new(), text),
scopes: vec![0],
+ break_stack: Vec::new(),
+ continue_stack: Vec::new(),
}
}
pub fn visit(&mut self, ast: AST) {
@@ 24,6 28,12 @@ impl Codegen {
While(condition, block) => {
self.visit_while(*condition, *block);
}
+ Break => {
+ self.visit_break(&ast);
+ }
+ Continue => {
+ self.visit_continue(&ast);
+ }
If(condition, block, else_expr) => {
self.visit_if(*condition, *block, *else_expr);
}
@@ 56,6 66,9 @@ impl Codegen {
}
}
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.chunk.len();
self.visit(condition.clone());
self.vm.chunk.push(OpCode::JNE.into());
@@ 79,6 92,39 @@ impl Codegen {
self.vm.lines.last_mut().unwrap().1 += 3;
}
self.patch_jump(start_jmp_loc);
+
+ let breaks = self.break_stack.pop().expect("break stack empty on pop");
+ for loc in breaks {
+ self.patch_jump(loc);
+ }
+ let continues = self.continue_stack.pop().expect("continue stack empty on pop");
+ for loc in continues {
+ let jump = loc - end_jmp - 2;
+ self.vm.chunk[loc] = ((jump >> 8) & 0xFF) as u8;
+ self.vm.chunk[loc + 1] = (jump & 0xFF) as u8;
+ }
+ }
+ pub fn visit_break(&mut self, ast: &AST) {
+ self.vm.chunk.push(OpCode::JMP.into());
+ self.vm.chunk.push(0xFF);
+ self.vm.chunk.push(0xFF);
+ if self.vm.lines.last().unwrap().0 != ast.end_line {
+ self.vm.lines.push((ast.end_line, 3));
+ } else {
+ self.vm.lines.last_mut().unwrap().1 += 3;
+ }
+ self.break_stack.last_mut().expect("break not in a loop").push(self.vm.chunk.len()-2);
+ }
+ pub fn visit_continue(&mut self, ast: &AST) {
+ self.vm.chunk.push(OpCode::JMP_UP.into());
+ self.vm.chunk.push(0xFF);
+ self.vm.chunk.push(0xFF);
+ if self.vm.lines.last().unwrap().0 != ast.end_line {
+ self.vm.lines.push((ast.end_line, 3));
+ } else {
+ self.vm.lines.last_mut().unwrap().1 += 3;
+ }
+ self.continue_stack.last_mut().expect("continue not in a loop").push(self.vm.chunk.len()-2);
}
pub fn visit_if(&mut self, condition: AST, block: AST, else_expr: Option<AST>) {
self.visit(condition.clone());
@@ 64,6 64,8 @@ impl Resolver {
let block = self.visit(*block);
ast_from_ast!(While(Box::new(condition), Box::new(block)), ast, ast)
}
+ Break => { ast_from_ast!(Break, ast, ast) }
+ Continue => { ast_from_ast!(Continue, ast, ast) }
For(expr1, expr2, expr3, block) => {
let mut n_expr1 = None;
let mut n_expr2 = None;