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