添加可选链操作符支持 (?. ?: ?[ ?():支持多返回值,新增 OP_SETTOP 指令,由 BEE_OPTCHAIN 宏启用 - #67
Open
sumneko wants to merge 8 commits into
Open
添加可选链操作符支持 (?. ?: ?[ ?():支持多返回值,新增 OP_SETTOP 指令,由 BEE_OPTCHAIN 宏启用#67sumneko wants to merge 8 commits into
sumneko wants to merge 8 commits into
Conversation
- 补丁位于 3rd/lua-patch/optchain/{lua54,lua55}/,不改动 vendored 源码
- 构建时 luamake -optchain 开启并定义 BEE_OPTCHAIN,默认关闭行为不变
- 新增 test/test_optional_chain.lua(test.lua 探测到可用时才加载)
- 语义对齐 ES2020:整链短路、false 不短路、不可作赋值目标
There was a problem hiding this comment.
Pull request overview
This PR adds an optional-chaining syntax extension to the vendored Lua 5.4/5.5 parser copies, gated behind a BEE_OPTCHAIN build macro and wired into the build/test flow so it only runs when the feature is enabled.
Changes:
- Add
?./?:/?[/?()optional-chaining support in patchedlparser.ccopies for Lua 5.4 and 5.5 (macro-gated). - Update build scripts to switch to the patched
onelua.cand defineBEE_OPTCHAINwhen-optchainis enabled. - Add optional-chaining test coverage and a loader that auto-skips these tests on builds without the syntax.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
test/test.lua |
Conditionally loads the optional-chaining test module only when the syntax is accepted by the interpreter. |
test/test_optional_chain.lua |
New test suite covering field/index/method/call chaining semantics and short-circuit behavior. |
compile/lua.lua |
Enables selecting patched onelua.c and defining BEE_OPTCHAIN for relevant targets when lm.optchain is on. |
compile/common.lua |
Switches the Lua source set to the patched onelua.c and defines BEE_OPTCHAIN when enabled. |
3rd/lua-patch/optchain/lua55/onelua.c |
Patched Lua 5.5 single-file build entry to include patched parser sources. |
3rd/lua-patch/optchain/lua55/lparser.c |
Patched Lua 5.5 parser copy with optional-chaining parsing/codegen (macro-gated). |
3rd/lua-patch/optchain/lua54/onelua.c |
Patched Lua 5.4 single-file build entry to include patched parser sources. |
3rd/lua-patch/optchain/lua54/lparser.c |
Patched Lua 5.4 parser copy with optional-chaining parsing/codegen (macro-gated). |
.luarc.json |
Adjusts LuaLS workspace settings to reduce diagnostics noise from the custom syntax in tests. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+1229
to
+1242
| case '?': { /* optional chain: '?.' '?:' '?[' '?(' */ | ||
| int reg, nilreg; | ||
| luaX_next(ls); /* consume '?' */ | ||
| if (ls->t.token != '.' && ls->t.token != ':' && | ||
| ls->t.token != '[' && ls->t.token != '(') | ||
| luaX_syntaxerror(ls, "unexpected symbol near '?'"); | ||
| reg = luaK_exp2anyreg(fs, v); /* evaluate receiver only once */ | ||
| nilreg = fs->freereg; | ||
| luaK_nil(fs, nilreg, 1); /* ensure it is nil at runtime */ | ||
| fs->freereg++; | ||
| luaK_codeABCk(fs, OP_EQ, reg, nilreg, 0, 1); /* jump when nil */ | ||
| luaK_concat(fs, &niljumps, luaK_jump(fs)); | ||
| break; /* next iteration handles '.' ':' '[' '(' */ | ||
| } |
Comment on lines
+1116
to
+1129
| case '?': { /* optional chain: '?.' '?:' '?[' '?(' */ | ||
| int reg, nilreg; | ||
| luaX_next(ls); /* consume '?' */ | ||
| if (ls->t.token != '.' && ls->t.token != ':' && | ||
| ls->t.token != '[' && ls->t.token != '(') | ||
| luaX_syntaxerror(ls, "unexpected symbol near '?'"); | ||
| reg = luaK_exp2anyreg(fs, v); /* evaluate receiver only once */ | ||
| nilreg = fs->freereg; | ||
| luaK_nil(fs, nilreg, 1); /* ensure it is nil at runtime */ | ||
| fs->freereg++; | ||
| luaK_codeABCk(fs, OP_EQ, reg, nilreg, 0, 1); /* jump when nil */ | ||
| luaK_concat(fs, &niljumps, luaK_jump(fs)); | ||
| break; /* next iteration handles '.' ':' '[' '(' */ | ||
| } |
sumneko
marked this pull request as draft
August 13, 2026 22:32
可选链调用(f?())短路路径改为固定布局:CALL(k 标志标记可选链) / JMP / OP_SETTOP。
OP_SETTOP 填 R[A..A+B] 为 nil 并精确设置 L->top,使开放指令
(OP_RETURN/OP_CALL/OP_SETLIST)读到恰好数量的 nil:
- return f?() 短路 -> 恰好 1 个 nil
- g(f?()) 短路 -> 恰好 1 个参数
- {f?()} 短路 -> 恰好 1 个 nil 元素
- a,b,c = f?() 短路 -> 3 个 nil
不再把短路指令位置 hack 进 expdesc->t(修复了 dischargevars/goiftrue
将 e->t 当作跳转列表 patch 导致的编译期崩溃);e->t/f 保持官方语义,
单值消费(print(f?())、if f?() then、f?()+1)天然安全。
OP_SETTOP 追加在 OP_EXTRAARG 之后,普通代码指令编号不变;
默认构建(无 BEE_OPTCHAIN)零影响。
将 case '?' 中裸的 fs->freereg++ 改为 luaK_reserveregs(fs, 1)(同时正确 更新 maxstacksize,避免绕过 luaK_checkstack),使用后立即释放。 曾尝试按 review 建议将 nil 寄存器一次性 reserve 到链末复用,但这会破坏 Lua 编译器的线性 freereg 不变量:链中间的 freeexp/exp2nextreg 假设待释放 表达式是最后分配的寄存器,reserve 保持会使链末 CALL 基址偏移、结果寄存器 与局部变量错位(CALL 0 变 CALL 1 + 多余 MOVE,返回错误值)。
验证 f?(1,2,3) 等带参数场景:非短路正常传参调用、短路时参数不求值 (无副作用)、多值短路精确、表构造/参数位置正确。
sumneko
marked this pull request as ready for review
August 14, 2026 00:20
Owner
|
这种补丁方式在版本升级时非常难维护 |
将可选链补丁从 3rd/lua-patch/optchain/ 的整文件复制改为直接写入
3rd/lua{54,55} 官方源码,全部由 #if defined(BEE_OPTCHAIN) 门控:
- lparser.c / lvm.c / lopcodes.{h,c} / lopnames.h:每文件仅增几行门控
补丁(lparser.c ~110 行、lvm.c 15 行、指令表各 3-7 行)
- OP_SETTOP 枚举/模式表/反汇编名均以 BEE_OPTCHAIN 门控,默认构建
预处理后与官方源码字节级一致,零影响
- compile 不再切换 onelua.c 来源,仅保留 -optchain 时定义宏
分支相对 master 的 diff 从 ~10100 行降至 ~500 行。
默认 + optchain(lua54/lua55)全部测试通过。
Collaborator
Author
|
改成在源码里插桩了,但感觉改的有点多。要不这个功能就留在分支里吧,之后master更新后让AI合到分支里 |
Owner
|
把它变成git diff提交到仓库,构建的时候apply? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
概述
为 Lua 添加可选链操作符(optional chaining)支持,语法与语义对齐 ES2020 的
?.,包含四种形式:x?.y— 字段访问x?:f()— 方法调用f?()— 函数调用t?[1]— 索引访问支持任意链式组合,如
a?.b.c、a?.b?.c、obj?:get()?.x。设计思路
解析器补丁 + 一个新增指令,默认构建零影响
实现直接修改
3rd/lua{54,55}的官方源码,所有改动都由#if defined(BEE_OPTCHAIN)宏门控:lparser.c):?.在编译期展开为标准的LOADNIL/EQ/JMP+ 字段访问/调用指令组合;OP_SETTOP:链末调用(f?()、obj?:m())的短路路径使用CALL(k) / JMP / OP_SETTOP固定布局,OP_SETTOP将结果寄存器填为 nil 并精确设置栈顶(L->top),使开放指令(OP_RETURN/OP_CALL/OP_SETLIST)读到恰好数量的 nil;OP_SETTOP追加在OP_EXTRAARG之后(同样由BEE_OPTCHAIN门控),普通代码的指令编号完全不变,补丁版与标准版对普通代码的字节码双向兼容;BEE_OPTCHAIN:所有补丁块被预处理剔除,与官方源码字节级一致、行为零影响;启用时仅需luamake -optchain。为什么需要新增指令(及备选方案)
Lua 的
LOADNIL只把寄存器填为 nil,不会调整栈顶L->top,因此链末调用在多值上下文(return f?()、g(f?())、{f?()})短路时,开放指令会读到栈上残留的旧值,产生"nil 数量偏多"。此前考虑过不加指令的绕过方案:短路路径改为调用一个预先准备的、返回 1 个 nil 的函数——OP_CALL会按实际结果数设置L->top,且在固定结果数时自动补足 nil(如a, b, c = nilfn()得 3 个 nil),可精确覆盖所有短路场景,语义上可行。但该方案:因此最终新增
OP_SETTOP:一条指令同时完成"寄存器填 nil"与"精确设置栈顶",语义直接、零运行时依赖与调用开销,且位于OP_EXTRAARG之后不影响普通代码的指令编号。语义(对齐 ES2020)
nil短路:false?.a仍会报错(与 JS 的undefined/null语义对齐);nil,整条链立即得到nil,后续 key / 实参不再求值(无副作用);recv()?.a?.b中recv()只调用一次;obj?.a = 1为语法错误;return f?()短路 → 恰好 1 个 nilg(f?())短路 → 恰好 1 个参数{f?()}短路 → 恰好 1 个 nil 元素a, b, c = f?()短路 → 3 个 nil构建与测试
luamake -optchain启用;test/test_optional_chain.lua,覆盖字段 / 索引 / 方法 / 调用(含带参)/ 链式组合 / 短路副作用 / 单次求值 / 不可赋值 / 非法语法 / 多返回值(含短路精确性)等用例;改动范围
3rd/lua{54,55}/lparser.cBEE_OPTCHAIN门控)3rd/lua{54,55}/lopcodes.{h,c}OP_SETTOP指令(枚举 / 模式表,门控)3rd/lua{54,55}/lopnames.h3rd/lua{54,55}/lvm.cvmcase(OP_SETTOP)(填 nil + 设置栈顶,门控)compile/common.lua、compile/lua.lua-optchain时定义BEE_OPTCHAIN宏test/test_optional_chain.lua、test/test.lua.luarc.json