use crate::ast::{Ass, Attr, Var};
use crate::compiler::Compiler;
use crate::error::Result;
use crate::vm::{OpCode, VarType};
impl<'a> Compiler<'a> {
pub(super) fn compile_ass(&mut self, ass: Ass) -> Result<()> {
let mut end = Vec::new();
let mut to_free = Vec::new();
let exp_regs: Vec<usize> = ass
.exps
.into_iter()
.map(|exp| self.compile_exp(exp))
.collect::<Result<_>>()?;
for (ind, var) in ass.vars.into_iter().enumerate() {
let src_reg = if ind + 1 < exp_regs.len() {
exp_regs[ind]
} else {
let dst_reg = self.scopes.reg_reserve();
self.code.emit(OpCode::MovMult {
src_reg: *exp_regs.last().unwrap(),
ind: ind + 1 - exp_regs.len(),
dst_reg,
});
to_free.push(dst_reg);
dst_reg
};
match var {
Var::Name(name) => match self.scopes.get(&name) {
Some((dst_var, attr)) => {
if matches!(attr, Some(Attr::Const)) {
panic!("cannot assign to const");
}
match dst_var {
VarType::Local(dst_loc) => {
end.push(OpCode::LocalSet { src_reg, dst_loc })
}
VarType::Up(dst_up) => end.push(OpCode::UpSet { src_reg, dst_up }),
}
}
None => {
end.push(OpCode::GlobalSet { src_reg, name });
}
},
Var::Index(prefix, exp) => {
let tbl_reg = self.compile_prefix_exp(prefix)?;
let ind_reg = self.compile_exp(exp)?;
end.push(OpCode::TableSet {
src_reg,
tbl_reg,
ind_reg,
});
to_free.push(tbl_reg);
to_free.push(ind_reg);
}
};
}
self.code.emit_all(end.into_iter());
for reg in exp_regs {
self.scopes.reg_free(reg);
}
for reg in to_free {
self.scopes.reg_free(reg);
}
Ok(())
}
}