luallaby 0.1.0

**Work in progress** A pure-Rust Lua interpreter/compiler
Documentation
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) => {
                // Jump to end of loop, placeholder
                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,
                ); // Placeholder
            }
            Stat::Label(label, pos) => {
                self.scopes
                    .label_insert(label, self.code.current_pc(), pos)?;
            }
        }
        Ok(())
    }
}