use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use crate::vm::Vm;
use crate::vm::VmOpcode;
use crate::vmbindings::carray::CArray;
struct Scope {
vars : Vec<String>
}
impl Scope {
fn new() -> Scope {
Scope { vars: Vec::new() }
}
}
struct LoopStatement {
pub fill_continue: Vec<usize>,
pub fill_break: Vec<usize>
}
pub type ArrayIndexRange = (usize, usize);
pub struct SourceMap {
pub file: ArrayIndexRange,
pub bytecode: ArrayIndexRange,
pub fileno: usize,
}
macro_rules! vm {
($c:ident) => ($c.vm.borrow_mut());
}
pub struct Compiler {
scopes : Vec<Scope>,
loop_stmts : Vec<LoopStatement>,
pub smap: Vec<SourceMap>,
pub files: Vec<String>,
pub modules_loaded: std::collections::HashSet<std::path::PathBuf>,
pub symbol: HashMap<usize, String>,
pub sources: Vec<String>,
pub vm : Rc<RefCell<Vm>>
}
impl Compiler {
pub fn new() -> Compiler {
Compiler{
scopes: Vec::new(),
loop_stmts: Vec::new(),
smap: Vec::new(),
files: Vec::new(),
modules_loaded: std::collections::HashSet::new(),
symbol: HashMap::new(),
sources: Vec::new(),
vm: Vm::new()
}
}
pub fn new_append_vm(vm: &mut Vm) -> Compiler {
use std::ptr::null_mut;
use crate::vmbindings::vmerror::VmError;
Compiler{
scopes: Vec::new(),
loop_stmts: Vec::new(),
smap: Vec::new(),
files: Vec::new(),
modules_loaded: std::collections::HashSet::new(),
symbol: HashMap::new(),
sources: Vec::new(),
vm: Rc::new(RefCell::new({
let mut vm_ = Vm::new_nil();
vm_.code = vm.code.deref();
vm_
}))
}
}
pub fn deref_vm_code(self) -> CArray<VmOpcode> {
vm!(self).code.deref()
}
pub fn is_in_function(&self) -> bool {
!self.scopes.is_empty()
}
fn get_local(&self, var : &String) -> Option<(u16, u16)> {
let mut relascope : u16 = 0;
for scope in self.scopes.iter().rev() {
if let Some(slot) = scope.vars.iter().position(|x| *x == *var) {
return Some((slot as u16, relascope));
}
relascope += 1;
}
None
}
pub fn set_local(&mut self, var : String) -> Option<(u16, u16)> {
if let Some(last) = self.scopes.last_mut() {
last.vars.push(var);
let idx = last.vars.len()-1;
return Some((idx as u16, 0));
}
None
}
pub fn emit_set_var(&mut self, var : String) {
if var.starts_with("$") || self.scopes.len() == 0 {
vm!(self).code.push(VmOpcode::OP_SET_GLOBAL);
vm!(self).cpushs(if var.starts_with("$") { &var[1..] }
else { var.as_str() });
} else if let Some(local) = self.get_local(&var) {
let mut slot = local.0;
let relascope = local.1;
if relascope != 0 {
let local = self.set_local(var.clone()).unwrap();
slot = local.0;
}
vm!(self).code.push(VmOpcode::OP_SET_LOCAL);
vm!(self).cpush16(slot);
} else {
let local = self.set_local(var.clone()).unwrap();
let slot = local.0;
vm!(self).code.push(VmOpcode::OP_SET_LOCAL);
vm!(self).cpush16(slot);
}
}
pub fn emit_set_var_fn(&mut self, var : String) {
if var.starts_with("$") || self.scopes.len() == 0 {
vm!(self).code.push(VmOpcode::OP_SET_GLOBAL);
vm!(self).cpushs(if var.starts_with("$") { &var[1..] }
else { var.as_str() });
} else if let Some(local) = self.get_local(&var) {
let mut slot = local.0;
let relascope = local.1;
if relascope != 0 {
let local = self.set_local(var.clone()).unwrap();
slot = local.0;
}
vm!(self).code.push(VmOpcode::OP_SET_LOCAL_FUNCTION_DEF);
vm!(self).cpush16(slot);
} else {
let local = self.set_local(var.clone()).unwrap();
let slot = local.0;
vm!(self).code.push(VmOpcode::OP_SET_LOCAL_FUNCTION_DEF);
vm!(self).cpush16(slot);
}
}
pub fn emit_get_var(&mut self, var : String) {
let local = self.get_local(&var);
if var.starts_with("$") || !local.is_some() {
vm!(self).code.push(VmOpcode::OP_GET_GLOBAL);
vm!(self).cpushs(if var.starts_with("$") { &var[1..] }
else { var.as_str() });
} else {
let local = local.unwrap();
let slot = local.0;
let relascope = local.1;
if relascope == 0 {
vm!(self).code.push(VmOpcode::OP_GET_LOCAL);
vm!(self).cpush16(slot);
} else {
vm!(self).code.push(VmOpcode::OP_GET_LOCAL_UP);
vm!(self).cpush16(slot);
vm!(self).cpush16(relascope);
}
}
}
pub fn reserve_label(&mut self) -> usize {
let pos = vm!(self).code.len();
vm!(self).cpush32(0xdeadbeef);
pos
}
pub fn reserve_label16(&mut self) -> usize {
let pos = vm!(self).code.len();
vm!(self).cpush16(0);
pos
}
pub fn fill_label16(&mut self, pos: usize, label: u16) {
vm!(self).cfill_label16(pos, label);
}
pub fn scope(&mut self) {
self.scopes.push(Scope::new());
}
pub fn unscope(&mut self) -> u16 {
let size = self.scopes.pop().unwrap().vars.len();
size as u16
}
pub fn loop_start(&mut self) {
self.loop_stmts.push(LoopStatement{
fill_continue: Vec::new(),
fill_break: Vec::new(),
});
}
pub fn loop_continue(&mut self) {
let label = self.reserve_label();
let ls = self.loop_stmts.last_mut().unwrap();
ls.fill_continue.push(label);
}
pub fn loop_break(&mut self) {
let label = self.reserve_label();
let ls = self.loop_stmts.last_mut().unwrap();
ls.fill_break.push(label);
}
pub fn loop_end(&mut self, next_it_pos : usize, end_pos : usize) {
let ls = self.loop_stmts.pop().unwrap();
for label in ls.fill_continue {
self.fill_label16(label, (next_it_pos - label) as u16);
}
for label in ls.fill_break {
self.fill_label16(label, (end_pos - label) as u16);
}
}
pub fn lookup_smap(&self, bc_idx: usize) -> Option<&SourceMap> {
let mut last_found : Option<&SourceMap> = None;
for smap in self.smap.iter() {
if (smap.bytecode.0..=smap.bytecode.1).contains(&bc_idx) {
last_found = Some(smap);
}
}
if last_found.is_some() { last_found }
else { None }
}
}