luallaby 0.1.0-alpha.3

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
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> {
    // Compiles prefix expression and value will be put on stack
    pub(super) fn compile_prefix_exp(&mut self, exp: PrefixExp) -> Result<usize> {
        let oper = self.convert_prefix_exp(exp)?;
        self.compile_prefix_oper(oper)
    }

    // Compiles prefix expression and outer prefix expression is returned as operand enum
    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),
            },
        })
    }

    // Compile prefix operand so that result ends up on stack
    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,
        })
    }
}