luallaby 0.1.0

**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::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 {
    // Compiles prefix expression and value will be put in register
    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)
    }

    // 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, 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,
            ),
        })
    }

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