use crate::{ast::{ASTType, BinOp, LhsAssign, LhsAssignType, 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<Vec<usize>>,
continue_stack: Vec<Vec<usize>>,
}
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.kind {
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());
}
Anonymous(params, block) => {
self.visit_anonymous(params, *block);
}
Subscript(ref left, ref right) => {
self.visit_subscript(&ast, *left.clone(), *right.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<Name>, 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.variables.push(Value::Fun(Function {
unit_ptr: new_unit_ptr,
arity: args.len(),
}));
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.kind {
self.visit_block(&block, stmts.clone(), ScopeType::Function);
}
match self.vm.units[self.vm.unit_ptr].code.last() {
Some(instr) => {
if *instr != <OpCode as Into<u8>>::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<AST>) {
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.kind {
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<AST>, expr2: Option<AST>,
expr3: Option<AST>, 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.kind {
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<AST>) {
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.kind {
self.visit_block(&block, stmts.clone(), ScopeType::If);
}
if let Some(ast) = else_expr {
match ast.kind {
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.kind {
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<AST>, 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);
self.vm.units[self.vm.unit_ptr].code.push(OpCode::DECL.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;
}
#[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: LhsAssign, expr: AST) {
self.visit(expr);
// pop stack to get value. then find variable in stack. set variable to value.
match name.kind {
LhsAssignType::Ident(_) => {
#[allow(irrefutable_let_patterns)]
if let Extension::Resolution(_scope, ptr) = ast.extensions[0] {
self.vm.units[self.vm.unit_ptr].code.push(OpCode::ASN.into());
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, 2));
} else {
self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2;
}
}
}
LhsAssignType::Subscript(name, index) => {
self.visit(*index);
#[allow(irrefutable_let_patterns)]
if let Extension::Resolution(_scope, ptr) = name.extensions[0] {
self.vm.units[self.vm.unit_ptr].code.push(OpCode::REF.into());
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, 2));
} else {
self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2;
}
} else {
panic!("Subscript assignment should always have a Resolution");
}
self.vm.units[self.vm.unit_ptr].code.push(OpCode::ASNARR.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_anonymous(&mut self, params: Vec<Name>, block: AST) {
self.vm.units.push(Unit::new_empty());
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: params.len(),
}));
// load function to stack
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;
// enter function unit
let old_unit_ptr = self.vm.unit_ptr;
let old_ip = self.vm.ip;
self.vm.unit_ptr = new_unit_ptr;
self.vm.ip = 0;
// update scopes with number of parameters
self.scopes.last_mut().expect("codegen scopes vec was empty").1 += params.len();
if let ASTType::Block(ref stmts) = block.kind {
self.visit_block(&block, stmts.clone(), ScopeType::Function);
}
// add implicit RET
match self.vm.units[self.vm.unit_ptr].code.last() {
Some(instr) => {
if *instr != <OpCode as Into<u8>>::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_subscript(&mut self, ast: &AST, left: AST, right: AST) {
self.visit(left);
self.visit(right);
self.vm.units[self.vm.unit_ptr].code.push(OpCode::SCR.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_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::Array(exprs) => {
for expr in &exprs {
self.visit(expr.clone());
}
self.vm.units[self.vm.unit_ptr].code.push(OpCode::LIST.into());
self.vm.units[self.vm.unit_ptr].code.push(exprs.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;
}
}
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<AST>) {
for arg in args.iter() {
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,
}