use std::rc::Rc;
use crate::ast::{Attr, Stat};
use crate::compiler::{Compiler, ScopeType};
use crate::error::Result;
use crate::vm::{Literal, OpCode, VarType};
use crate::LuaError;
use super::Variable;
impl Compiler {
pub(super) fn compile_stat(&mut self, stat: Stat) -> Result<()> {
match stat {
Stat::Nop => {}
Stat::Ass(ass) => self.compile_ass(ass)?,
Stat::Call(pos, exp, args) => {
let reg_ret = self.compile_call(pos, exp, args, false)?;
self.scopes.reg_free(reg_ret);
}
Stat::CallMethod(pos, prefix, name, args) => {
let reg_ret = self.compile_call_method(pos, *prefix, name, args, false)?;
self.scopes.reg_free(reg_ret);
}
Stat::Func(prefix, name, func) => {
let pos_start = func.pos_start;
let src_reg = self.compile_func(func)?;
match prefix {
Some(prefix) => {
let tbl_reg = self.compile_prefix_exp(prefix)?;
let ind_reg = self.scopes.reg_reserve();
self.code.emit(
OpCode::Lit {
val: Literal::String(Rc::new(name.into_bytes())),
dst_reg: ind_reg,
},
pos_start,
);
self.code.emit(
OpCode::TableSet {
src_reg,
tbl_reg,
ind_reg,
},
pos_start,
);
self.scopes.reg_free(ind_reg);
self.scopes.reg_free(tbl_reg);
}
None => self.code.emit(
match self.scopes.get(&name) {
Variable::Direct(var, attr) => {
if matches!(attr, Some(Attr::Const | Attr::Close)) {
return err!(LuaError::AssignToConst(name));
}
match var {
VarType::Local(dst_loc) => {
OpCode::LocalSet { src_reg, dst_loc }
}
VarType::Up(dst_up) => OpCode::UpSet { src_reg, dst_up },
}
}
Variable::Global(env) => OpCode::GlobalSet { src_reg, name, env },
},
pos_start,
),
}
self.scopes.reg_free(src_reg);
}
Stat::LocalFunc(name, func) => {
let pos = func.pos_end;
let dst_loc = self.declare_local(name, None, pos);
let src_reg = self.compile_func(func)?;
self.code.emit(OpCode::LocalSet { src_reg, dst_loc }, pos);
self.scopes.reg_free(src_reg);
}
Stat::Local(local) => self.compile_local(local)?,
Stat::Do(block) => {
self.scope_enter(ScopeType::Do);
self.compile_block(block)?;
self.scope_leave(ScopeType::Do, self.code.get_pos_last())?;
}
Stat::If(iff) => {
self.compile_if(iff)?;
}
Stat::Break(pos_break) => {
let pc = self.code.current_pc();
self.code.emit(
OpCode::JumpClose {
loc_from: 0,
off: 0,
},
pos_break,
);
self.scopes.break_insert(pc);
}
Stat::While(r#while) => {
self.compile_while(r#while)?;
}
Stat::Repeat(repeat) => {
self.compile_repeat(repeat)?;
}
Stat::For(r#for) => {
self.compile_for(r#for)?;
}
Stat::Goto(label, pos) => {
self.scopes.goto_insert(label, self.code.current_pc());
self.code.emit(
OpCode::JumpClose {
loc_from: 0,
off: 0,
},
pos,
); }
Stat::Label(label, pos) => {
self.scopes
.label_insert(label, self.code.current_pc(), pos)?;
}
}
Ok(())
}
}