use crate::ast::{Exp, PrefixExp};
use crate::compiler::Compiler;
use crate::error::Result;
use crate::vm::{OpCode, Oper, VarType};
pub enum PrefixOper {
Exp(Exp),
Field(PrefixExp, Exp),
Global(String),
Local(usize),
Up(usize),
Reg(usize),
}
impl<'a> Compiler<'a> {
pub(super) fn compile_prefix_exp(&mut self, exp: PrefixExp) -> Result<usize> {
let oper = self.convert_prefix_exp(exp)?;
self.compile_prefix_oper(oper)
}
pub(super) fn compile_prefix_exp_to_oper(&mut self, exp: PrefixExp) -> Result<Oper> {
let oper = self.convert_prefix_exp(exp)?;
Ok(match oper {
PrefixOper::Exp(exp) => {
let reg = self.compile_exp(exp)?;
self.code.emit(OpCode::Single { reg });
Oper::Reg(reg)
}
PrefixOper::Field(prefix, exp) => {
let reg_tbl = self.compile_prefix_exp(prefix)?;
let reg_ind = self.compile_exp(exp)?;
Oper::Field(reg_tbl, reg_ind)
}
PrefixOper::Global(name) => Oper::Global(name),
PrefixOper::Local(local) => Oper::Local(local),
PrefixOper::Up(up) => Oper::Up(up),
PrefixOper::Reg(r) => Oper::Reg(r),
})
}
fn convert_prefix_exp(&mut self, exp: PrefixExp) -> Result<PrefixOper> {
Ok(match exp {
PrefixExp::Call(pos, prefix, args) => {
let reg = self.compile_call(pos, *prefix, args)?;
PrefixOper::Reg(reg)
}
PrefixExp::CallMethod(pos, prefix, name, args) => {
let reg = self.compile_call_method(pos, *prefix, name, args)?;
PrefixOper::Reg(reg)
}
PrefixExp::Index(prefix, exp) => PrefixOper::Field(*prefix, *exp),
PrefixExp::Par(exp) => PrefixOper::Exp(*exp),
PrefixExp::Var(name) => match self.scopes.get(&name) {
Some((var, _)) => match var {
VarType::Local(ind) => PrefixOper::Local(ind),
VarType::Up(ind) => PrefixOper::Up(ind),
},
None => PrefixOper::Global(name),
},
})
}
fn compile_prefix_oper(&mut self, oper: PrefixOper) -> Result<usize> {
Ok(match oper {
PrefixOper::Exp(exp) => {
let reg = self.compile_exp(exp)?;
self.code.emit(OpCode::Single { reg });
reg
}
PrefixOper::Field(prefix, exp) => {
let tbl_reg = self.compile_prefix_exp(prefix)?;
let ind_reg = self.compile_exp(exp)?;
let dst_reg = self.scopes.reg_reserve();
self.code.emit(OpCode::TableGet {
tbl_reg,
ind_reg,
dst_reg,
});
self.scopes.reg_free(tbl_reg);
self.scopes.reg_free(ind_reg);
dst_reg
}
PrefixOper::Global(name) => {
let dst_reg = self.scopes.reg_reserve();
self.code.emit(OpCode::GlobalGet { name, dst_reg });
dst_reg
}
PrefixOper::Local(src_loc) => {
let dst_reg = self.scopes.reg_reserve();
self.code.emit(OpCode::LocalGet { src_loc, dst_reg });
dst_reg
}
PrefixOper::Up(src_up) => {
let dst_reg = self.scopes.reg_reserve();
self.code.emit(OpCode::UpGet { src_up, dst_reg });
dst_reg
}
PrefixOper::Reg(r) => r,
})
}
}