unluac 1.1.1

Multi-dialect Lua decompiler written in Rust.
Documentation
# Parser

## 职责

Parser 负责把某个 dialect 的字节流解析成保真的 raw 模型。

这一层应当一次性保存后面各层恢复会依赖的协议事实,包括:

- chunk header 与版本布局
- proto 树与 child 关系
- 常量池、upvalue、debug 信息
- 已解码的 raw instruction 与扩展字绑定关系
- 会影响后面 lowering 的 dialect extra

## 入口与模块

### 对外入口

- `src/parser/mod.rs`
  - `parse_chunk`
  - `parse_lua51_chunk`
  - `parse_lua52_chunk`
  - `parse_lua53_chunk`
  - `parse_lua54_chunk`
  - `parse_lua55_chunk`
  - `parse_luajit_chunk`
  - `parse_luau_chunk`

### 共享模型

- `src/parser/raw.rs`
- `src/parser/error.rs`
- `src/parser/options.rs`
- `src/parser/reader.rs`

### dialect 共享层

- `src/parser/dialect/opcodes.rs`
- `src/parser/dialect/puc_lua/header.rs`
- `src/parser/dialect/puc_lua/layout.rs`
- `src/parser/dialect/puc_lua/instruction.rs`
- `src/parser/dialect/puc_lua/proto.rs`
- `src/parser/dialect/puc_lua/sections.rs`
- `src/parser/dialect/puc_lua/strings.rs`
- `src/parser/dialect/puc_lua/macros.rs`

### dialect 专属层

- `src/parser/dialect/lua5x/{raw,parser,debug}.rs`
- `src/parser/dialect/luajit/{raw,parser,debug}.rs`
- `src/parser/dialect/luau/{raw,parser,debug}.rs`

## 应消费的输入

Parser 只应消费:

- 原始字节流
- `ParseOptions`
- 调用方显式选择的 dialect 入口,或 `parse_chunk` 的版本探测结果

Parser 不应依赖:

- 任何 lower、CFG、Dataflow、Structure、HIR 层的推断
- 为了兼容后层而发明的伪语义

## 应优先复用的共享设施

### reader

应优先通过 `BinaryReader` 读取稳定二进制事实:

- `read_varint_u32_lua54`
- `read_varint_u32_lua55`
- `read_varint_u32_luau`
- `read_u32_le`
- `read_i32_le`
- `read_f32_le`
- `read_f64_le`

新的字节级 helper 只有在多个 parser 共同需要时才上移到这里。

### opcode / operand 宏

- `define_opcode_enum!`
- `define_opcode_kind_table!`
- `define_puc_lua_opcodes!`
- `define_puc_lua_instruction_codec!`

新的 opcode family 如果只是“声明表 + enum + label + operand kind”,应优先接入这些宏,而不是再写大段 `match`。

### raw accessor

`src/parser/raw.rs` 提供的 accessor 是后层消费 raw 事实的正式入口,例如:

- dialect extra accessor
- raw instr typed view
- const pool accessor
- header/layout accessor

如果某个协议事实已经能稳定表达在 raw 模型里,就应优先补 accessor,而不是让后层直接匹配 enum 内部形状。

## 维护规范

### 1. 协议事实必须在 parser/raw 收口

如果某个事实来源于字节编码,而不是运行语义,就应在 parser 阶段固化。

典型例子:

- `EXTRAARG` 与前一条指令的绑定
- Luau closure const 到 child proto slot 的映射
- Lua 5.5 字符串复用展开
- 各 dialect opcode 的 label、operand kind、extra-word policy

也要注意“协议里本来就没有”的边界:

- Lua 5.4 / 5.5 chunk 里的 debug local 只序列化 `name/startpc/endpc`
- 因此 `local <const>` 不是 parser 能从 chunk 里读出的事实
- `local <close>` 之所以还能恢复,依赖的是后层消费 `TBC/CLOSE` 一类运行时语义线索,而不是 debug local 本身

### 2. parser.rs 只负责“按协议顺序读取”

`parser.rs` 适合放:

- 某个 dialect 的读取顺序
- 该 dialect 的字段合法性校验
- 把字段填回 raw 模型

`parser.rs` 不适合长期保留:

- opcode label 大表
- operand decode 大表
- 可共享的 varint / endian / section driver 样板

### 3. raw.rs 只保存稳定事实

`raw.rs` 适合保存:

- 后层会直接消费的 typed extra
- 已经解析好的协议映射
- 能稳定复用的 accessor

`raw.rs` 不适合保存:

- 只为单个 parser 内部临时读取而存在的中间态
- 后层永远不会用到的重复缓存

### 4. debug.rs 只展示本 dialect parser 产物

每个 dialect 的 `debug.rs` 只应:

- 解构本 dialect raw 结构
- 输出稳定、可读的 parser dump

它不应再做跨 dialect 判断,也不应重新校验 parser 已经保证的版本约束。

### 5. 错误尽量在 parser 边界暴露

如果某个协议关系不合法,应优先在 parser 报 `ParseError`,而不是拖到 transformer 或更后层再暴露。

## 向后提供的事实

Parser 向后正式提供:

- `RawChunk`
- `ChunkHeader`
- `RawProto`
- `RawInstr`
- `RawConstPool`
- `RawUpvalueInfo`
- `RawDebugInfo`
- `RawString`
- 所有 dialect raw extra

这些事实默认被视为“后层不需要再猜补”的正式输入。

## 维护检查清单

修改 Parser 时,应至少检查:

1. 这个事实是否属于字节协议,而不是 lowering 语义。
2. 这个事实是否已经在 raw 模型里,只是缺 accessor。
3. 这个读取规则是否其实属于共享 reader / opcode macro / PUC-Lua helper。
4. `parser.rs` 是否只剩协议时序,而不是又长出一份静态表。
5. `debug.rs` 是否继续只是本 dialect dump。