luallaby 0.1.0

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
use crate::ast::{Block, Exp, PrefixExp};
use crate::compiler::Compiler;
use crate::error::Result;
use crate::vm::{Literal, OpCode};

impl Compiler {
    pub(super) fn compile_block(&mut self, block: Block) -> Result<bool> {
        for stat in block.stats {
            self.compile_stat(stat)?;
        }

        Ok(if let Some((mut exps, pos)) = block.ret {
            if !self.scopes.has_to_be_closed() && exps.len() == 1 {
                let exp = exps.pop().unwrap();
                match exp {
                    Exp::Prefix(PrefixExp::Call(pos_call, prefix, args)) => {
                        let ret_reg = self.compile_call(pos_call, *prefix, args, true)?;
                        self.code.emit(OpCode::Return { ret_reg }, pos);
                        self.scopes.reg_free(ret_reg);
                    }
                    Exp::Prefix(PrefixExp::CallMethod(pos_call, prefix, name, args)) => {
                        let ret_reg =
                            self.compile_call_method(pos_call, *prefix, name, args, true)?;
                        self.code.emit(OpCode::Return { ret_reg }, pos);
                        self.scopes.reg_free(ret_reg);
                    }
                    exp => {
                        let ret_reg = self.scopes.reg_reserve();
                        self.code.emit(
                            OpCode::Lit {
                                val: Literal::Empty,
                                dst_reg: ret_reg,
                            },
                            pos,
                        );
                        let exp_reg = self.compile_exp(exp)?;
                        self.code.emit(
                            OpCode::Extend {
                                src_reg: exp_reg,
                                dst_reg: ret_reg,
                            },
                            pos,
                        );
                        self.scopes.reg_free(exp_reg);
                        self.code.emit(OpCode::Return { ret_reg }, pos);
                        self.scopes.reg_free(ret_reg);
                    }
                }
            } else {
                let ret_reg = self.scopes.reg_reserve();
                self.code.emit(
                    OpCode::Lit {
                        val: Literal::Empty,
                        dst_reg: ret_reg,
                    },
                    pos,
                );
                let last = exps.pop();
                for exp in exps.into_iter() {
                    let exp_reg = self.compile_exp(exp)?;
                    self.code.emit(OpCode::Single { reg: exp_reg }, pos);
                    self.code.emit(
                        OpCode::Append {
                            src_reg: exp_reg,
                            dst_reg: ret_reg,
                        },
                        pos,
                    );
                    self.scopes.reg_free(exp_reg);
                }
                if let Some(exp) = last {
                    let exp_reg = self.compile_exp(exp)?;
                    self.code.emit(
                        OpCode::Extend {
                            src_reg: exp_reg,
                            dst_reg: ret_reg,
                        },
                        pos,
                    );
                    self.scopes.reg_free(exp_reg);
                }
                self.code.emit(OpCode::Return { ret_reg }, pos);
                self.scopes.reg_free(ret_reg);
            }

            true
        } else {
            false
        })
    }
}