luallaby 0.1.0-alpha.3

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
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<()> {
        // Delay all assignments until end, save here
        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(())
    }
}