{"owner":"LuaLS","repo":"lua-language-server","hasSkills":true,"totalSkillsCount":3,"totalTokensCount":10493,"categories":["copilot-instructions","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":[".github/copilot-instructions.md",".github/skills/luajit-syntax/references/syntax-extensions.md",".github/skills/luajit-syntax/SKILL.md"],"skills":{".github/copilot-instructions.md":"# Coding Instructions\n\n## PowerShell 文件编码\n\n- Windows PowerShell 默认编码为 GBK/UTF-16 LE，**严禁**写入项目文件时不指定编码（会导致 UTF8 文件乱码）\n- `Set-Content`、`Out-File`、`echo >`、`Write-Output >` 等写入操作必须加 `-Encoding UTF8`\n- 正确示例：`Set-Content -Path file.ts -Value \"...\" -Encoding UTF8`\n- 项目所有源码文件统一 UTF-8（无 BOM），由 `.editorconfig` 和 ESLint 兜底\n\n## 长耗时命令轮询\n\n- 后台运行的长时间命令（如 `test.lua` 完整测试、`build-doc.lua`）**不会自动通知完成**，必须主动轮询结果文件确认\n- 完整测试：检查 `temp/test_full*.log` 是否出现 `test finish.`（无 error 即通过；full 阶段耗时较长，需多次轮询）\n- build-doc：检查退出码 `DOC=0`\n- 轮询方式：读取日志文件末尾或 `grep` 结束标记，间隔由命令耗时决定，不要依赖系统通知\n- 推荐命令模式（Windows PowerShell + cmd 重定向，规避管道无法捕获输出的问题）：\n  - `cmd /c \"cd /d <repo> && bin\\lua-language-server.exe test.lua --name=<test> > temp\\test_xx.log 2>&1 & echo EXIT=%ERRORLEVEL%\"`\n  - 完成后用 `read_file` / `grep_search` 查日志，或用 `Select-String` 找结束标记\n\n## Pull Request Review Language\n\nWhen performing pull request reviews, write every finding in both Chinese and English.\n\nUse this format for inline comments and review summaries:\n\n**中文**：<审查意见>\n\n**English**: <equivalent review comment>\n\nKeep both versions semantically equivalent. Report only actionable, evidence-based findings.\n",".github/skills/luajit-syntax/references/syntax-extensions.md":"# LuaJIT 3.0 语法扩展参考文档\n\n> 来源：[LuaJIT/LuaJIT#1475](https://github.com/LuaJIT/LuaJIT/issues/1475) MikePall 的第一条评论（权威文档），及 [LuaJIT/LuaJIT#1476](https://github.com/LuaJIT/LuaJIT/issues/1476) 反向移植说明。\n> 反向移植到 v2.1 的扩展由官方测试覆盖：`test/parser_test/LuaJIT/`（来自 gist [a8372d92cb2e6380cf56e78f69ee70a4](https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4)）。\n\n## 反向移植范围（v2.1 backport）\n\n**已移植：**\n1. 位运算：一元 `~`，二元 `& | ~ << >> ~>>`\n2. 自定义运算符 `! && || !=`\n3. 三元条件 `?:`（**本次实现已搁置**，见下文第 3 节）\n4. 安全导航 `?.`（含方法形式 `:?`，全部实现）\n5. 空值合并 `??`\n6. 复合赋值 `+= -= *= …`\n7. `continue` 语句\n8. `const` 声明\n9. 短函数表达式\n10. 数字字面量下划线\n\n**未移植（无需支持）：**\n- 位运算元方法\n- 地板除 `//`\n- 复合赋值元方法 `__add(a, b, true)`\n- 命名变参 `...name`\n\n## 运算符优先级表（从高到低）\n\n| 运算符 / 结构 | 元数 | 结合性 |\n|--------------|------|--------|\n| `( )` | 一元 | - |\n| `->`（短函数） | 二元 | 右 |\n| `.` `:` `[` `]` `f()` `?.` | 二元 | 左 |\n| `^` | 二元 | 右 |\n| `not` `!` `-` `~` `#` | 一元 | - |\n| `*` `/` `//` `%` | 二元 | 左 |\n| `+` `-` | 二元 | 左 |\n| `..` | 二元 | 右 |\n| `<<` `>>` `~>>` | 二元 | 左 |\n| `&` | 二元 | 左 |\n| `~` | 二元 | 左 |\n| `\\|` | 二元 | 左 |\n| `==` `~=` `!=` `<` `>` `<=` `>=` | 二元 | 左 |\n| `and` `&&` | 二元 | 左 |\n| `or` `\\|\\|` `??` | 二元 | 左 |\n| `?:` ⏸ | 三元 | 右 |\n| `=` `compound=` | 二元 | - |\n\n> 对应 `script/parser/compile.lua` 中 `BinarySymbol` 的数值等级（数值越大优先级越高）：\n> - `??` 与 `or` 同级（1）\n> - `?:` 已搁置（若未来实现：单独处理，优先级最低，在 `=` 之上）\n> - `~>>` 与 `<<`/`>>` 同级（7）\n\n## 各语法详解\n\n### 1. 位运算 `~ & | ~ << >> ~>>`\n\n| 运算符 | 功能 | 对应 bit.* 函数 |\n|--------|------|-----------------|\n| `~ a` | 按位取反 | `bit.bnot()` |\n| `a & b` | 按位与 | `bit.band()` |\n| `a \\| b` | 按位或 | `bit.bor()` |\n| `a ~ b` | 按位异或 | `bit.bxor()` |\n| `a << b` | 左移 | `bit.lshift()` |\n| `a >> b` | 逻辑右移 | `bit.rshift()` |\n| `a ~>> b` | **算术右移** | `bit.arshift()` |\n\n- `~>>`（算术右移）当前 **未实现**，是本规划要补的（tokenizer + `BinarySymbol`）。\n- 测试：`expr_bit.lua`、`ffi_expr_bit*.lua`。注意测试中 `LL`/`ULL` 后缀仅 LuaJIT 支持（`dropNumberTail` 已处理）。\n\n### 2. 自定义运算符 `! && || !=`\n\n| 自定义写法 | 等价经典写法 | 功能 |\n|-----------|--------------|------|\n| `!` | `not` | 逻辑非 |\n| `&&` | `and` | 短路与 |\n| `\\|\\|` | `or` | 短路或 |\n| `!=` | `~=` | 不相等比较 |\n\n- 语义与经典运算符完全一致。\n- compile.lua 已有 `UnaryAlias`（`!`）与 `BinaryAlias`（`&&`、`||`、`!=`）；`BinaryAlias` 带 `ERR_NONSTANDARD_SYMBOL` 检查。\n- 测试：`expr_customary.lua`。\n\n### 3. 三元条件 `?:`（研究完成，待实现）\n\n```\na ? b : c\n```\n- `a` 为条件（真值判定同 `if`）；为真求 `b`，否则求 `c`。\n- **右结合**，可嵌套：`cond1 ? a : cond2 ? b : x` = `cond1 ? a : (cond2 ? b : x)`。\n- 短路：未选中的分支不求值。\n- **语法歧义（源码确认）**：b 部分解析使用 `EXPR_F_NOCOLON` 标志（`lj_parse.c expr()`），在后缀解析中遇到 `:` 即 break，禁止方法调用；括号内方法调用需加括号：`cond ? (obj:method()) : default`。\n\n**NOCOLON 传播/重置规则**（源码 `lj_parse.c` 依据）：\n- 传播（eflags 透传）：一元操作数、二元操作数、后缀链（name/表/字符串后缀）\n- 重置（传 0）：括号 `expr(v,0)`、索引 `expr_bracket→expr(v,0)`、函数参数 `parse_args→expr_list→expr(v,0)`、表值、lambda\n- 推论：`a ? x + obj:method() : c` 也报错（操作数传播屏蔽）；`a ? f(obj:method()) : c` 合法（参数重置）\n\n**compile.lua 实现方案**：\n- `parseExp` 二元循环后处理三元（优先级最低、右结合）：b = `parseExp(nil, nil, true)`（noMethod），c = `parseExp()`\n- 加 `noMethod` 参数：`parseExp(asAction, level, noMethod)`、`parseExpUnit(noMethod)`、`parseSimple(node, funcName, noMethod)`\n- `parseSimple` 的 `:` 分支：`if noMethod then break`\n- 重置点：parseParen / parseIndex / parseExpList / parseTable 值 / parseLambda\n- tokenizer 无需改动（`?` `:` 均为单字符 token）\n- AST：`ternary` 节点（`[1]`=条件、`[2]`=b、`[3]`=c），需 guide + vm 类型推断\n- 测试：expr_cond.lua 移入正式测试\n\n**测试要点**（expr_cond.lua）：\n- `a ? obj:method() : c` → 报错 eof；`a ? (a:b()) : c` → 合法\n- `a ? a : b():c() + 30` → 合法（c 部分不屏蔽）\n- 无空格 `a?b:c` → 报错（方法调用歧义）；`a ? : c`、`a ? b :` → 报错\n\n### 4. 安全导航 `?.`\n\n| 原表达式 | 安全导航写法 | 检查对象 |\n|----------|--------------|----------|\n| `a[key]` | `a?.[key]` | a |\n| `a.field` | `a?.field` | a |\n| `a[key] = expr` | `a?.[key] = expr` | a |\n| `a.field = expr` | `a?.field = expr` | a |\n| `f(…)` | `f?.(…)` | f |\n| `f\"…\"` | `f?.\"…\"` | f |\n| `f{…}` | `f?.{…}` | f |\n| `obj:method(…)` | `obj?.:method(…)` | obj |\n| `obj:method(…)` | `obj:method?.(…)` | method |\n| `obj:method(…)` | `obj?.:method?.(…)` | 两者 |\n\n- 规则：`.` 可直接替换为 `?.`；调用类（`()`, `\"\"`, `{}`）与 `:` 需要 `?.` 和原符号**同时出现**。\n- 左结合，可链式：`a?.b?.[c]?.d?.:e?.()`（五种检查全部支持）。\n- 短路：左侧为 nil（或 NULL cdata）时，右侧操作与后续链不执行；赋值时右侧表达式也不求值。\n- 本规划实现 `?.` 的**全部形式**：`.`/`[]`/`()`/`\"\"`/`{}`、赋值形式、以及 `:?` 方法形式（`obj?.:method`、`obj:method?.`、`obj?.:method?.`）。\n- 实现提示：`parseSimple` 中需区分 token 序列 `?.`+`:`（obj?.:method）与 `:`+`?.`（obj:method?.），前者检查 obj，后者检查 method。\n- 测试：`expr_nav.lua`（全部用例应通过）。\n\n### 5. 空值合并 `??`\n\n```\na ?? b\n```\n- 仅当 `a` 等于 nil 时求 `b` 并返回，否则返回 `a`（`a` 只求值一次）。\n- 与 `or` 同级优先级（`or || ??`），短路。\n- 测试：`expr_coal.lua`。\n\n### 6. 复合赋值\n\n| 运算符 | 功能 | 长形式 |\n|--------|------|--------|\n| `a += b` | 加 | `a = a + b` |\n| `a -= b` | 减 | `a = a - b` |\n| `a *= b` | 乘 | `a = a * b` |\n| `a /= b` | 除 | `a = a / b` |\n| `a %= b` | 取模 | `a = a % b` |\n| `a &= b` | 按位与 | `a = a & b` |\n| `a \\|= b` | 按位或 | `a = a \\| b` |\n| `a ~= b` | 按位异或 | `a = a ~ b` |\n| `a <<= b` | 左移 | `a = a << b` |\n| `a >>= b` | 逻辑右移 | `a = a >> b` |\n| `a ~>>= b` | **算术右移** | `a = a ~>> b` |\n| `a ..= b` | **连接** | `a = a .. b` |\n\n- 索引表达式只求值一次前缀：`x.y.z.field += b` ≈ `local tmp = x.y.z; tmp.field = tmp.field + b`。\n- **不支持**并行赋值：`x, y, z += u, v, w` 为语法错误。\n- **没有** `^=`（故意省略，避免与异或混淆）。\n- 可与安全导航组合：`a?.field += f()`。\n- 现状：`+= -= *= /= %= &= |= <<= >>=` 已支持；需新增 `..=` 与 `~>>=`。\n- 测试：`stmt_compound.lua`。\n\n### 7. `continue` 语句\n\n- 跳转语义：`while`/`repeat` → 循环条件；`for` → 循环更新逻辑。\n- 只能作为（嵌套）块的最后一个语句（同 `break`）。\n- **soft keyword**：可作变量名/字段名/函数名/参数名。\n- `repeat` 中不能跳入其后声明的局部变量作用域（同 `goto` 规则）：\n  `repeat if x then continue end; local a = a; until not a` → 报错。\n- 现状：`parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`），但语义是当作 break 处理，需要确认 `repeat`/`for` 的跳转目标正确性。\n- 测试：`stmt_continue.lua`。\n\n### 8. `const` 声明\n\n```\nconst x = 1\nconst y, z = 2, 3\nconst foo                 -- 无赋值，值为 nil，但不可再赋值\nconst function foo() end  -- const 局部函数\n```\n- 块级局部常量：不可重新赋值、不可在相同或内层作用域重复声明、不可作为函数参数重复声明。\n- **soft keyword**：可作变量名、字段名、函数名、参数名（`local const = 1` 合法；`const const = 1` 也合法——第一个是关键字，第二个是名字）。\n- 变量本身不可变，但引用对象（如 table）内容可变。\n- 需新增 `parseAction` 支持；错误码参考测试中的 `assign to const` / `declare const`。\n- 测试：`stmt_const.lua`。\n\n### 9. 短函数表达式\n\n```\n|…| -> expr          -- 返回 expr 结果\n|…| -> do … end      -- 语句块，无 return 则返回 0 个结果\nx -> x+1             -- 单参数可省略管道\n|| -> true           -- 无参数\n|a, b, ...| -> ...   -- 支持变参\n```\n- 可在任何函数表达式位置使用。\n- expr 变体只返回**单个**结果（多返回用语句块形式）。\n- `->` 优先级最高（仅低于 `()`），右结合。\n- 现状：`|x| -> expr` 已有（`|lambda|`），但 `->` 无独立 token；需补 `x -> expr`、`|| -> expr`、`-> do … end`。\n- 测试：`expr_shortfunc.lua`。\n\n### 10. 数字字面量下划线\n\n```\n1_234_567_890\n0x1_2     0b1_0\n1_2.3_4   1e1_0\n0x1_2p_3\n```\n- 下划线在**任何位置**（初始数字之后）都被忽略，适用于所有进制、所有后缀（含 `LL`/`ULL`）、整数与浮点。\n- `tonumber()` 与字符串隐式转换**不接受**下划线。\n- 需修改 tokenizer 的 `Number` 规则与 `parseNumber10/16/2`。\n- 测试：`number_underscore.lua`。\n\n## 测试文件对照表\n\n| 文件 | 覆盖语法 |\n|------|----------|\n| `expr_bit.lua` | 位运算（`~ & \\| << >> ~>>`） |\n| `expr_bit_bitop.lua` | `bit.*` 库运行时行为（语法层面价值低） |\n| `expr_coal.lua` | `??` 空值合并 |\n| `expr_cond.lua` ⏸ | `?:` 三元条件（已搁置，本阶段不要求通过） |\n| `expr_customary.lua` | `! && \\|\\| !=` |\n| `expr_nav.lua` | `?.` 安全导航（全部形式，含 `:?` 方法形式） |\n| `expr_shortfunc.lua` | 短函数 `->` |\n| `ffi_expr_bit.lua` / `_bit64` / `_bitop` | FFI 位运算 + `LL`/`ULL` 后缀 |\n| `number_underscore.lua` | 数字下划线 |\n| `stmt_compound.lua` | 复合赋值 |\n| `stmt_const.lua` | `const` 声明 |\n| `stmt_continue.lua` | `continue` 语句 |\n\n> 补充：`expr_cond.lua`（三元 `?:`）已搁置，本阶段不要求通过。\n> 所有文件内容现已被包裹为 `return [[ … ]]` 字符串形式，可作为代码字符串测试源。\n",".github/skills/luajit-syntax/SKILL.md":"---\nname: luajit-syntax\ndescription: 'LuaJIT 3.0 扩展语法支持的实施规划与实现指南。Use when implementing LuaJIT extension syntax support (bitwise ~>>, ternary ?:, safe navigation ?., nil-coalescing ??, customary operators ! && || !=, compound assignment, continue, const declaration, short function expression, number underscores) in the Lua language server, when modifying script/parser/compile.lua, script/parser/tokens.lua, script/config/template.lua, script/files.lua, or adding tests under test/parser_test/LuaJIT/.'\n---\n\n# LuaJIT 扩展语法支持规划\n\n## 实施进度\n\n| 阶段 | 内容 | 状态 |\n|------|------|------|\n| 阶段 0 | 设置 `Lua.runtime.enableLuaJITExtensions`、options 传递、initState 判定（语法处单独判断） | ✅ 完成 |\n| 阶段 1 | tokenizer 新增 `-> ?? ?. ~>> ~>>= ..=` | ✅ 完成 |\n| 阶段 2 | 语法解析（`~>>`、复合赋值、数字下划线、`??`、`?.` 全形式、`const`、短函数、`continue` soft keyword、`a ~= b` 异或赋值） | ✅ 完成 |\n| 阶段 3 | 语义层（`?.`→optional、`??` 类型合并、`~>>`→integer） | ✅ 完成 |\n| 阶段 4 | 诊断文案 `PARSER_DECLARE_CONST`（6 语言） | ✅ 完成 |\n| 阶段 5 | 正式测试接入 `test/parser_test/luajit_ext.lua` | ✅ 完成 |\n| 阶段 6 | 三元 `?:`（noMethod/noTernary 机制、ternary AST 节点、vm 类型推断） | ✅ 完成 |\n\n验证：\n- 语法层 40 用例（`temp/tmp_test_luajit.lua`）、语义层 7 用例（`temp/test_luajit_semantic.lua`）通过\n- 三元语法用例（`temp/tmp_test_ternary.lua`）、语义用例（`temp/test_ternary_semantic.lua`）通过\n- `parser_test`（含 `luajit_ext`：**14 个官方测试文件全部编译通过**，含 expr_cond/expr_coal/expr_nav）、`type_inference`、`hover` 回归通过\n\n实现中发现的现有问题及修复：\n- LuaJIT 下 `<<`/`>>` 误报 `UNSUPPORT_SYMBOL`（已修复：LuaJIT 支持 `<<`/`>>`，`//` 仍不支持）\n- `a ~= b` 在语句上下文应识别为异或复合赋值（已修复，与现有 `+=` 简化一致）\n- `continue` 是 soft keyword，需按后续 token 判断（`continue = 2` 是变量赋值）\n- `safe` 字段只在 `safe == true` 时设置，否则普通 AST 会多出 `safe = false`（ast 测试失败教训）\n\n三元 `?:` 实现要点（源码依据 lj_parse.c）：\n- 三元在 `expr()` 尾部（`expr_binop` 完整解析后）检查 `?`，**优先级最低、右结合**\n- b 部分用 `EXPR_F_NOCOLON`（0x02）禁止方法调用：`a ? obj:method() : c` 报错，`a ? (obj:method()) : c` 合法\n- NOCOLON 传播到一元/二元操作数与后缀链；**重置**于括号/索引/参数/表值/lambda\n- **b 部分嵌套三元非法**（`a ? b ? c : d : e` 中 `d:e` 方法调用歧义，与 LuaJIT 一致）；右结合靠 **c 部分递归**（`a ? b : c ? d : e`）\n- compile.lua：`parseExp(asAction, level, noMethod, noTernary)`；操作数位置 `noTernary=true`；`parseSimple` 的 `:` 分支 `noMethod` 时 break；`?.` 后紧跟 `:` 且 noMethod 时 break 不消费\n- AST：`type='ternary'`，`[1]`=条件、`[2]`=b、`[3]`=c；guide childMap `['ternary'] = {1,2,3}`\n- vm：`testCondition` 恒真→b、恒假→c、不确定→b|c 合并\n\n## 概述\n\n为 lua-language-server 增加 LuaJIT v3 反向移植到 v2.1 的扩展语法支持（对应 [LuaLS/lua-language-server#3434](https://github.com/LuaLS/lua-language-server/issues/3434)）。\n\n## 背景与参考\n\n| 主题 | 链接 |\n|------|------|\n| LuaLS 需求 issue | https://github.com/LuaLS/lua-language-server/issues/3434 |\n| LuaJIT v3 语法总览（权威文档，见 MikePall 第一条评论） | https://github.com/LuaJIT/LuaJIT/issues/1475 |\n| 反向移植到 v2.1 说明 | https://github.com/LuaJIT/LuaJIT/issues/1476 |\n| 官方测试用例 gist | https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4 |\n| 本地测试文件 | `test/parser_test/LuaJIT/`（已包裹为 `return [[...]]`） |\n| 语法扩展详细文档 | `references/syntax-extensions.md` |\n\n## 开关设计（新设置）\n\n新增设置：**`Lua.runtime.enableLuaJITExtensions`**（`Boolean`，默认 `false`）\n\n生效条件（**两者同时满足**才启用）：\n1. `Lua.runtime.version == 'LuaJIT'`\n2. `Lua.runtime.enableLuaJITExtensions == true`\n\n传递链路：\n```\n用户配置\n  → script/config/template.lua 注册设置\n  → script/files.lua 构造 options：enableLuaJITExtensions = config.get(uri, 'Lua.runtime.enableLuaJITExtensions')\n  → parser.compile(text, 'Lua', version, options)\n  → script/parser/compile.lua initState 中判定：\n      if version == 'LuaJIT' and options.enableLuaJITExtensions then\n          state.luaJITExtensions = true\n      end\n```\n\n要点：\n- 启用后，相关 LuaJIT 语法**直接生效**，无需用户再去 `Lua.runtime.nonstandardSymbol` 手动勾选。\n- 与 `nonstandardSymbol` 的关系：`continue`、`|lambda|`、`&&`、`||`、`!`、`!=`、复合赋值等原本由 `nonstandardSymbol` 控制；启用本开关时这些项自动视为开启。\n- **单独启用（2026-08-13 完成）**：10 项 LuaJIT 扩展语法也已加入 `Lua.runtime.nonstandardSymbol` 枚举（`?.` `??` `?:` `~>>` `~>>=` `..=` `~=` `const` `->` `number_underscore`），可单独启用，**不要求** version == 'LuaJIT'。生效判定统一走 `compile.lua` 的 `isLuaJITExt(symbol)` helper（`State.luaJITExtensions or nonstandardSymbol[symbol]`）。\n- `//`（注释）与反引号字符串 `` ` `` 属于 LuaLS 自身扩展，**不属于** LuaJIT 语法，保持由 `nonstandardSymbol` 独立控制。\n\n## 支持范围速览\n\n### ✅ 现有代码已支持（本次基本无需改动）\n| 语法 | 现状 |\n|------|------|\n| 位运算 `~ & \\| << >>` | LuaJIT 2.1 原生，compile 已支持 |\n| 自定义运算符 `! && \\|\\| !=` | tokenizer 已有 token；compile 有 `UnaryAlias`/`BinaryAlias`，带 `ERR_NONSTANDARD_SYMBOL` 检查 |\n| 复合赋值 `+= -= *= /= %= &= \\|= <<= >>=` | `expectAssign` 已支持（需 `nonstandardSymbol`） |\n| `continue` 语句 | `parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`） |\n| 短函数 `\\|x\\| -> expr` | `parseLambda` 已支持（`nonstandardSymbol['\\|lambda\\|']`），但 `->` 尚无独立 token |\n| 数字后缀 `LL`/`ULL`/`I` | `dropNumberTail` 已支持（仅 LuaJIT） |\n\n### ✅ 已实现（本次规划核心）\n| # | 语法 | 涉及 | 状态 |\n|---|------|------|------|\n| 1 | `~>>` 算术右移 与 `~>>=` 复合赋值 | tokenizer + `BinarySymbol` + `expectAssign` | ✅ |\n| 2 | `..=` 字符串连接复合赋值 | tokenizer + `expectAssign` | ✅ |\n| 3 | `??` 空值合并 | tokenizer + `BinarySymbol` + 类型语义 | ✅ |\n| 4 | `?.` 安全导航（属性/索引/调用/**方法形式 `:?`**） | tokenizer + `parseSimple` | ✅ |\n| 5 | `const` 声明 | `parseAction` + 局部常量语义 | ✅ |\n| 6 | 数字下划线 `1_000` | tokenizer `Number` + `parseNumber*` | ✅ |\n| 7 | 短函数完整形式（`x -> expr`、`\\|\\| -> expr`、`-> do ... end`） | tokenizer `->` + `parseLambda` 扩展 | ✅ |\n| 8 | **三元 `ternary`**（符号 `?:`，`cond ? x : y`，NOCOLON 屏蔽方法调用、右结合；**key 为 `ternary`，与无点号安全方法 `?:` 拆开**） | `parseExp` + `parseSimple` + guide + vm | ✅ |\n\n### ⏸ 未反向移植到 v2.1（无需支持）\n- `//` 地板除、命名变参 `...name`、位运算元方法、`__add(a, b, true)` 元方法。\n\n## 实现步骤\n\n### 阶段 0：基础设施\n1. `script/config/template.lua`：注册 `Lua.runtime.enableLuaJITExtensions`。\n2. `script/files.lua`：两处（`compileStateAsync` 与 `compileState`）构造 options 时传入。\n3. `script/parser/compile.lua` `initState`：判定并设置 `state.luaJITExtensions`。\n4. 各 `locale/*/setting.lua` 补充设置说明文案。\n\n### 阶段 1：tokenizer（script/parser/tokens.lua）\n新增 token（注意顺序，长 token 在前）：\n- `->`（短函数箭头）\n- `??`、`?.`（均需排在单字符 `?` 之前）\n- `~>>`、`~>>=`（`~>>=` 排在 `~>>` 之前）\n- `..=`（排在 `..` 之前）\n- 说明：`?` 与 `:` 均为单字符 token（已存在），三元无需新 token。\n\n### 阶段 2：compile.lua 语法解析\n按依赖顺序（建议）：\n1. **`~>>`**：`BinarySymbol` 加 `['~>>'] = 7`（与 `<<`/`>>` 同级）。\n2. **复合赋值补全**：`expectAssign` 加 `..=`、`~>>=`。\n3. **数字下划线**：`parseNumber10/16/2` 允许下划线；注意 `dropNumberTail` 需放行下划线。\n4. **`??`**：`BinarySymbol` 加 `['??']`（与 `or` 同级），复用 `parseExp` 的二元循环；检查短路语义。\n5. **`?.`**：`parseSimple` 中 `token == '.'` 分支旁新增 `token == '?.'` 分支；支持 `a?.field`、`a?.[key]`、`f?.()`、`f?.\"str\"`、`f?.{...}`、`a?.field = v`、`a?.[key] = v`；**并支持 `:?` 方法形式**：\n   - `obj?.:method(...)`（检查 obj）\n   - `obj:method?.(...)`（检查 method）\n   - `obj?.:method?.(...)`（两者都检查）\n6. **`const`**：`parseAction` 中处理 soft keyword `const`；`const x = 1` 为块级局部常量，禁止重复声明/重新赋值；`const function foo() end` 一并考虑。\n7. **短函数**：`parseExpUnit` 中识别 `->`；`parseLambda` 支持 `x -> expr`（单参数省略管道）、`|| -> expr`、`-> do ... end` 语句体。\n\n### 阶段 3：AST 与语义层\n- 新节点类型（建议在实现时定稿）：\n  - `?.` → `getfield`/`getindex`/`call`/`getmethod` 的安全导航变体（可加标记字段，如 `safe = true`），语义层需处理 nullable 访问；`obj:method?.` 的检查目标是 method，`obj?.:method` 的检查目标是 obj。\n  - `??` → 复用 `binary`（op = `??`）。\n  - `const` → 复用 `local` 结构并打 const 标记，或新节点类型。\n  - `?:` → `ternary` 节点：`[1]`=条件、`[2]`=b、`[3]`=c（guide childMap 已加 `['ternary'] = {1,2,3}`）。\n- `script/parser/guide.lua`、`script/vm/*`：让类型推断/引用/悬停等理解新节点。\n- 运算符优先级遵循 LuaJIT 官方表（见 references/syntax-extensions.md）。\n\n### 阶段 4：诊断与文案\n- 新错误码直接在 compile.lua 中 `type = 'XXX'` 定义（`script/proto/diagnostic.lua` 的 `getDiagAndErrNameMap` 会自动从源码提取注册）。\n- 需要的新错误码示例：\n  - `?.` 使用但未启用扩展 → 复用 `ERR_NONSTANDARD_SYMBOL` 或新增\n  - `const` 重新赋值 / 重复声明 → 参考测试中的 `assign to const` / `declare const`（注意现已有 `SET_CONST`、`ASSIGN_CONST_GLOBAL`）\n- 各 `locale/*/diagnostic.lua` 补充翻译。\n\n### 阶段 5：测试\n- `test/parser_test/LuaJIT/` 下的 14 个测试文件已包裹为 `return [[...]]`，可作为代码字符串测试源。\n- 需接入现有 parser_test 框架（参考 `test/parser_test/ast/` 的结构），或用语法检查断言（`syntax_check.lua` 的 `TestWith` 模式）。\n- 为开关状态各写测试：未启用时报错、启用后通过。\n\n## 如何测试（实测方法）\n- **直接运行**：`bin/lua-language-server.exe test.lua` —— `lua-language-server.exe` 可直接作为 Lua 运行时执行脚本。\n- **限定测试范围**：`test.lua` 支持 `--name=<pattern>` 参数，`test(name)` 内部用 `name:match(TARGET_TEST_NAME)` 过滤，例如：\n  - `bin/lua-language-server.exe test.lua --name=parser_test` 只跑 `parser_test`\n- **自定义测试脚本**：可自行改造 `test.lua` 或编写独立脚本，直接 `bin/lua-language-server.exe <script>.lua` 运行（需正确设置 `package.path`，见 `test.lua` 开头）。\n- **当前进度验证脚本**：`temp/tmp_test_luajit.lua`（临时，验证 compile 解析 LuaJIT 扩展语法，含开关开关两种状态的断言）。\n- **长耗时命令必须轮询（重要）**：完整测试 / build-doc 后台运行**不会自动通知完成**。运行后须主动轮询日志：`temp/test_full*.log` 出现 `test finish.`（无 error）即通过，full 阶段耗时较长需多次轮询；build-doc 看退出码 `DOC=0`。用 `read_file`/`grep_search` 查日志末尾，不要依赖系统通知。\n\n## 关键文件地图\n\n| 文件 | 职责 |\n|------|------|\n| `script/config/template.lua` | 注册 `Lua.runtime.enableLuaJITExtensions` |\n| `script/files.lua` | 构造 compile options（两处） |\n| `script/parser/tokens.lua` | 词法：新增 token |\n| `script/parser/compile.lua` | 语法：`initState`/`parseExp`/`parseSimple`/`parseLambda`/`expectAssign`/`parseAction`/`parseNumber*`/`dropNumberTail` |\n| `script/parser/guide.lua` | AST 辅助（行号、块、子节点遍历） |\n| `script/vm/*` | 语义/类型推断 |\n| `script/proto/diagnostic.lua` | 错误码注册（自动提取） |\n| `locale/*/setting.lua`、`locale/*/diagnostic.lua` | 文案 |\n| `test/parser_test/LuaJIT/` | LuaJIT 官方测试（已包裹） |\n\n## 注意事项\n- **优先级表**：所有新增运算符的优先级必须严格遵循 LuaJIT 官方定义（见 references/syntax-extensions.md「运算符优先级」）。\n- **三元 `ternary` 已实现（2026-08-13 拆分）**：见 references/syntax-extensions.md 第 3 节；b 部分禁止方法调用（NOCOLON），需括号 `(obj:method())` 绕过；b 部分嵌套三元非法（`d:e` 歧义），c 部分右结合递归合法。**一个 key 只承载一个功能**：三元从 `?:` 拆出，改用 **`ternary`** key（`isLuaJITExt('ternary')`，主开关 enableLuaJITExtensions 也启用，因三元是 LuaJIT 官方语法）；`?:` 专用于无点号安全方法 `obj?:get()`（仅 `nonstandardSymbol['?:']` 门控，主开关不启用）。\n- **`?.` 与 `:` 的歧义**：实现安全导航的方法形式时需小心区分 `obj?.:method`（`?.` + `:`）与 `obj:method?.`（`:` + `?.`），以及普通方法调用 `obj:method`，三者 token 序列不同，需在 `parseSimple` 中分别处理。\n- **`?.` 已拆分为 3 项（2026-08-13）**：`?.`（字段/方法：`a?.b` / `obj?.:method` / `obj:method?.`）、`?.(`（安全调用：`f?.()` / `f?.\"str\"` / `f?.{...}` / `f?.[[...]]`）、`?.[`（安全索引：`t?.[key]`）。parseSimple 在 `?.` 后按后缀选择启用项：`(`/`{`/引号→`?.('`；`[`→看 `Tokens[Index+5]` 是否为 `[`/`=`（长字符串调用 vs 索引）；其他→`?.`。主开关 enableLuaJITExtensions 仍一并启用全部。\n- **无点号可选链 `?(` / `?[`（2026-08-13 新增，非 LuaJIT 语法）**：`f?()` / `t?[1]` 等价于 `f?.()` / `t?.[1]`，AST 一致（`call`/`getindex` 的 `safe=true`）。仅由 `nonstandardSymbol['?(']`/`['?[']` 单独启用，**与 LuaJIT 无关**（主开关 enableLuaJITExtensions 不启用，与版本无关）；与三元（`ternary`）解析冲突（`a ? (x) : c` 会被当作 `a?(x)`），template 注释与 locale 描述均有警告。\n- **无点号安全方法 `obj?:get()`（2026-08-13，非 LuaJIT 语法）**：`?:` 选项只做无点号安全方法（`obj?:get()` = `obj?.:get()`），与三元拆开（三元用 `ternary`）。parseSimple 的 `?` 分支处理 `?`+`:`（需 `nonstandardSymbol['?:']` 且 `not noMethod`，三元 b 部分不消费）。合法三元 `a ? b : c` 的 `?` 后跟表达式不受影响（仅 `?`+`:` 相邻才走方法）。\n- **soft keyword**：`const` 与 `continue` 都是 soft keyword，可作变量名/字段名/函数名，解析时必须先识别上下文。\n- **短路语义**：`??`、`?.` 都有短路行为，虽然语法层不关心求值，但 AST 结构与类型推断需正确表达。\n- **`continue` 与 `repeat`**：`continue` 跳转到循环条件；在 `repeat` 中不能跳入其后声明的局部变量作用域（测试 `stmt_continue.lua` 有覆盖）。\n"},"files":{".github/copilot-instructions.md":"# Coding Instructions\n\n## PowerShell 文件编码\n\n- Windows PowerShell 默认编码为 GBK/UTF-16 LE，**严禁**写入项目文件时不指定编码（会导致 UTF8 文件乱码）\n- `Set-Content`、`Out-File`、`echo >`、`Write-Output >` 等写入操作必须加 `-Encoding UTF8`\n- 正确示例：`Set-Content -Path file.ts -Value \"...\" -Encoding UTF8`\n- 项目所有源码文件统一 UTF-8（无 BOM），由 `.editorconfig` 和 ESLint 兜底\n\n## 长耗时命令轮询\n\n- 后台运行的长时间命令（如 `test.lua` 完整测试、`build-doc.lua`）**不会自动通知完成**，必须主动轮询结果文件确认\n- 完整测试：检查 `temp/test_full*.log` 是否出现 `test finish.`（无 error 即通过；full 阶段耗时较长，需多次轮询）\n- build-doc：检查退出码 `DOC=0`\n- 轮询方式：读取日志文件末尾或 `grep` 结束标记，间隔由命令耗时决定，不要依赖系统通知\n- 推荐命令模式（Windows PowerShell + cmd 重定向，规避管道无法捕获输出的问题）：\n  - `cmd /c \"cd /d <repo> && bin\\lua-language-server.exe test.lua --name=<test> > temp\\test_xx.log 2>&1 & echo EXIT=%ERRORLEVEL%\"`\n  - 完成后用 `read_file` / `grep_search` 查日志，或用 `Select-String` 找结束标记\n\n## Pull Request Review Language\n\nWhen performing pull request reviews, write every finding in both Chinese and English.\n\nUse this format for inline comments and review summaries:\n\n**中文**：<审查意见>\n\n**English**: <equivalent review comment>\n\nKeep both versions semantically equivalent. Report only actionable, evidence-based findings.\n",".github/skills/luajit-syntax/references/syntax-extensions.md":"# LuaJIT 3.0 语法扩展参考文档\n\n> 来源：[LuaJIT/LuaJIT#1475](https://github.com/LuaJIT/LuaJIT/issues/1475) MikePall 的第一条评论（权威文档），及 [LuaJIT/LuaJIT#1476](https://github.com/LuaJIT/LuaJIT/issues/1476) 反向移植说明。\n> 反向移植到 v2.1 的扩展由官方测试覆盖：`test/parser_test/LuaJIT/`（来自 gist [a8372d92cb2e6380cf56e78f69ee70a4](https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4)）。\n\n## 反向移植范围（v2.1 backport）\n\n**已移植：**\n1. 位运算：一元 `~`，二元 `& | ~ << >> ~>>`\n2. 自定义运算符 `! && || !=`\n3. 三元条件 `?:`（**本次实现已搁置**，见下文第 3 节）\n4. 安全导航 `?.`（含方法形式 `:?`，全部实现）\n5. 空值合并 `??`\n6. 复合赋值 `+= -= *= …`\n7. `continue` 语句\n8. `const` 声明\n9. 短函数表达式\n10. 数字字面量下划线\n\n**未移植（无需支持）：**\n- 位运算元方法\n- 地板除 `//`\n- 复合赋值元方法 `__add(a, b, true)`\n- 命名变参 `...name`\n\n## 运算符优先级表（从高到低）\n\n| 运算符 / 结构 | 元数 | 结合性 |\n|--------------|------|--------|\n| `( )` | 一元 | - |\n| `->`（短函数） | 二元 | 右 |\n| `.` `:` `[` `]` `f()` `?.` | 二元 | 左 |\n| `^` | 二元 | 右 |\n| `not` `!` `-` `~` `#` | 一元 | - |\n| `*` `/` `//` `%` | 二元 | 左 |\n| `+` `-` | 二元 | 左 |\n| `..` | 二元 | 右 |\n| `<<` `>>` `~>>` | 二元 | 左 |\n| `&` | 二元 | 左 |\n| `~` | 二元 | 左 |\n| `\\|` | 二元 | 左 |\n| `==` `~=` `!=` `<` `>` `<=` `>=` | 二元 | 左 |\n| `and` `&&` | 二元 | 左 |\n| `or` `\\|\\|` `??` | 二元 | 左 |\n| `?:` ⏸ | 三元 | 右 |\n| `=` `compound=` | 二元 | - |\n\n> 对应 `script/parser/compile.lua` 中 `BinarySymbol` 的数值等级（数值越大优先级越高）：\n> - `??` 与 `or` 同级（1）\n> - `?:` 已搁置（若未来实现：单独处理，优先级最低，在 `=` 之上）\n> - `~>>` 与 `<<`/`>>` 同级（7）\n\n## 各语法详解\n\n### 1. 位运算 `~ & | ~ << >> ~>>`\n\n| 运算符 | 功能 | 对应 bit.* 函数 |\n|--------|------|-----------------|\n| `~ a` | 按位取反 | `bit.bnot()` |\n| `a & b` | 按位与 | `bit.band()` |\n| `a \\| b` | 按位或 | `bit.bor()` |\n| `a ~ b` | 按位异或 | `bit.bxor()` |\n| `a << b` | 左移 | `bit.lshift()` |\n| `a >> b` | 逻辑右移 | `bit.rshift()` |\n| `a ~>> b` | **算术右移** | `bit.arshift()` |\n\n- `~>>`（算术右移）当前 **未实现**，是本规划要补的（tokenizer + `BinarySymbol`）。\n- 测试：`expr_bit.lua`、`ffi_expr_bit*.lua`。注意测试中 `LL`/`ULL` 后缀仅 LuaJIT 支持（`dropNumberTail` 已处理）。\n\n### 2. 自定义运算符 `! && || !=`\n\n| 自定义写法 | 等价经典写法 | 功能 |\n|-----------|--------------|------|\n| `!` | `not` | 逻辑非 |\n| `&&` | `and` | 短路与 |\n| `\\|\\|` | `or` | 短路或 |\n| `!=` | `~=` | 不相等比较 |\n\n- 语义与经典运算符完全一致。\n- compile.lua 已有 `UnaryAlias`（`!`）与 `BinaryAlias`（`&&`、`||`、`!=`）；`BinaryAlias` 带 `ERR_NONSTANDARD_SYMBOL` 检查。\n- 测试：`expr_customary.lua`。\n\n### 3. 三元条件 `?:`（研究完成，待实现）\n\n```\na ? b : c\n```\n- `a` 为条件（真值判定同 `if`）；为真求 `b`，否则求 `c`。\n- **右结合**，可嵌套：`cond1 ? a : cond2 ? b : x` = `cond1 ? a : (cond2 ? b : x)`。\n- 短路：未选中的分支不求值。\n- **语法歧义（源码确认）**：b 部分解析使用 `EXPR_F_NOCOLON` 标志（`lj_parse.c expr()`），在后缀解析中遇到 `:` 即 break，禁止方法调用；括号内方法调用需加括号：`cond ? (obj:method()) : default`。\n\n**NOCOLON 传播/重置规则**（源码 `lj_parse.c` 依据）：\n- 传播（eflags 透传）：一元操作数、二元操作数、后缀链（name/表/字符串后缀）\n- 重置（传 0）：括号 `expr(v,0)`、索引 `expr_bracket→expr(v,0)`、函数参数 `parse_args→expr_list→expr(v,0)`、表值、lambda\n- 推论：`a ? x + obj:method() : c` 也报错（操作数传播屏蔽）；`a ? f(obj:method()) : c` 合法（参数重置）\n\n**compile.lua 实现方案**：\n- `parseExp` 二元循环后处理三元（优先级最低、右结合）：b = `parseExp(nil, nil, true)`（noMethod），c = `parseExp()`\n- 加 `noMethod` 参数：`parseExp(asAction, level, noMethod)`、`parseExpUnit(noMethod)`、`parseSimple(node, funcName, noMethod)`\n- `parseSimple` 的 `:` 分支：`if noMethod then break`\n- 重置点：parseParen / parseIndex / parseExpList / parseTable 值 / parseLambda\n- tokenizer 无需改动（`?` `:` 均为单字符 token）\n- AST：`ternary` 节点（`[1]`=条件、`[2]`=b、`[3]`=c），需 guide + vm 类型推断\n- 测试：expr_cond.lua 移入正式测试\n\n**测试要点**（expr_cond.lua）：\n- `a ? obj:method() : c` → 报错 eof；`a ? (a:b()) : c` → 合法\n- `a ? a : b():c() + 30` → 合法（c 部分不屏蔽）\n- 无空格 `a?b:c` → 报错（方法调用歧义）；`a ? : c`、`a ? b :` → 报错\n\n### 4. 安全导航 `?.`\n\n| 原表达式 | 安全导航写法 | 检查对象 |\n|----------|--------------|----------|\n| `a[key]` | `a?.[key]` | a |\n| `a.field` | `a?.field` | a |\n| `a[key] = expr` | `a?.[key] = expr` | a |\n| `a.field = expr` | `a?.field = expr` | a |\n| `f(…)` | `f?.(…)` | f |\n| `f\"…\"` | `f?.\"…\"` | f |\n| `f{…}` | `f?.{…}` | f |\n| `obj:method(…)` | `obj?.:method(…)` | obj |\n| `obj:method(…)` | `obj:method?.(…)` | method |\n| `obj:method(…)` | `obj?.:method?.(…)` | 两者 |\n\n- 规则：`.` 可直接替换为 `?.`；调用类（`()`, `\"\"`, `{}`）与 `:` 需要 `?.` 和原符号**同时出现**。\n- 左结合，可链式：`a?.b?.[c]?.d?.:e?.()`（五种检查全部支持）。\n- 短路：左侧为 nil（或 NULL cdata）时，右侧操作与后续链不执行；赋值时右侧表达式也不求值。\n- 本规划实现 `?.` 的**全部形式**：`.`/`[]`/`()`/`\"\"`/`{}`、赋值形式、以及 `:?` 方法形式（`obj?.:method`、`obj:method?.`、`obj?.:method?.`）。\n- 实现提示：`parseSimple` 中需区分 token 序列 `?.`+`:`（obj?.:method）与 `:`+`?.`（obj:method?.），前者检查 obj，后者检查 method。\n- 测试：`expr_nav.lua`（全部用例应通过）。\n\n### 5. 空值合并 `??`\n\n```\na ?? b\n```\n- 仅当 `a` 等于 nil 时求 `b` 并返回，否则返回 `a`（`a` 只求值一次）。\n- 与 `or` 同级优先级（`or || ??`），短路。\n- 测试：`expr_coal.lua`。\n\n### 6. 复合赋值\n\n| 运算符 | 功能 | 长形式 |\n|--------|------|--------|\n| `a += b` | 加 | `a = a + b` |\n| `a -= b` | 减 | `a = a - b` |\n| `a *= b` | 乘 | `a = a * b` |\n| `a /= b` | 除 | `a = a / b` |\n| `a %= b` | 取模 | `a = a % b` |\n| `a &= b` | 按位与 | `a = a & b` |\n| `a \\|= b` | 按位或 | `a = a \\| b` |\n| `a ~= b` | 按位异或 | `a = a ~ b` |\n| `a <<= b` | 左移 | `a = a << b` |\n| `a >>= b` | 逻辑右移 | `a = a >> b` |\n| `a ~>>= b` | **算术右移** | `a = a ~>> b` |\n| `a ..= b` | **连接** | `a = a .. b` |\n\n- 索引表达式只求值一次前缀：`x.y.z.field += b` ≈ `local tmp = x.y.z; tmp.field = tmp.field + b`。\n- **不支持**并行赋值：`x, y, z += u, v, w` 为语法错误。\n- **没有** `^=`（故意省略，避免与异或混淆）。\n- 可与安全导航组合：`a?.field += f()`。\n- 现状：`+= -= *= /= %= &= |= <<= >>=` 已支持；需新增 `..=` 与 `~>>=`。\n- 测试：`stmt_compound.lua`。\n\n### 7. `continue` 语句\n\n- 跳转语义：`while`/`repeat` → 循环条件；`for` → 循环更新逻辑。\n- 只能作为（嵌套）块的最后一个语句（同 `break`）。\n- **soft keyword**：可作变量名/字段名/函数名/参数名。\n- `repeat` 中不能跳入其后声明的局部变量作用域（同 `goto` 规则）：\n  `repeat if x then continue end; local a = a; until not a` → 报错。\n- 现状：`parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`），但语义是当作 break 处理，需要确认 `repeat`/`for` 的跳转目标正确性。\n- 测试：`stmt_continue.lua`。\n\n### 8. `const` 声明\n\n```\nconst x = 1\nconst y, z = 2, 3\nconst foo                 -- 无赋值，值为 nil，但不可再赋值\nconst function foo() end  -- const 局部函数\n```\n- 块级局部常量：不可重新赋值、不可在相同或内层作用域重复声明、不可作为函数参数重复声明。\n- **soft keyword**：可作变量名、字段名、函数名、参数名（`local const = 1` 合法；`const const = 1` 也合法——第一个是关键字，第二个是名字）。\n- 变量本身不可变，但引用对象（如 table）内容可变。\n- 需新增 `parseAction` 支持；错误码参考测试中的 `assign to const` / `declare const`。\n- 测试：`stmt_const.lua`。\n\n### 9. 短函数表达式\n\n```\n|…| -> expr          -- 返回 expr 结果\n|…| -> do … end      -- 语句块，无 return 则返回 0 个结果\nx -> x+1             -- 单参数可省略管道\n|| -> true           -- 无参数\n|a, b, ...| -> ...   -- 支持变参\n```\n- 可在任何函数表达式位置使用。\n- expr 变体只返回**单个**结果（多返回用语句块形式）。\n- `->` 优先级最高（仅低于 `()`），右结合。\n- 现状：`|x| -> expr` 已有（`|lambda|`），但 `->` 无独立 token；需补 `x -> expr`、`|| -> expr`、`-> do … end`。\n- 测试：`expr_shortfunc.lua`。\n\n### 10. 数字字面量下划线\n\n```\n1_234_567_890\n0x1_2     0b1_0\n1_2.3_4   1e1_0\n0x1_2p_3\n```\n- 下划线在**任何位置**（初始数字之后）都被忽略，适用于所有进制、所有后缀（含 `LL`/`ULL`）、整数与浮点。\n- `tonumber()` 与字符串隐式转换**不接受**下划线。\n- 需修改 tokenizer 的 `Number` 规则与 `parseNumber10/16/2`。\n- 测试：`number_underscore.lua`。\n\n## 测试文件对照表\n\n| 文件 | 覆盖语法 |\n|------|----------|\n| `expr_bit.lua` | 位运算（`~ & \\| << >> ~>>`） |\n| `expr_bit_bitop.lua` | `bit.*` 库运行时行为（语法层面价值低） |\n| `expr_coal.lua` | `??` 空值合并 |\n| `expr_cond.lua` ⏸ | `?:` 三元条件（已搁置，本阶段不要求通过） |\n| `expr_customary.lua` | `! && \\|\\| !=` |\n| `expr_nav.lua` | `?.` 安全导航（全部形式，含 `:?` 方法形式） |\n| `expr_shortfunc.lua` | 短函数 `->` |\n| `ffi_expr_bit.lua` / `_bit64` / `_bitop` | FFI 位运算 + `LL`/`ULL` 后缀 |\n| `number_underscore.lua` | 数字下划线 |\n| `stmt_compound.lua` | 复合赋值 |\n| `stmt_const.lua` | `const` 声明 |\n| `stmt_continue.lua` | `continue` 语句 |\n\n> 补充：`expr_cond.lua`（三元 `?:`）已搁置，本阶段不要求通过。\n> 所有文件内容现已被包裹为 `return [[ … ]]` 字符串形式，可作为代码字符串测试源。\n",".github/skills/luajit-syntax/SKILL.md":"---\nname: luajit-syntax\ndescription: 'LuaJIT 3.0 扩展语法支持的实施规划与实现指南。Use when implementing LuaJIT extension syntax support (bitwise ~>>, ternary ?:, safe navigation ?., nil-coalescing ??, customary operators ! && || !=, compound assignment, continue, const declaration, short function expression, number underscores) in the Lua language server, when modifying script/parser/compile.lua, script/parser/tokens.lua, script/config/template.lua, script/files.lua, or adding tests under test/parser_test/LuaJIT/.'\n---\n\n# LuaJIT 扩展语法支持规划\n\n## 实施进度\n\n| 阶段 | 内容 | 状态 |\n|------|------|------|\n| 阶段 0 | 设置 `Lua.runtime.enableLuaJITExtensions`、options 传递、initState 判定（语法处单独判断） | ✅ 完成 |\n| 阶段 1 | tokenizer 新增 `-> ?? ?. ~>> ~>>= ..=` | ✅ 完成 |\n| 阶段 2 | 语法解析（`~>>`、复合赋值、数字下划线、`??`、`?.` 全形式、`const`、短函数、`continue` soft keyword、`a ~= b` 异或赋值） | ✅ 完成 |\n| 阶段 3 | 语义层（`?.`→optional、`??` 类型合并、`~>>`→integer） | ✅ 完成 |\n| 阶段 4 | 诊断文案 `PARSER_DECLARE_CONST`（6 语言） | ✅ 完成 |\n| 阶段 5 | 正式测试接入 `test/parser_test/luajit_ext.lua` | ✅ 完成 |\n| 阶段 6 | 三元 `?:`（noMethod/noTernary 机制、ternary AST 节点、vm 类型推断） | ✅ 完成 |\n\n验证：\n- 语法层 40 用例（`temp/tmp_test_luajit.lua`）、语义层 7 用例（`temp/test_luajit_semantic.lua`）通过\n- 三元语法用例（`temp/tmp_test_ternary.lua`）、语义用例（`temp/test_ternary_semantic.lua`）通过\n- `parser_test`（含 `luajit_ext`：**14 个官方测试文件全部编译通过**，含 expr_cond/expr_coal/expr_nav）、`type_inference`、`hover` 回归通过\n\n实现中发现的现有问题及修复：\n- LuaJIT 下 `<<`/`>>` 误报 `UNSUPPORT_SYMBOL`（已修复：LuaJIT 支持 `<<`/`>>`，`//` 仍不支持）\n- `a ~= b` 在语句上下文应识别为异或复合赋值（已修复，与现有 `+=` 简化一致）\n- `continue` 是 soft keyword，需按后续 token 判断（`continue = 2` 是变量赋值）\n- `safe` 字段只在 `safe == true` 时设置，否则普通 AST 会多出 `safe = false`（ast 测试失败教训）\n\n三元 `?:` 实现要点（源码依据 lj_parse.c）：\n- 三元在 `expr()` 尾部（`expr_binop` 完整解析后）检查 `?`，**优先级最低、右结合**\n- b 部分用 `EXPR_F_NOCOLON`（0x02）禁止方法调用：`a ? obj:method() : c` 报错，`a ? (obj:method()) : c` 合法\n- NOCOLON 传播到一元/二元操作数与后缀链；**重置**于括号/索引/参数/表值/lambda\n- **b 部分嵌套三元非法**（`a ? b ? c : d : e` 中 `d:e` 方法调用歧义，与 LuaJIT 一致）；右结合靠 **c 部分递归**（`a ? b : c ? d : e`）\n- compile.lua：`parseExp(asAction, level, noMethod, noTernary)`；操作数位置 `noTernary=true`；`parseSimple` 的 `:` 分支 `noMethod` 时 break；`?.` 后紧跟 `:` 且 noMethod 时 break 不消费\n- AST：`type='ternary'`，`[1]`=条件、`[2]`=b、`[3]`=c；guide childMap `['ternary'] = {1,2,3}`\n- vm：`testCondition` 恒真→b、恒假→c、不确定→b|c 合并\n\n## 概述\n\n为 lua-language-server 增加 LuaJIT v3 反向移植到 v2.1 的扩展语法支持（对应 [LuaLS/lua-language-server#3434](https://github.com/LuaLS/lua-language-server/issues/3434)）。\n\n## 背景与参考\n\n| 主题 | 链接 |\n|------|------|\n| LuaLS 需求 issue | https://github.com/LuaLS/lua-language-server/issues/3434 |\n| LuaJIT v3 语法总览（权威文档，见 MikePall 第一条评论） | https://github.com/LuaJIT/LuaJIT/issues/1475 |\n| 反向移植到 v2.1 说明 | https://github.com/LuaJIT/LuaJIT/issues/1476 |\n| 官方测试用例 gist | https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4 |\n| 本地测试文件 | `test/parser_test/LuaJIT/`（已包裹为 `return [[...]]`） |\n| 语法扩展详细文档 | `references/syntax-extensions.md` |\n\n## 开关设计（新设置）\n\n新增设置：**`Lua.runtime.enableLuaJITExtensions`**（`Boolean`，默认 `false`）\n\n生效条件（**两者同时满足**才启用）：\n1. `Lua.runtime.version == 'LuaJIT'`\n2. `Lua.runtime.enableLuaJITExtensions == true`\n\n传递链路：\n```\n用户配置\n  → script/config/template.lua 注册设置\n  → script/files.lua 构造 options：enableLuaJITExtensions = config.get(uri, 'Lua.runtime.enableLuaJITExtensions')\n  → parser.compile(text, 'Lua', version, options)\n  → script/parser/compile.lua initState 中判定：\n      if version == 'LuaJIT' and options.enableLuaJITExtensions then\n          state.luaJITExtensions = true\n      end\n```\n\n要点：\n- 启用后，相关 LuaJIT 语法**直接生效**，无需用户再去 `Lua.runtime.nonstandardSymbol` 手动勾选。\n- 与 `nonstandardSymbol` 的关系：`continue`、`|lambda|`、`&&`、`||`、`!`、`!=`、复合赋值等原本由 `nonstandardSymbol` 控制；启用本开关时这些项自动视为开启。\n- **单独启用（2026-08-13 完成）**：10 项 LuaJIT 扩展语法也已加入 `Lua.runtime.nonstandardSymbol` 枚举（`?.` `??` `?:` `~>>` `~>>=` `..=` `~=` `const` `->` `number_underscore`），可单独启用，**不要求** version == 'LuaJIT'。生效判定统一走 `compile.lua` 的 `isLuaJITExt(symbol)` helper（`State.luaJITExtensions or nonstandardSymbol[symbol]`）。\n- `//`（注释）与反引号字符串 `` ` `` 属于 LuaLS 自身扩展，**不属于** LuaJIT 语法，保持由 `nonstandardSymbol` 独立控制。\n\n## 支持范围速览\n\n### ✅ 现有代码已支持（本次基本无需改动）\n| 语法 | 现状 |\n|------|------|\n| 位运算 `~ & \\| << >>` | LuaJIT 2.1 原生，compile 已支持 |\n| 自定义运算符 `! && \\|\\| !=` | tokenizer 已有 token；compile 有 `UnaryAlias`/`BinaryAlias`，带 `ERR_NONSTANDARD_SYMBOL` 检查 |\n| 复合赋值 `+= -= *= /= %= &= \\|= <<= >>=` | `expectAssign` 已支持（需 `nonstandardSymbol`） |\n| `continue` 语句 | `parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`） |\n| 短函数 `\\|x\\| -> expr` | `parseLambda` 已支持（`nonstandardSymbol['\\|lambda\\|']`），但 `->` 尚无独立 token |\n| 数字后缀 `LL`/`ULL`/`I` | `dropNumberTail` 已支持（仅 LuaJIT） |\n\n### ✅ 已实现（本次规划核心）\n| # | 语法 | 涉及 | 状态 |\n|---|------|------|------|\n| 1 | `~>>` 算术右移 与 `~>>=` 复合赋值 | tokenizer + `BinarySymbol` + `expectAssign` | ✅ |\n| 2 | `..=` 字符串连接复合赋值 | tokenizer + `expectAssign` | ✅ |\n| 3 | `??` 空值合并 | tokenizer + `BinarySymbol` + 类型语义 | ✅ |\n| 4 | `?.` 安全导航（属性/索引/调用/**方法形式 `:?`**） | tokenizer + `parseSimple` | ✅ |\n| 5 | `const` 声明 | `parseAction` + 局部常量语义 | ✅ |\n| 6 | 数字下划线 `1_000` | tokenizer `Number` + `parseNumber*` | ✅ |\n| 7 | 短函数完整形式（`x -> expr`、`\\|\\| -> expr`、`-> do ... end`） | tokenizer `->` + `parseLambda` 扩展 | ✅ |\n| 8 | **三元 `ternary`**（符号 `?:`，`cond ? x : y`，NOCOLON 屏蔽方法调用、右结合；**key 为 `ternary`，与无点号安全方法 `?:` 拆开**） | `parseExp` + `parseSimple` + guide + vm | ✅ |\n\n### ⏸ 未反向移植到 v2.1（无需支持）\n- `//` 地板除、命名变参 `...name`、位运算元方法、`__add(a, b, true)` 元方法。\n\n## 实现步骤\n\n### 阶段 0：基础设施\n1. `script/config/template.lua`：注册 `Lua.runtime.enableLuaJITExtensions`。\n2. `script/files.lua`：两处（`compileStateAsync` 与 `compileState`）构造 options 时传入。\n3. `script/parser/compile.lua` `initState`：判定并设置 `state.luaJITExtensions`。\n4. 各 `locale/*/setting.lua` 补充设置说明文案。\n\n### 阶段 1：tokenizer（script/parser/tokens.lua）\n新增 token（注意顺序，长 token 在前）：\n- `->`（短函数箭头）\n- `??`、`?.`（均需排在单字符 `?` 之前）\n- `~>>`、`~>>=`（`~>>=` 排在 `~>>` 之前）\n- `..=`（排在 `..` 之前）\n- 说明：`?` 与 `:` 均为单字符 token（已存在），三元无需新 token。\n\n### 阶段 2：compile.lua 语法解析\n按依赖顺序（建议）：\n1. **`~>>`**：`BinarySymbol` 加 `['~>>'] = 7`（与 `<<`/`>>` 同级）。\n2. **复合赋值补全**：`expectAssign` 加 `..=`、`~>>=`。\n3. **数字下划线**：`parseNumber10/16/2` 允许下划线；注意 `dropNumberTail` 需放行下划线。\n4. **`??`**：`BinarySymbol` 加 `['??']`（与 `or` 同级），复用 `parseExp` 的二元循环；检查短路语义。\n5. **`?.`**：`parseSimple` 中 `token == '.'` 分支旁新增 `token == '?.'` 分支；支持 `a?.field`、`a?.[key]`、`f?.()`、`f?.\"str\"`、`f?.{...}`、`a?.field = v`、`a?.[key] = v`；**并支持 `:?` 方法形式**：\n   - `obj?.:method(...)`（检查 obj）\n   - `obj:method?.(...)`（检查 method）\n   - `obj?.:method?.(...)`（两者都检查）\n6. **`const`**：`parseAction` 中处理 soft keyword `const`；`const x = 1` 为块级局部常量，禁止重复声明/重新赋值；`const function foo() end` 一并考虑。\n7. **短函数**：`parseExpUnit` 中识别 `->`；`parseLambda` 支持 `x -> expr`（单参数省略管道）、`|| -> expr`、`-> do ... end` 语句体。\n\n### 阶段 3：AST 与语义层\n- 新节点类型（建议在实现时定稿）：\n  - `?.` → `getfield`/`getindex`/`call`/`getmethod` 的安全导航变体（可加标记字段，如 `safe = true`），语义层需处理 nullable 访问；`obj:method?.` 的检查目标是 method，`obj?.:method` 的检查目标是 obj。\n  - `??` → 复用 `binary`（op = `??`）。\n  - `const` → 复用 `local` 结构并打 const 标记，或新节点类型。\n  - `?:` → `ternary` 节点：`[1]`=条件、`[2]`=b、`[3]`=c（guide childMap 已加 `['ternary'] = {1,2,3}`）。\n- `script/parser/guide.lua`、`script/vm/*`：让类型推断/引用/悬停等理解新节点。\n- 运算符优先级遵循 LuaJIT 官方表（见 references/syntax-extensions.md）。\n\n### 阶段 4：诊断与文案\n- 新错误码直接在 compile.lua 中 `type = 'XXX'` 定义（`script/proto/diagnostic.lua` 的 `getDiagAndErrNameMap` 会自动从源码提取注册）。\n- 需要的新错误码示例：\n  - `?.` 使用但未启用扩展 → 复用 `ERR_NONSTANDARD_SYMBOL` 或新增\n  - `const` 重新赋值 / 重复声明 → 参考测试中的 `assign to const` / `declare const`（注意现已有 `SET_CONST`、`ASSIGN_CONST_GLOBAL`）\n- 各 `locale/*/diagnostic.lua` 补充翻译。\n\n### 阶段 5：测试\n- `test/parser_test/LuaJIT/` 下的 14 个测试文件已包裹为 `return [[...]]`，可作为代码字符串测试源。\n- 需接入现有 parser_test 框架（参考 `test/parser_test/ast/` 的结构），或用语法检查断言（`syntax_check.lua` 的 `TestWith` 模式）。\n- 为开关状态各写测试：未启用时报错、启用后通过。\n\n## 如何测试（实测方法）\n- **直接运行**：`bin/lua-language-server.exe test.lua` —— `lua-language-server.exe` 可直接作为 Lua 运行时执行脚本。\n- **限定测试范围**：`test.lua` 支持 `--name=<pattern>` 参数，`test(name)` 内部用 `name:match(TARGET_TEST_NAME)` 过滤，例如：\n  - `bin/lua-language-server.exe test.lua --name=parser_test` 只跑 `parser_test`\n- **自定义测试脚本**：可自行改造 `test.lua` 或编写独立脚本，直接 `bin/lua-language-server.exe <script>.lua` 运行（需正确设置 `package.path`，见 `test.lua` 开头）。\n- **当前进度验证脚本**：`temp/tmp_test_luajit.lua`（临时，验证 compile 解析 LuaJIT 扩展语法，含开关开关两种状态的断言）。\n- **长耗时命令必须轮询（重要）**：完整测试 / build-doc 后台运行**不会自动通知完成**。运行后须主动轮询日志：`temp/test_full*.log` 出现 `test finish.`（无 error）即通过，full 阶段耗时较长需多次轮询；build-doc 看退出码 `DOC=0`。用 `read_file`/`grep_search` 查日志末尾，不要依赖系统通知。\n\n## 关键文件地图\n\n| 文件 | 职责 |\n|------|------|\n| `script/config/template.lua` | 注册 `Lua.runtime.enableLuaJITExtensions` |\n| `script/files.lua` | 构造 compile options（两处） |\n| `script/parser/tokens.lua` | 词法：新增 token |\n| `script/parser/compile.lua` | 语法：`initState`/`parseExp`/`parseSimple`/`parseLambda`/`expectAssign`/`parseAction`/`parseNumber*`/`dropNumberTail` |\n| `script/parser/guide.lua` | AST 辅助（行号、块、子节点遍历） |\n| `script/vm/*` | 语义/类型推断 |\n| `script/proto/diagnostic.lua` | 错误码注册（自动提取） |\n| `locale/*/setting.lua`、`locale/*/diagnostic.lua` | 文案 |\n| `test/parser_test/LuaJIT/` | LuaJIT 官方测试（已包裹） |\n\n## 注意事项\n- **优先级表**：所有新增运算符的优先级必须严格遵循 LuaJIT 官方定义（见 references/syntax-extensions.md「运算符优先级」）。\n- **三元 `ternary` 已实现（2026-08-13 拆分）**：见 references/syntax-extensions.md 第 3 节；b 部分禁止方法调用（NOCOLON），需括号 `(obj:method())` 绕过；b 部分嵌套三元非法（`d:e` 歧义），c 部分右结合递归合法。**一个 key 只承载一个功能**：三元从 `?:` 拆出，改用 **`ternary`** key（`isLuaJITExt('ternary')`，主开关 enableLuaJITExtensions 也启用，因三元是 LuaJIT 官方语法）；`?:` 专用于无点号安全方法 `obj?:get()`（仅 `nonstandardSymbol['?:']` 门控，主开关不启用）。\n- **`?.` 与 `:` 的歧义**：实现安全导航的方法形式时需小心区分 `obj?.:method`（`?.` + `:`）与 `obj:method?.`（`:` + `?.`），以及普通方法调用 `obj:method`，三者 token 序列不同，需在 `parseSimple` 中分别处理。\n- **`?.` 已拆分为 3 项（2026-08-13）**：`?.`（字段/方法：`a?.b` / `obj?.:method` / `obj:method?.`）、`?.(`（安全调用：`f?.()` / `f?.\"str\"` / `f?.{...}` / `f?.[[...]]`）、`?.[`（安全索引：`t?.[key]`）。parseSimple 在 `?.` 后按后缀选择启用项：`(`/`{`/引号→`?.('`；`[`→看 `Tokens[Index+5]` 是否为 `[`/`=`（长字符串调用 vs 索引）；其他→`?.`。主开关 enableLuaJITExtensions 仍一并启用全部。\n- **无点号可选链 `?(` / `?[`（2026-08-13 新增，非 LuaJIT 语法）**：`f?()` / `t?[1]` 等价于 `f?.()` / `t?.[1]`，AST 一致（`call`/`getindex` 的 `safe=true`）。仅由 `nonstandardSymbol['?(']`/`['?[']` 单独启用，**与 LuaJIT 无关**（主开关 enableLuaJITExtensions 不启用，与版本无关）；与三元（`ternary`）解析冲突（`a ? (x) : c` 会被当作 `a?(x)`），template 注释与 locale 描述均有警告。\n- **无点号安全方法 `obj?:get()`（2026-08-13，非 LuaJIT 语法）**：`?:` 选项只做无点号安全方法（`obj?:get()` = `obj?.:get()`），与三元拆开（三元用 `ternary`）。parseSimple 的 `?` 分支处理 `?`+`:`（需 `nonstandardSymbol['?:']` 且 `not noMethod`，三元 b 部分不消费）。合法三元 `a ? b : c` 的 `?` 后跟表达式不受影响（仅 `?`+`:` 相邻才走方法）。\n- **soft keyword**：`const` 与 `continue` 都是 soft keyword，可作变量名/字段名/函数名，解析时必须先识别上下文。\n- **短路语义**：`??`、`?.` 都有短路行为，虽然语法层不关心求值，但 AST 结构与类型推断需正确表达。\n- **`continue` 与 `repeat`**：`continue` 跳转到循环条件；在 `repeat` 中不能跳入其后声明的局部变量作用域（测试 `stmt_continue.lua` 有覆盖）。\n"},"items":[{"name":"syntax-extensions.md","path":".github/skills/luajit-syntax/references/syntax-extensions.md","rawUrl":"https://raw.githubusercontent.com/LuaLS/lua-language-server/HEAD/.github/skills/luajit-syntax/references/syntax-extensions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# LuaJIT 3.0 语法扩展参考文档\n\n> 来源：[LuaJIT/LuaJIT#1475](https://github.com/LuaJIT/LuaJIT/issues/1475) MikePall 的第一条评论（权威文档），及 [LuaJIT/LuaJIT#1476](https://github.com/LuaJIT/LuaJIT/issues/1476) 反向移植说明。\n> 反向移植到 v2.1 的扩展由官方测试覆盖：`test/parser_test/LuaJIT/`（来自 gist [a8372d92cb2e6380cf56e78f69ee70a4](https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4)）。\n\n## 反向移植范围（v2.1 backport）\n\n**已移植：**\n1. 位运算：一元 `~`，二元 `& | ~ << >> ~>>`\n2. 自定义运算符 `! && || !=`\n3. 三元条件 `?:`（**本次实现已搁置**，见下文第 3 节）\n4. 安全导航 `?.`（含方法形式 `:?`，全部实现）\n5. 空值合并 `??`\n6. 复合赋值 `+= -= *= …`\n7. `continue` 语句\n8. `const` 声明\n9. 短函数表达式\n10. 数字字面量下划线\n\n**未移植（无需支持）：**\n- 位运算元方法\n- 地板除 `//`\n- 复合赋值元方法 `__add(a, b, true)`\n- 命名变参 `...name`\n\n## 运算符优先级表（从高到低）\n\n| 运算符 / 结构 | 元数 | 结合性 |\n|--------------|------|--------|\n| `( )` | 一元 | - |\n| `->`（短函数） | 二元 | 右 |\n| `.` `:` `[` `]` `f()` `?.` | 二元 | 左 |\n| `^` | 二元 | 右 |\n| `not` `!` `-` `~` `#` | 一元 | - |\n| `*` `/` `//` `%` | 二元 | 左 |\n| `+` `-` | 二元 | 左 |\n| `..` | 二元 | 右 |\n| `<<` `>>` `~>>` | 二元 | 左 |\n| `&` | 二元 | 左 |\n| `~` | 二元 | 左 |\n| `\\|` | 二元 | 左 |\n| `==` `~=` `!=` `<` `>` `<=` `>=` | 二元 | 左 |\n| `and` `&&` | 二元 | 左 |\n| `or` `\\|\\|` `??` | 二元 | 左 |\n| `?:` ⏸ | 三元 | 右 |\n| `=` `compound=` | 二元 | - |\n\n> 对应 `script/parser/compile.lua` 中 `BinarySymbol` 的数值等级（数值越大优先级越高）：\n> - `??` 与 `or` 同级（1）\n> - `?:` 已搁置（若未来实现：单独处理，优先级最低，在 `=` 之上）\n> - `~>>` 与 `<<`/`>>` 同级（7）\n\n## 各语法详解\n\n### 1. 位运算 `~ & | ~ << >> ~>>`\n\n| 运算符 | 功能 | 对应 bit.* 函数 |\n|--------|------|-----------------|\n| `~ a` | 按位取反 | `bit.bnot()` |\n| `a & b` | 按位与 | `bit.band()` |\n| `a \\| b` | 按位或 | `bit.bor()` |\n| `a ~ b` | 按位异或 | `bit.bxor()` |\n| `a << b` | 左移 | `bit.lshift()` |\n| `a >> b` | 逻辑右移 | `bit.rshift()` |\n| `a ~>> b` | **算术右移** | `bit.arshift()` |\n\n- `~>>`（算术右移）当前 **未实现**，是本规划要补的（tokenizer + `BinarySymbol`）。\n- 测试：`expr_bit.lua`、`ffi_expr_bit*.lua`。注意测试中 `LL`/`ULL` 后缀仅 LuaJIT 支持（`dropNumberTail` 已处理）。\n\n### 2. 自定义运算符 `! && || !=`\n\n| 自定义写法 | 等价经典写法 | 功能 |\n|-----------|--------------|------|\n| `!` | `not` | 逻辑非 |\n| `&&` | `and` | 短路与 |\n| `\\|\\|` | `or` | 短路或 |\n| `!=` | `~=` | 不相等比较 |\n\n- 语义与经典运算符完全一致。\n- compile.lua 已有 `UnaryAlias`（`!`）与 `BinaryAlias`（`&&`、`||`、`!=`）；`BinaryAlias` 带 `ERR_NONSTANDARD_SYMBOL` 检查。\n- 测试：`expr_customary.lua`。\n\n### 3. 三元条件 `?:`（研究完成，待实现）\n\n```\na ? b : c\n```\n- `a` 为条件（真值判定同 `if`）；为真求 `b`，否则求 `c`。\n- **右结合**，可嵌套：`cond1 ? a : cond2 ? b : x` = `cond1 ? a : (cond2 ? b : x)`。\n- 短路：未选中的分支不求值。\n- **语法歧义（源码确认）**：b 部分解析使用 `EXPR_F_NOCOLON` 标志（`lj_parse.c expr()`），在后缀解析中遇到 `:` 即 break，禁止方法调用；括号内方法调用需加括号：`cond ? (obj:method()) : default`。\n\n**NOCOLON 传播/重置规则**（源码 `lj_parse.c` 依据）：\n- 传播（eflags 透传）：一元操作数、二元操作数、后缀链（name/表/字符串后缀）\n- 重置（传 0）：括号 `expr(v,0)`、索引 `expr_bracket→expr(v,0)`、函数参数 `parse_args→expr_list→expr(v,0)`、表值、lambda\n- 推论：`a ? x + obj:method() : c` 也报错（操作数传播屏蔽）；`a ? f(obj:method()) : c` 合法（参数重置）\n\n**compile.lua 实现方案**：\n- `parseExp` 二元循环后处理三元（优先级最低、右结合）：b = `parseExp(nil, nil, true)`（noMethod），c = `parseExp()`\n- 加 `noMethod` 参数：`parseExp(asAction, level, noMethod)`、`parseExpUnit(noMethod)`、`parseSimple(node, funcName, noMethod)`\n- `parseSimple` 的 `:` 分支：`if noMethod then break`\n- 重置点：parseParen / parseIndex / parseExpList / parseTable 值 / parseLambda\n- tokenizer 无需改动（`?` `:` 均为单字符 token）\n- AST：`ternary` 节点（`[1]`=条件、`[2]`=b、`[3]`=c），需 guide + vm 类型推断\n- 测试：expr_cond.lua 移入正式测试\n\n**测试要点**（expr_cond.lua）：\n- `a ? obj:method() : c` → 报错 eof；`a ? (a:b()) : c` → 合法\n- `a ? a : b():c() + 30` → 合法（c 部分不屏蔽）\n- 无空格 `a?b:c` → 报错（方法调用歧义）；`a ? : c`、`a ? b :` → 报错\n\n### 4. 安全导航 `?.`\n\n| 原表达式 | 安全导航写法 | 检查对象 |\n|----------|--------------|----------|\n| `a[key]` | `a?.[key]` | a |\n| `a.field` | `a?.field` | a |\n| `a[key] = expr` | `a?.[key] = expr` | a |\n| `a.field = expr` | `a?.field = expr` | a |\n| `f(…)` | `f?.(…)` | f |\n| `f\"…\"` | `f?.\"…\"` | f |\n| `f{…}` | `f?.{…}` | f |\n| `obj:method(…)` | `obj?.:method(…)` | obj |\n| `obj:method(…)` | `obj:method?.(…)` | method |\n| `obj:method(…)` | `obj?.:method?.(…)` | 两者 |\n\n- 规则：`.` 可直接替换为 `?.`；调用类（`()`, `\"\"`, `{}`）与 `:` 需要 `?.` 和原符号**同时出现**。\n- 左结合，可链式：`a?.b?.[c]?.d?.:e?.()`（五种检查全部支持）。\n- 短路：左侧为 nil（或 NULL cdata）时，右侧操作与后续链不执行；赋值时右侧表达式也不求值。\n- 本规划实现 `?.` 的**全部形式**：`.`/`[]`/`()`/`\"\"`/`{}`、赋值形式、以及 `:?` 方法形式（`obj?.:method`、`obj:method?.`、`obj?.:method?.`）。\n- 实现提示：`parseSimple` 中需区分 token 序列 `?.`+`:`（obj?.:method）与 `:`+`?.`（obj:method?.），前者检查 obj，后者检查 method。\n- 测试：`expr_nav.lua`（全部用例应通过）。\n\n### 5. 空值合并 `??`\n\n```\na ?? b\n```\n- 仅当 `a` 等于 nil 时求 `b` 并返回，否则返回 `a`（`a` 只求值一次）。\n- 与 `or` 同级优先级（`or || ??`），短路。\n- 测试：`expr_coal.lua`。\n\n### 6. 复合赋值\n\n| 运算符 | 功能 | 长形式 |\n|--------|------|--------|\n| `a += b` | 加 | `a = a + b` |\n| `a -= b` | 减 | `a = a - b` |\n| `a *= b` | 乘 | `a = a * b` |\n| `a /= b` | 除 | `a = a / b` |\n| `a %= b` | 取模 | `a = a % b` |\n| `a &= b` | 按位与 | `a = a & b` |\n| `a \\|= b` | 按位或 | `a = a \\| b` |\n| `a ~= b` | 按位异或 | `a = a ~ b` |\n| `a <<= b` | 左移 | `a = a << b` |\n| `a >>= b` | 逻辑右移 | `a = a >> b` |\n| `a ~>>= b` | **算术右移** | `a = a ~>> b` |\n| `a ..= b` | **连接** | `a = a .. b` |\n\n- 索引表达式只求值一次前缀：`x.y.z.field += b` ≈ `local tmp = x.y.z; tmp.field = tmp.field + b`。\n- **不支持**并行赋值：`x, y, z += u, v, w` 为语法错误。\n- **没有** `^=`（故意省略，避免与异或混淆）。\n- 可与安全导航组合：`a?.field += f()`。\n- 现状：`+= -= *= /= %= &= |= <<= >>=` 已支持；需新增 `..=` 与 `~>>=`。\n- 测试：`stmt_compound.lua`。\n\n### 7. `continue` 语句\n\n- 跳转语义：`while`/`repeat` → 循环条件；`for` → 循环更新逻辑。\n- 只能作为（嵌套）块的最后一个语句（同 `break`）。\n- **soft keyword**：可作变量名/字段名/函数名/参数名。\n- `repeat` 中不能跳入其后声明的局部变量作用域（同 `goto` 规则）：\n  `repeat if x then continue end; local a = a; until not a` → 报错。\n- 现状：`parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`），但语义是当作 break 处理，需要确认 `repeat`/`for` 的跳转目标正确性。\n- 测试：`stmt_continue.lua`。\n\n### 8. `const` 声明\n\n```\nconst x = 1\nconst y, z = 2, 3\nconst foo                 -- 无赋值，值为 nil，但不可再赋值\nconst function foo() end  -- const 局部函数\n```\n- 块级局部常量：不可重新赋值、不可在相同或内层作用域重复声明、不可作为函数参数重复声明。\n- **soft keyword**：可作变量名、字段名、函数名、参数名（`local const = 1` 合法；`const const = 1` 也合法——第一个是关键字，第二个是名字）。\n- 变量本身不可变，但引用对象（如 table）内容可变。\n- 需新增 `parseAction` 支持；错误码参考测试中的 `assign to const` / `declare const`。\n- 测试：`stmt_const.lua`。\n\n### 9. 短函数表达式\n\n```\n|…| -> expr          -- 返回 expr 结果\n|…| -> do … end      -- 语句块，无 return 则返回 0 个结果\nx -> x+1             -- 单参数可省略管道\n|| -> true           -- 无参数\n|a, b, ...| -> ...   -- 支持变参\n```\n- 可在任何函数表达式位置使用。\n- expr 变体只返回**单个**结果（多返回用语句块形式）。\n- `->` 优先级最高（仅低于 `()`），右结合。\n- 现状：`|x| -> expr` 已有（`|lambda|`），但 `->` 无独立 token；需补 `x -> expr`、`|| -> expr`、`-> do … end`。\n- 测试：`expr_shortfunc.lua`。\n\n### 10. 数字字面量下划线\n\n```\n1_234_567_890\n0x1_2     0b1_0\n1_2.3_4   1e1_0\n0x1_2p_3\n```\n- 下划线在**任何位置**（初始数字之后）都被忽略，适用于所有进制、所有后缀（含 `LL`/`ULL`）、整数与浮点。\n- `tonumber()` 与字符串隐式转换**不接受**下划线。\n- 需修改 tokenizer 的 `Number` 规则与 `parseNumber10/16/2`。\n- 测试：`number_underscore.lua`。\n\n## 测试文件对照表\n\n| 文件 | 覆盖语法 |\n|------|----------|\n| `expr_bit.lua` | 位运算（`~ & \\| << >> ~>>`） |\n| `expr_bit_bitop.lua` | `bit.*` 库运行时行为（语法层面价值低） |\n| `expr_coal.lua` | `??` 空值合并 |\n| `expr_cond.lua` ⏸ | `?:` 三元条件（已搁置，本阶段不要求通过） |\n| `expr_customary.lua` | `! && \\|\\| !=` |\n| `expr_nav.lua` | `?.` 安全导航（全部形式，含 `:?` 方法形式） |\n| `expr_shortfunc.lua` | 短函数 `->` |\n| `ffi_expr_bit.lua` / `_bit64` / `_bitop` | FFI 位运算 + `LL`/`ULL` 后缀 |\n| `number_underscore.lua` | 数字下划线 |\n| `stmt_compound.lua` | 复合赋值 |\n| `stmt_const.lua` | `const` 声明 |\n| `stmt_continue.lua` | `continue` 语句 |\n\n> 补充：`expr_cond.lua`（三元 `?:`）已搁置，本阶段不要求通过。\n> 所有文件内容现已被包裹为 `return [[ … ]]` 字符串形式，可作为代码字符串测试源。\n","isInternal":false,"tokens":4307,"sizeBytes":10727},{"name":"SKILL.md","path":".github/skills/luajit-syntax/SKILL.md","rawUrl":"https://raw.githubusercontent.com/LuaLS/lua-language-server/HEAD/.github/skills/luajit-syntax/SKILL.md","title":"luajit-syntax","category":"anthropic-skill","format":"markdown","content":"---\nname: luajit-syntax\ndescription: 'LuaJIT 3.0 扩展语法支持的实施规划与实现指南。Use when implementing LuaJIT extension syntax support (bitwise ~>>, ternary ?:, safe navigation ?., nil-coalescing ??, customary operators ! && || !=, compound assignment, continue, const declaration, short function expression, number underscores) in the Lua language server, when modifying script/parser/compile.lua, script/parser/tokens.lua, script/config/template.lua, script/files.lua, or adding tests under test/parser_test/LuaJIT/.'\n---\n\n# LuaJIT 扩展语法支持规划\n\n## 实施进度\n\n| 阶段 | 内容 | 状态 |\n|------|------|------|\n| 阶段 0 | 设置 `Lua.runtime.enableLuaJITExtensions`、options 传递、initState 判定（语法处单独判断） | ✅ 完成 |\n| 阶段 1 | tokenizer 新增 `-> ?? ?. ~>> ~>>= ..=` | ✅ 完成 |\n| 阶段 2 | 语法解析（`~>>`、复合赋值、数字下划线、`??`、`?.` 全形式、`const`、短函数、`continue` soft keyword、`a ~= b` 异或赋值） | ✅ 完成 |\n| 阶段 3 | 语义层（`?.`→optional、`??` 类型合并、`~>>`→integer） | ✅ 完成 |\n| 阶段 4 | 诊断文案 `PARSER_DECLARE_CONST`（6 语言） | ✅ 完成 |\n| 阶段 5 | 正式测试接入 `test/parser_test/luajit_ext.lua` | ✅ 完成 |\n| 阶段 6 | 三元 `?:`（noMethod/noTernary 机制、ternary AST 节点、vm 类型推断） | ✅ 完成 |\n\n验证：\n- 语法层 40 用例（`temp/tmp_test_luajit.lua`）、语义层 7 用例（`temp/test_luajit_semantic.lua`）通过\n- 三元语法用例（`temp/tmp_test_ternary.lua`）、语义用例（`temp/test_ternary_semantic.lua`）通过\n- `parser_test`（含 `luajit_ext`：**14 个官方测试文件全部编译通过**，含 expr_cond/expr_coal/expr_nav）、`type_inference`、`hover` 回归通过\n\n实现中发现的现有问题及修复：\n- LuaJIT 下 `<<`/`>>` 误报 `UNSUPPORT_SYMBOL`（已修复：LuaJIT 支持 `<<`/`>>`，`//` 仍不支持）\n- `a ~= b` 在语句上下文应识别为异或复合赋值（已修复，与现有 `+=` 简化一致）\n- `continue` 是 soft keyword，需按后续 token 判断（`continue = 2` 是变量赋值）\n- `safe` 字段只在 `safe == true` 时设置，否则普通 AST 会多出 `safe = false`（ast 测试失败教训）\n\n三元 `?:` 实现要点（源码依据 lj_parse.c）：\n- 三元在 `expr()` 尾部（`expr_binop` 完整解析后）检查 `?`，**优先级最低、右结合**\n- b 部分用 `EXPR_F_NOCOLON`（0x02）禁止方法调用：`a ? obj:method() : c` 报错，`a ? (obj:method()) : c` 合法\n- NOCOLON 传播到一元/二元操作数与后缀链；**重置**于括号/索引/参数/表值/lambda\n- **b 部分嵌套三元非法**（`a ? b ? c : d : e` 中 `d:e` 方法调用歧义，与 LuaJIT 一致）；右结合靠 **c 部分递归**（`a ? b : c ? d : e`）\n- compile.lua：`parseExp(asAction, level, noMethod, noTernary)`；操作数位置 `noTernary=true`；`parseSimple` 的 `:` 分支 `noMethod` 时 break；`?.` 后紧跟 `:` 且 noMethod 时 break 不消费\n- AST：`type='ternary'`，`[1]`=条件、`[2]`=b、`[3]`=c；guide childMap `['ternary'] = {1,2,3}`\n- vm：`testCondition` 恒真→b、恒假→c、不确定→b|c 合并\n\n## 概述\n\n为 lua-language-server 增加 LuaJIT v3 反向移植到 v2.1 的扩展语法支持（对应 [LuaLS/lua-language-server#3434](https://github.com/LuaLS/lua-language-server/issues/3434)）。\n\n## 背景与参考\n\n| 主题 | 链接 |\n|------|------|\n| LuaLS 需求 issue | https://github.com/LuaLS/lua-language-server/issues/3434 |\n| LuaJIT v3 语法总览（权威文档，见 MikePall 第一条评论） | https://github.com/LuaJIT/LuaJIT/issues/1475 |\n| 反向移植到 v2.1 说明 | https://github.com/LuaJIT/LuaJIT/issues/1476 |\n| 官方测试用例 gist | https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4 |\n| 本地测试文件 | `test/parser_test/LuaJIT/`（已包裹为 `return [[...]]`） |\n| 语法扩展详细文档 | `references/syntax-extensions.md` |\n\n## 开关设计（新设置）\n\n新增设置：**`Lua.runtime.enableLuaJITExtensions`**（`Boolean`，默认 `false`）\n\n生效条件（**两者同时满足**才启用）：\n1. `Lua.runtime.version == 'LuaJIT'`\n2. `Lua.runtime.enableLuaJITExtensions == true`\n\n传递链路：\n```\n用户配置\n  → script/config/template.lua 注册设置\n  → script/files.lua 构造 options：enableLuaJITExtensions = config.get(uri, 'Lua.runtime.enableLuaJITExtensions')\n  → parser.compile(text, 'Lua', version, options)\n  → script/parser/compile.lua initState 中判定：\n      if version == 'LuaJIT' and options.enableLuaJITExtensions then\n          state.luaJITExtensions = true\n      end\n```\n\n要点：\n- 启用后，相关 LuaJIT 语法**直接生效**，无需用户再去 `Lua.runtime.nonstandardSymbol` 手动勾选。\n- 与 `nonstandardSymbol` 的关系：`continue`、`|lambda|`、`&&`、`||`、`!`、`!=`、复合赋值等原本由 `nonstandardSymbol` 控制；启用本开关时这些项自动视为开启。\n- **单独启用（2026-08-13 完成）**：10 项 LuaJIT 扩展语法也已加入 `Lua.runtime.nonstandardSymbol` 枚举（`?.` `??` `?:` `~>>` `~>>=` `..=` `~=` `const` `->` `number_underscore`），可单独启用，**不要求** version == 'LuaJIT'。生效判定统一走 `compile.lua` 的 `isLuaJITExt(symbol)` helper（`State.luaJITExtensions or nonstandardSymbol[symbol]`）。\n- `//`（注释）与反引号字符串 `` ` `` 属于 LuaLS 自身扩展，**不属于** LuaJIT 语法，保持由 `nonstandardSymbol` 独立控制。\n\n## 支持范围速览\n\n### ✅ 现有代码已支持（本次基本无需改动）\n| 语法 | 现状 |\n|------|------|\n| 位运算 `~ & \\| << >>` | LuaJIT 2.1 原生，compile 已支持 |\n| 自定义运算符 `! && \\|\\| !=` | tokenizer 已有 token；compile 有 `UnaryAlias`/`BinaryAlias`，带 `ERR_NONSTANDARD_SYMBOL` 检查 |\n| 复合赋值 `+= -= *= /= %= &= \\|= <<= >>=` | `expectAssign` 已支持（需 `nonstandardSymbol`） |\n| `continue` 语句 | `parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`） |\n| 短函数 `\\|x\\| -> expr` | `parseLambda` 已支持（`nonstandardSymbol['\\|lambda\\|']`），但 `->` 尚无独立 token |\n| 数字后缀 `LL`/`ULL`/`I` | `dropNumberTail` 已支持（仅 LuaJIT） |\n\n### ✅ 已实现（本次规划核心）\n| # | 语法 | 涉及 | 状态 |\n|---|------|------|------|\n| 1 | `~>>` 算术右移 与 `~>>=` 复合赋值 | tokenizer + `BinarySymbol` + `expectAssign` | ✅ |\n| 2 | `..=` 字符串连接复合赋值 | tokenizer + `expectAssign` | ✅ |\n| 3 | `??` 空值合并 | tokenizer + `BinarySymbol` + 类型语义 | ✅ |\n| 4 | `?.` 安全导航（属性/索引/调用/**方法形式 `:?`**） | tokenizer + `parseSimple` | ✅ |\n| 5 | `const` 声明 | `parseAction` + 局部常量语义 | ✅ |\n| 6 | 数字下划线 `1_000` | tokenizer `Number` + `parseNumber*` | ✅ |\n| 7 | 短函数完整形式（`x -> expr`、`\\|\\| -> expr`、`-> do ... end`） | tokenizer `->` + `parseLambda` 扩展 | ✅ |\n| 8 | **三元 `ternary`**（符号 `?:`，`cond ? x : y`，NOCOLON 屏蔽方法调用、右结合；**key 为 `ternary`，与无点号安全方法 `?:` 拆开**） | `parseExp` + `parseSimple` + guide + vm | ✅ |\n\n### ⏸ 未反向移植到 v2.1（无需支持）\n- `//` 地板除、命名变参 `...name`、位运算元方法、`__add(a, b, true)` 元方法。\n\n## 实现步骤\n\n### 阶段 0：基础设施\n1. `script/config/template.lua`：注册 `Lua.runtime.enableLuaJITExtensions`。\n2. `script/files.lua`：两处（`compileStateAsync` 与 `compileState`）构造 options 时传入。\n3. `script/parser/compile.lua` `initState`：判定并设置 `state.luaJITExtensions`。\n4. 各 `locale/*/setting.lua` 补充设置说明文案。\n\n### 阶段 1：tokenizer（script/parser/tokens.lua）\n新增 token（注意顺序，长 token 在前）：\n- `->`（短函数箭头）\n- `??`、`?.`（均需排在单字符 `?` 之前）\n- `~>>`、`~>>=`（`~>>=` 排在 `~>>` 之前）\n- `..=`（排在 `..` 之前）\n- 说明：`?` 与 `:` 均为单字符 token（已存在），三元无需新 token。\n\n### 阶段 2：compile.lua 语法解析\n按依赖顺序（建议）：\n1. **`~>>`**：`BinarySymbol` 加 `['~>>'] = 7`（与 `<<`/`>>` 同级）。\n2. **复合赋值补全**：`expectAssign` 加 `..=`、`~>>=`。\n3. **数字下划线**：`parseNumber10/16/2` 允许下划线；注意 `dropNumberTail` 需放行下划线。\n4. **`??`**：`BinarySymbol` 加 `['??']`（与 `or` 同级），复用 `parseExp` 的二元循环；检查短路语义。\n5. **`?.`**：`parseSimple` 中 `token == '.'` 分支旁新增 `token == '?.'` 分支；支持 `a?.field`、`a?.[key]`、`f?.()`、`f?.\"str\"`、`f?.{...}`、`a?.field = v`、`a?.[key] = v`；**并支持 `:?` 方法形式**：\n   - `obj?.:method(...)`（检查 obj）\n   - `obj:method?.(...)`（检查 method）\n   - `obj?.:method?.(...)`（两者都检查）\n6. **`const`**：`parseAction` 中处理 soft keyword `const`；`const x = 1` 为块级局部常量，禁止重复声明/重新赋值；`const function foo() end` 一并考虑。\n7. **短函数**：`parseExpUnit` 中识别 `->`；`parseLambda` 支持 `x -> expr`（单参数省略管道）、`|| -> expr`、`-> do ... end` 语句体。\n\n### 阶段 3：AST 与语义层\n- 新节点类型（建议在实现时定稿）：\n  - `?.` → `getfield`/`getindex`/`call`/`getmethod` 的安全导航变体（可加标记字段，如 `safe = true`），语义层需处理 nullable 访问；`obj:method?.` 的检查目标是 method，`obj?.:method` 的检查目标是 obj。\n  - `??` → 复用 `binary`（op = `??`）。\n  - `const` → 复用 `local` 结构并打 const 标记，或新节点类型。\n  - `?:` → `ternary` 节点：`[1]`=条件、`[2]`=b、`[3]`=c（guide childMap 已加 `['ternary'] = {1,2,3}`）。\n- `script/parser/guide.lua`、`script/vm/*`：让类型推断/引用/悬停等理解新节点。\n- 运算符优先级遵循 LuaJIT 官方表（见 references/syntax-extensions.md）。\n\n### 阶段 4：诊断与文案\n- 新错误码直接在 compile.lua 中 `type = 'XXX'` 定义（`script/proto/diagnostic.lua` 的 `getDiagAndErrNameMap` 会自动从源码提取注册）。\n- 需要的新错误码示例：\n  - `?.` 使用但未启用扩展 → 复用 `ERR_NONSTANDARD_SYMBOL` 或新增\n  - `const` 重新赋值 / 重复声明 → 参考测试中的 `assign to const` / `declare const`（注意现已有 `SET_CONST`、`ASSIGN_CONST_GLOBAL`）\n- 各 `locale/*/diagnostic.lua` 补充翻译。\n\n### 阶段 5：测试\n- `test/parser_test/LuaJIT/` 下的 14 个测试文件已包裹为 `return [[...]]`，可作为代码字符串测试源。\n- 需接入现有 parser_test 框架（参考 `test/parser_test/ast/` 的结构），或用语法检查断言（`syntax_check.lua` 的 `TestWith` 模式）。\n- 为开关状态各写测试：未启用时报错、启用后通过。\n\n## 如何测试（实测方法）\n- **直接运行**：`bin/lua-language-server.exe test.lua` —— `lua-language-server.exe` 可直接作为 Lua 运行时执行脚本。\n- **限定测试范围**：`test.lua` 支持 `--name=<pattern>` 参数，`test(name)` 内部用 `name:match(TARGET_TEST_NAME)` 过滤，例如：\n  - `bin/lua-language-server.exe test.lua --name=parser_test` 只跑 `parser_test`\n- **自定义测试脚本**：可自行改造 `test.lua` 或编写独立脚本，直接 `bin/lua-language-server.exe <script>.lua` 运行（需正确设置 `package.path`，见 `test.lua` 开头）。\n- **当前进度验证脚本**：`temp/tmp_test_luajit.lua`（临时，验证 compile 解析 LuaJIT 扩展语法，含开关开关两种状态的断言）。\n- **长耗时命令必须轮询（重要）**：完整测试 / build-doc 后台运行**不会自动通知完成**。运行后须主动轮询日志：`temp/test_full*.log` 出现 `test finish.`（无 error）即通过，full 阶段耗时较长需多次轮询；build-doc 看退出码 `DOC=0`。用 `read_file`/`grep_search` 查日志末尾，不要依赖系统通知。\n\n## 关键文件地图\n\n| 文件 | 职责 |\n|------|------|\n| `script/config/template.lua` | 注册 `Lua.runtime.enableLuaJITExtensions` |\n| `script/files.lua` | 构造 compile options（两处） |\n| `script/parser/tokens.lua` | 词法：新增 token |\n| `script/parser/compile.lua` | 语法：`initState`/`parseExp`/`parseSimple`/`parseLambda`/`expectAssign`/`parseAction`/`parseNumber*`/`dropNumberTail` |\n| `script/parser/guide.lua` | AST 辅助（行号、块、子节点遍历） |\n| `script/vm/*` | 语义/类型推断 |\n| `script/proto/diagnostic.lua` | 错误码注册（自动提取） |\n| `locale/*/setting.lua`、`locale/*/diagnostic.lua` | 文案 |\n| `test/parser_test/LuaJIT/` | LuaJIT 官方测试（已包裹） |\n\n## 注意事项\n- **优先级表**：所有新增运算符的优先级必须严格遵循 LuaJIT 官方定义（见 references/syntax-extensions.md「运算符优先级」）。\n- **三元 `ternary` 已实现（2026-08-13 拆分）**：见 references/syntax-extensions.md 第 3 节；b 部分禁止方法调用（NOCOLON），需括号 `(obj:method())` 绕过；b 部分嵌套三元非法（`d:e` 歧义），c 部分右结合递归合法。**一个 key 只承载一个功能**：三元从 `?:` 拆出，改用 **`ternary`** key（`isLuaJITExt('ternary')`，主开关 enableLuaJITExtensions 也启用，因三元是 LuaJIT 官方语法）；`?:` 专用于无点号安全方法 `obj?:get()`（仅 `nonstandardSymbol['?:']` 门控，主开关不启用）。\n- **`?.` 与 `:` 的歧义**：实现安全导航的方法形式时需小心区分 `obj?.:method`（`?.` + `:`）与 `obj:method?.`（`:` + `?.`），以及普通方法调用 `obj:method`，三者 token 序列不同，需在 `parseSimple` 中分别处理。\n- **`?.` 已拆分为 3 项（2026-08-13）**：`?.`（字段/方法：`a?.b` / `obj?.:method` / `obj:method?.`）、`?.(`（安全调用：`f?.()` / `f?.\"str\"` / `f?.{...}` / `f?.[[...]]`）、`?.[`（安全索引：`t?.[key]`）。parseSimple 在 `?.` 后按后缀选择启用项：`(`/`{`/引号→`?.('`；`[`→看 `Tokens[Index+5]` 是否为 `[`/`=`（长字符串调用 vs 索引）；其他→`?.`。主开关 enableLuaJITExtensions 仍一并启用全部。\n- **无点号可选链 `?(` / `?[`（2026-08-13 新增，非 LuaJIT 语法）**：`f?()` / `t?[1]` 等价于 `f?.()` / `t?.[1]`，AST 一致（`call`/`getindex` 的 `safe=true`）。仅由 `nonstandardSymbol['?(']`/`['?[']` 单独启用，**与 LuaJIT 无关**（主开关 enableLuaJITExtensions 不启用，与版本无关）；与三元（`ternary`）解析冲突（`a ? (x) : c` 会被当作 `a?(x)`），template 注释与 locale 描述均有警告。\n- **无点号安全方法 `obj?:get()`（2026-08-13，非 LuaJIT 语法）**：`?:` 选项只做无点号安全方法（`obj?:get()` = `obj?.:get()`），与三元拆开（三元用 `ternary`）。parseSimple 的 `?` 分支处理 `?`+`:`（需 `nonstandardSymbol['?:']` 且 `not noMethod`，三元 b 部分不消费）。合法三元 `a ? b : c` 的 `?` 后跟表达式不受影响（仅 `?`+`:` 相邻才走方法）。\n- **soft keyword**：`const` 与 `continue` 都是 soft keyword，可作变量名/字段名/函数名，解析时必须先识别上下文。\n- **短路语义**：`??`、`?.` 都有短路行为，虽然语法层不关心求值，但 AST 结构与类型推断需正确表达。\n- **`continue` 与 `repeat`**：`continue` 跳转到循环条件；在 `repeat` 中不能跳入其后声明的局部变量作用域（测试 `stmt_continue.lua` 有覆盖）。\n","frontmatter":{"name":"luajit-syntax","description":"LuaJIT 3.0 扩展语法支持的实施规划与实现指南。Use when implementing LuaJIT extension syntax support (bitwise ~>>, ternary ?:, safe navigation ?., nil-coalescing ??, customary operators ! && || !=, compound assignment, continue, const declaration, short function expression, number underscores) in the Lua language server, when modifying script/parser/compile.lua, script/parser/tokens.lua, script/config/template.lua, script/files.lua, or adding tests under test/parser_test/LuaJIT/."},"isInternal":false,"tokens":5686,"sizeBytes":15589},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/LuaLS/lua-language-server/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"# Coding Instructions\n\n## PowerShell 文件编码\n\n- Windows PowerShell 默认编码为 GBK/UTF-16 LE，**严禁**写入项目文件时不指定编码（会导致 UTF8 文件乱码）\n- `Set-Content`、`Out-File`、`echo >`、`Write-Output >` 等写入操作必须加 `-Encoding UTF8`\n- 正确示例：`Set-Content -Path file.ts -Value \"...\" -Encoding UTF8`\n- 项目所有源码文件统一 UTF-8（无 BOM），由 `.editorconfig` 和 ESLint 兜底\n\n## 长耗时命令轮询\n\n- 后台运行的长时间命令（如 `test.lua` 完整测试、`build-doc.lua`）**不会自动通知完成**，必须主动轮询结果文件确认\n- 完整测试：检查 `temp/test_full*.log` 是否出现 `test finish.`（无 error 即通过；full 阶段耗时较长，需多次轮询）\n- build-doc：检查退出码 `DOC=0`\n- 轮询方式：读取日志文件末尾或 `grep` 结束标记，间隔由命令耗时决定，不要依赖系统通知\n- 推荐命令模式（Windows PowerShell + cmd 重定向，规避管道无法捕获输出的问题）：\n  - `cmd /c \"cd /d <repo> && bin\\lua-language-server.exe test.lua --name=<test> > temp\\test_xx.log 2>&1 & echo EXIT=%ERRORLEVEL%\"`\n  - 完成后用 `read_file` / `grep_search` 查日志，或用 `Select-String` 找结束标记\n\n## Pull Request Review Language\n\nWhen performing pull request reviews, write every finding in both Chinese and English.\n\nUse this format for inline comments and review summaries:\n\n**中文**：<审查意见>\n\n**English**: <equivalent review comment>\n\nKeep both versions semantically equivalent. Report only actionable, evidence-based findings.\n","isInternal":false,"tokens":500,"sizeBytes":1609}],"systemPromptSnippet":"<yakaai_skills repo=\"LuaLS/lua-language-server\">\n<!-- File: .github/skills/luajit-syntax/references/syntax-extensions.md (Tokens: ~4307 | Category: anthropic-skill) -->\n# LuaJIT 3.0 语法扩展参考文档\n\n> 来源：[LuaJIT/LuaJIT#1475](https://github.com/LuaJIT/LuaJIT/issues/1475) MikePall 的第一条评论（权威文档），及 [LuaJIT/LuaJIT#1476](https://github.com/LuaJIT/LuaJIT/issues/1476) 反向移植说明。\n> 反向移植到 v2.1 的扩展由官方测试覆盖：`test/parser_test/LuaJIT/`（来自 gist [a8372d92cb2e6380cf56e78f69ee70a4](https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4)）。\n\n## 反向移植范围（v2.1 backport）\n\n**已移植：**\n1. 位运算：一元 `~`，二元 `& | ~ << >> ~>>`\n2. 自定义运算符 `! && || !=`\n3. 三元条件 `?:`（**本次实现已搁置**，见下文第 3 节）\n4. 安全导航 `?.`（含方法形式 `:?`，全部实现）\n5. 空值合并 `??`\n6. 复合赋值 `+= -= *= …`\n7. `continue` 语句\n8. `const` 声明\n9. 短函数表达式\n10. 数字字面量下划线\n\n**未移植（无需支持）：**\n- 位运算元方法\n- 地板除 `//`\n- 复合赋值元方法 `__add(a, b, true)`\n- 命名变参 `...name`\n\n## 运算符优先级表（从高到低）\n\n| 运算符 / 结构 | 元数 | 结合性 |\n|--------------|------|--------|\n| `( )` | 一元 | - |\n| `->`（短函数） | 二元 | 右 |\n| `.` `:` `[` `]` `f()` `?.` | 二元 | 左 |\n| `^` | 二元 | 右 |\n| `not` `!` `-` `~` `#` | 一元 | - |\n| `*` `/` `//` `%` | 二元 | 左 |\n| `+` `-` | 二元 | 左 |\n| `..` | 二元 | 右 |\n| `<<` `>>` `~>>` | 二元 | 左 |\n| `&` | 二元 | 左 |\n| `~` | 二元 | 左 |\n| `\\|` | 二元 | 左 |\n| `==` `~=` `!=` `<` `>` `<=` `>=` | 二元 | 左 |\n| `and` `&&` | 二元 | 左 |\n| `or` `\\|\\|` `??` | 二元 | 左 |\n| `?:` ⏸ | 三元 | 右 |\n| `=` `compound=` | 二元 | - |\n\n> 对应 `script/parser/compile.lua` 中 `BinarySymbol` 的数值等级（数值越大优先级越高）：\n> - `??` 与 `or` 同级（1）\n> - `?:` 已搁置（若未来实现：单独处理，优先级最低，在 `=` 之上）\n> - `~>>` 与 `<<`/`>>` 同级（7）\n\n## 各语法详解\n\n### 1. 位运算 `~ & | ~ << >> ~>>`\n\n| 运算符 | 功能 | 对应 bit.* 函数 |\n|--------|------|-----------------|\n| `~ a` | 按位取反 | `bit.bnot()` |\n| `a & b` | 按位与 | `bit.band()` |\n| `a \\| b` | 按位或 | `bit.bor()` |\n| `a ~ b` | 按位异或 | `bit.bxor()` |\n| `a << b` | 左移 | `bit.lshift()` |\n| `a >> b` | 逻辑右移 | `bit.rshift()` |\n| `a ~>> b` | **算术右移** | `bit.arshift()` |\n\n- `~>>`（算术右移）当前 **未实现**，是本规划要补的（tokenizer + `BinarySymbol`）。\n- 测试：`expr_bit.lua`、`ffi_expr_bit*.lua`。注意测试中 `LL`/`ULL` 后缀仅 LuaJIT 支持（`dropNumberTail` 已处理）。\n\n### 2. 自定义运算符 `! && || !=`\n\n| 自定义写法 | 等价经典写法 | 功能 |\n|-----------|--------------|------|\n| `!` | `not` | 逻辑非 |\n| `&&` | `and` | 短路与 |\n| `\\|\\|` | `or` | 短路或 |\n| `!=` | `~=` | 不相等比较 |\n\n- 语义与经典运算符完全一致。\n- compile.lua 已有 `UnaryAlias`（`!`）与 `BinaryAlias`（`&&`、`||`、`!=`）；`BinaryAlias` 带 `ERR_NONSTANDARD_SYMBOL` 检查。\n- 测试：`expr_customary.lua`。\n\n### 3. 三元条件 `?:`（研究完成，待实现）\n\n```\na ? b : c\n```\n- `a` 为条件（真值判定同 `if`）；为真求 `b`，否则求 `c`。\n- **右结合**，可嵌套：`cond1 ? a : cond2 ? b : x` = `cond1 ? a : (cond2 ? b : x)`。\n- 短路：未选中的分支不求值。\n- **语法歧义（源码确认）**：b 部分解析使用 `EXPR_F_NOCOLON` 标志（`lj_parse.c expr()`），在后缀解析中遇到 `:` 即 break，禁止方法调用；括号内方法调用需加括号：`cond ? (obj:method()) : default`。\n\n**NOCOLON 传播/重置规则**（源码 `lj_parse.c` 依据）：\n- 传播（eflags 透传）：一元操作数、二元操作数、后缀链（name/表/字符串后缀）\n- 重置（传 0）：括号 `expr(v,0)`、索引 `expr_bracket→expr(v,0)`、函数参数 `parse_args→expr_list→expr(v,0)`、表值、lambda\n- 推论：`a ? x + obj:method() : c` 也报错（操作数传播屏蔽）；`a ? f(obj:method()) : c` 合法（参数重置）\n\n**compile.lua 实现方案**：\n- `parseExp` 二元循环后处理三元（优先级最低、右结合）：b = `parseExp(nil, nil, true)`（noMethod），c = `parseExp()`\n- 加 `noMethod` 参数：`parseExp(asAction, level, noMethod)`、`parseExpUnit(noMethod)`、`parseSimple(node, funcName, noMethod)`\n- `parseSimple` 的 `:` 分支：`if noMethod then break`\n- 重置点：parseParen / parseIndex / parseExpList / parseTable 值 / parseLambda\n- tokenizer 无需改动（`?` `:` 均为单字符 token）\n- AST：`ternary` 节点（`[1]`=条件、`[2]`=b、`[3]`=c），需 guide + vm 类型推断\n- 测试：expr_cond.lua 移入正式测试\n\n**测试要点**（expr_cond.lua）：\n- `a ? obj:method() : c` → 报错 eof；`a ? (a:b()) : c` → 合法\n- `a ? a : b():c() + 30` → 合法（c 部分不屏蔽）\n- 无空格 `a?b:c` → 报错（方法调用歧义）；`a ? : c`、`a ? b :` → 报错\n\n### 4. 安全导航 `?.`\n\n| 原表达式 | 安全导航写法 | 检查对象 |\n|----------|--------------|----------|\n| `a[key]` | `a?.[key]` | a |\n| `a.field` | `a?.field` | a |\n| `a[key] = expr` | `a?.[key] = expr` | a |\n| `a.field = expr` | `a?.field = expr` | a |\n| `f(…)` | `f?.(…)` | f |\n| `f\"…\"` | `f?.\"…\"` | f |\n| `f{…}` | `f?.{…}` | f |\n| `obj:method(…)` | `obj?.:method(…)` | obj |\n| `obj:method(…)` | `obj:method?.(…)` | method |\n| `obj:method(…)` | `obj?.:method?.(…)` | 两者 |\n\n- 规则：`.` 可直接替换为 `?.`；调用类（`()`, `\"\"`, `{}`）与 `:` 需要 `?.` 和原符号**同时出现**。\n- 左结合，可链式：`a?.b?.[c]?.d?.:e?.()`（五种检查全部支持）。\n- 短路：左侧为 nil（或 NULL cdata）时，右侧操作与后续链不执行；赋值时右侧表达式也不求值。\n- 本规划实现 `?.` 的**全部形式**：`.`/`[]`/`()`/`\"\"`/`{}`、赋值形式、以及 `:?` 方法形式（`obj?.:method`、`obj:method?.`、`obj?.:method?.`）。\n- 实现提示：`parseSimple` 中需区分 token 序列 `?.`+`:`（obj?.:method）与 `:`+`?.`（obj:method?.），前者检查 obj，后者检查 method。\n- 测试：`expr_nav.lua`（全部用例应通过）。\n\n### 5. 空值合并 `??`\n\n```\na ?? b\n```\n- 仅当 `a` 等于 nil 时求 `b` 并返回，否则返回 `a`（`a` 只求值一次）。\n- 与 `or` 同级优先级（`or || ??`），短路。\n- 测试：`expr_coal.lua`。\n\n### 6. 复合赋值\n\n| 运算符 | 功能 | 长形式 |\n|--------|------|--------|\n| `a += b` | 加 | `a = a + b` |\n| `a -= b` | 减 | `a = a - b` |\n| `a *= b` | 乘 | `a = a * b` |\n| `a /= b` | 除 | `a = a / b` |\n| `a %= b` | 取模 | `a = a % b` |\n| `a &= b` | 按位与 | `a = a & b` |\n| `a \\|= b` | 按位或 | `a = a \\| b` |\n| `a ~= b` | 按位异或 | `a = a ~ b` |\n| `a <<= b` | 左移 | `a = a << b` |\n| `a >>= b` | 逻辑右移 | `a = a >> b` |\n| `a ~>>= b` | **算术右移** | `a = a ~>> b` |\n| `a ..= b` | **连接** | `a = a .. b` |\n\n- 索引表达式只求值一次前缀：`x.y.z.field += b` ≈ `local tmp = x.y.z; tmp.field = tmp.field + b`。\n- **不支持**并行赋值：`x, y, z += u, v, w` 为语法错误。\n- **没有** `^=`（故意省略，避免与异或混淆）。\n- 可与安全导航组合：`a?.field += f()`。\n- 现状：`+= -= *= /= %= &= |= <<= >>=` 已支持；需新增 `..=` 与 `~>>=`。\n- 测试：`stmt_compound.lua`。\n\n### 7. `continue` 语句\n\n- 跳转语义：`while`/`repeat` → 循环条件；`for` → 循环更新逻辑。\n- 只能作为（嵌套）块的最后一个语句（同 `break`）。\n- **soft keyword**：可作变量名/字段名/函数名/参数名。\n- `repeat` 中不能跳入其后声明的局部变量作用域（同 `goto` 规则）：\n  `repeat if x then continue end; local a = a; until not a` → 报错。\n- 现状：`parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`），但语义是当作 break 处理，需要确认 `repeat`/`for` 的跳转目标正确性。\n- 测试：`stmt_continue.lua`。\n\n### 8. `const` 声明\n\n```\nconst x = 1\nconst y, z = 2, 3\nconst foo                 -- 无赋值，值为 nil，但不可再赋值\nconst function foo() end  -- const 局部函数\n```\n- 块级局部常量：不可重新赋值、不可在相同或内层作用域重复声明、不可作为函数参数重复声明。\n- **soft keyword**：可作变量名、字段名、函数名、参数名（`local const = 1` 合法；`const const = 1` 也合法——第一个是关键字，第二个是名字）。\n- 变量本身不可变，但引用对象（如 table）内容可变。\n- 需新增 `parseAction` 支持；错误码参考测试中的 `assign to const` / `declare const`。\n- 测试：`stmt_const.lua`。\n\n### 9. 短函数表达式\n\n```\n|…| -> expr          -- 返回 expr 结果\n|…| -> do … end      -- 语句块，无 return 则返回 0 个结果\nx -> x+1             -- 单参数可省略管道\n|| -> true           -- 无参数\n|a, b, ...| -> ...   -- 支持变参\n```\n- 可在任何函数表达式位置使用。\n- expr 变体只返回**单个**结果（多返回用语句块形式）。\n- `->` 优先级最高（仅低于 `()`），右结合。\n- 现状：`|x| -> expr` 已有（`|lambda|`），但 `->` 无独立 token；需补 `x -> expr`、`|| -> expr`、`-> do … end`。\n- 测试：`expr_shortfunc.lua`。\n\n### 10. 数字字面量下划线\n\n```\n1_234_567_890\n0x1_2     0b1_0\n1_2.3_4   1e1_0\n0x1_2p_3\n```\n- 下划线在**任何位置**（初始数字之后）都被忽略，适用于所有进制、所有后缀（含 `LL`/`ULL`）、整数与浮点。\n- `tonumber()` 与字符串隐式转换**不接受**下划线。\n- 需修改 tokenizer 的 `Number` 规则与 `parseNumber10/16/2`。\n- 测试：`number_underscore.lua`。\n\n## 测试文件对照表\n\n| 文件 | 覆盖语法 |\n|------|----------|\n| `expr_bit.lua` | 位运算（`~ & \\| << >> ~>>`） |\n| `expr_bit_bitop.lua` | `bit.*` 库运行时行为（语法层面价值低） |\n| `expr_coal.lua` | `??` 空值合并 |\n| `expr_cond.lua` ⏸ | `?:` 三元条件（已搁置，本阶段不要求通过） |\n| `expr_customary.lua` | `! && \\|\\| !=` |\n| `expr_nav.lua` | `?.` 安全导航（全部形式，含 `:?` 方法形式） |\n| `expr_shortfunc.lua` | 短函数 `->` |\n| `ffi_expr_bit.lua` / `_bit64` / `_bitop` | FFI 位运算 + `LL`/`ULL` 后缀 |\n| `number_underscore.lua` | 数字下划线 |\n| `stmt_compound.lua` | 复合赋值 |\n| `stmt_const.lua` | `const` 声明 |\n| `stmt_continue.lua` | `continue` 语句 |\n\n> 补充：`expr_cond.lua`（三元 `?:`）已搁置，本阶段不要求通过。\n> 所有文件内容现已被包裹为 `return [[ … ]]` 字符串形式，可作为代码字符串测试源。\n\n<!-- File: .github/skills/luajit-syntax/SKILL.md (Tokens: ~5686 | Category: anthropic-skill) -->\n---\nname: luajit-syntax\ndescription: 'LuaJIT 3.0 扩展语法支持的实施规划与实现指南。Use when implementing LuaJIT extension syntax support (bitwise ~>>, ternary ?:, safe navigation ?., nil-coalescing ??, customary operators ! && || !=, compound assignment, continue, const declaration, short function expression, number underscores) in the Lua language server, when modifying script/parser/compile.lua, script/parser/tokens.lua, script/config/template.lua, script/files.lua, or adding tests under test/parser_test/LuaJIT/.'\n---\n\n# LuaJIT 扩展语法支持规划\n\n## 实施进度\n\n| 阶段 | 内容 | 状态 |\n|------|------|------|\n| 阶段 0 | 设置 `Lua.runtime.enableLuaJITExtensions`、options 传递、initState 判定（语法处单独判断） | ✅ 完成 |\n| 阶段 1 | tokenizer 新增 `-> ?? ?. ~>> ~>>= ..=` | ✅ 完成 |\n| 阶段 2 | 语法解析（`~>>`、复合赋值、数字下划线、`??`、`?.` 全形式、`const`、短函数、`continue` soft keyword、`a ~= b` 异或赋值） | ✅ 完成 |\n| 阶段 3 | 语义层（`?.`→optional、`??` 类型合并、`~>>`→integer） | ✅ 完成 |\n| 阶段 4 | 诊断文案 `PARSER_DECLARE_CONST`（6 语言） | ✅ 完成 |\n| 阶段 5 | 正式测试接入 `test/parser_test/luajit_ext.lua` | ✅ 完成 |\n| 阶段 6 | 三元 `?:`（noMethod/noTernary 机制、ternary AST 节点、vm 类型推断） | ✅ 完成 |\n\n验证：\n- 语法层 40 用例（`temp/tmp_test_luajit.lua`）、语义层 7 用例（`temp/test_luajit_semantic.lua`）通过\n- 三元语法用例（`temp/tmp_test_ternary.lua`）、语义用例（`temp/test_ternary_semantic.lua`）通过\n- `parser_test`（含 `luajit_ext`：**14 个官方测试文件全部编译通过**，含 expr_cond/expr_coal/expr_nav）、`type_inference`、`hover` 回归通过\n\n实现中发现的现有问题及修复：\n- LuaJIT 下 `<<`/`>>` 误报 `UNSUPPORT_SYMBOL`（已修复：LuaJIT 支持 `<<`/`>>`，`//` 仍不支持）\n- `a ~= b` 在语句上下文应识别为异或复合赋值（已修复，与现有 `+=` 简化一致）\n- `continue` 是 soft keyword，需按后续 token 判断（`continue = 2` 是变量赋值）\n- `safe` 字段只在 `safe == true` 时设置，否则普通 AST 会多出 `safe = false`（ast 测试失败教训）\n\n三元 `?:` 实现要点（源码依据 lj_parse.c）：\n- 三元在 `expr()` 尾部（`expr_binop` 完整解析后）检查 `?`，**优先级最低、右结合**\n- b 部分用 `EXPR_F_NOCOLON`（0x02）禁止方法调用：`a ? obj:method() : c` 报错，`a ? (obj:method()) : c` 合法\n- NOCOLON 传播到一元/二元操作数与后缀链；**重置**于括号/索引/参数/表值/lambda\n- **b 部分嵌套三元非法**（`a ? b ? c : d : e` 中 `d:e` 方法调用歧义，与 LuaJIT 一致）；右结合靠 **c 部分递归**（`a ? b : c ? d : e`）\n- compile.lua：`parseExp(asAction, level, noMethod, noTernary)`；操作数位置 `noTernary=true`；`parseSimple` 的 `:` 分支 `noMethod` 时 break；`?.` 后紧跟 `:` 且 noMethod 时 break 不消费\n- AST：`type='ternary'`，`[1]`=条件、`[2]`=b、`[3]`=c；guide childMap `['ternary'] = {1,2,3}`\n- vm：`testCondition` 恒真→b、恒假→c、不确定→b|c 合并\n\n## 概述\n\n为 lua-language-server 增加 LuaJIT v3 反向移植到 v2.1 的扩展语法支持（对应 [LuaLS/lua-language-server#3434](https://github.com/LuaLS/lua-language-server/issues/3434)）。\n\n## 背景与参考\n\n| 主题 | 链接 |\n|------|------|\n| LuaLS 需求 issue | https://github.com/LuaLS/lua-language-server/issues/3434 |\n| LuaJIT v3 语法总览（权威文档，见 MikePall 第一条评论） | https://github.com/LuaJIT/LuaJIT/issues/1475 |\n| 反向移植到 v2.1 说明 | https://github.com/LuaJIT/LuaJIT/issues/1476 |\n| 官方测试用例 gist | https://gist.github.com/MikePall/a8372d92cb2e6380cf56e78f69ee70a4 |\n| 本地测试文件 | `test/parser_test/LuaJIT/`（已包裹为 `return [[...]]`） |\n| 语法扩展详细文档 | `references/syntax-extensions.md` |\n\n## 开关设计（新设置）\n\n新增设置：**`Lua.runtime.enableLuaJITExtensions`**（`Boolean`，默认 `false`）\n\n生效条件（**两者同时满足**才启用）：\n1. `Lua.runtime.version == 'LuaJIT'`\n2. `Lua.runtime.enableLuaJITExtensions == true`\n\n传递链路：\n```\n用户配置\n  → script/config/template.lua 注册设置\n  → script/files.lua 构造 options：enableLuaJITExtensions = config.get(uri, 'Lua.runtime.enableLuaJITExtensions')\n  → parser.compile(text, 'Lua', version, options)\n  → script/parser/compile.lua initState 中判定：\n      if version == 'LuaJIT' and options.enableLuaJITExtensions then\n          state.luaJITExtensions = true\n      end\n```\n\n要点：\n- 启用后，相关 LuaJIT 语法**直接生效**，无需用户再去 `Lua.runtime.nonstandardSymbol` 手动勾选。\n- 与 `nonstandardSymbol` 的关系：`continue`、`|lambda|`、`&&`、`||`、`!`、`!=`、复合赋值等原本由 `nonstandardSymbol` 控制；启用本开关时这些项自动视为开启。\n- **单独启用（2026-08-13 完成）**：10 项 LuaJIT 扩展语法也已加入 `Lua.runtime.nonstandardSymbol` 枚举（`?.` `??` `?:` `~>>` `~>>=` `..=` `~=` `const` `->` `number_underscore`），可单独启用，**不要求** version == 'LuaJIT'。生效判定统一走 `compile.lua` 的 `isLuaJITExt(symbol)` helper（`State.luaJITExtensions or nonstandardSymbol[symbol]`）。\n- `//`（注释）与反引号字符串 `` ` `` 属于 LuaLS 自身扩展，**不属于** LuaJIT 语法，保持由 `nonstandardSymbol` 独立控制。\n\n## 支持范围速览\n\n### ✅ 现有代码已支持（本次基本无需改动）\n| 语法 | 现状 |\n|------|------|\n| 位运算 `~ & \\| << >>` | LuaJIT 2.1 原生，compile 已支持 |\n| 自定义运算符 `! && \\|\\| !=` | tokenizer 已有 token；compile 有 `UnaryAlias`/`BinaryAlias`，带 `ERR_NONSTANDARD_SYMBOL` 检查 |\n| 复合赋值 `+= -= *= /= %= &= \\|= <<= >>=` | `expectAssign` 已支持（需 `nonstandardSymbol`） |\n| `continue` 语句 | `parseAction` 已支持（`nonstandardSymbol['continue']` → `parseBreak()`） |\n| 短函数 `\\|x\\| -> expr` | `parseLambda` 已支持（`nonstandardSymbol['\\|lambda\\|']`），但 `->` 尚无独立 token |\n| 数字后缀 `LL`/`ULL`/`I` | `dropNumberTail` 已支持（仅 LuaJIT） |\n\n### ✅ 已实现（本次规划核心）\n| # | 语法 | 涉及 | 状态 |\n|---|------|------|------|\n| 1 | `~>>` 算术右移 与 `~>>=` 复合赋值 | tokenizer + `BinarySymbol` + `expectAssign` | ✅ |\n| 2 | `..=` 字符串连接复合赋值 | tokenizer + `expectAssign` | ✅ |\n| 3 | `??` 空值合并 | tokenizer + `BinarySymbol` + 类型语义 | ✅ |\n| 4 | `?.` 安全导航（属性/索引/调用/**方法形式 `:?`**） | tokenizer + `parseSimple` | ✅ |\n| 5 | `const` 声明 | `parseAction` + 局部常量语义 | ✅ |\n| 6 | 数字下划线 `1_000` | tokenizer `Number` + `parseNumber*` | ✅ |\n| 7 | 短函数完整形式（`x -> expr`、`\\|\\| -> expr`、`-> do ... end`） | tokenizer `->` + `parseLambda` 扩展 | ✅ |\n| 8 | **三元 `ternary`**（符号 `?:`，`cond ? x : y`，NOCOLON 屏蔽方法调用、右结合；**key 为 `ternary`，与无点号安全方法 `?:` 拆开**） | `parseExp` + `parseSimple` + guide + vm | ✅ |\n\n### ⏸ 未反向移植到 v2.1（无需支持）\n- `//` 地板除、命名变参 `...name`、位运算元方法、`__add(a, b, true)` 元方法。\n\n## 实现步骤\n\n### 阶段 0：基础设施\n1. `script/config/template.lua`：注册 `Lua.runtime.enableLuaJITExtensions`。\n2. `script/files.lua`：两处（`compileStateAsync` 与 `compileState`）构造 options 时传入。\n3. `script/parser/compile.lua` `initState`：判定并设置 `state.luaJITExtensions`。\n4. 各 `locale/*/setting.lua` 补充设置说明文案。\n\n### 阶段 1：tokenizer（script/parser/tokens.lua）\n新增 token（注意顺序，长 token 在前）：\n- `->`（短函数箭头）\n- `??`、`?.`（均需排在单字符 `?` 之前）\n- `~>>`、`~>>=`（`~>>=` 排在 `~>>` 之前）\n- `..=`（排在 `..` 之前）\n- 说明：`?` 与 `:` 均为单字符 token（已存在），三元无需新 token。\n\n### 阶段 2：compile.lua 语法解析\n按依赖顺序（建议）：\n1. **`~>>`**：`BinarySymbol` 加 `['~>>'] = 7`（与 `<<`/`>>` 同级）。\n2. **复合赋值补全**：`expectAssign` 加 `..=`、`~>>=`。\n3. **数字下划线**：`parseNumber10/16/2` 允许下划线；注意 `dropNumberTail` 需放行下划线。\n4. **`??`**：`BinarySymbol` 加 `['??']`（与 `or` 同级），复用 `parseExp` 的二元循环；检查短路语义。\n5. **`?.`**：`parseSimple` 中 `token == '.'` 分支旁新增 `token == '?.'` 分支；支持 `a?.field`、`a?.[key]`、`f?.()`、`f?.\"str\"`、`f?.{...}`、`a?.field = v`、`a?.[key] = v`；**并支持 `:?` 方法形式**：\n   - `obj?.:method(...)`（检查 obj）\n   - `obj:method?.(...)`（检查 method）\n   - `obj?.:method?.(...)`（两者都检查）\n6. **`const`**：`parseAction` 中处理 soft keyword `const`；`const x = 1` 为块级局部常量，禁止重复声明/重新赋值；`const function foo() end` 一并考虑。\n7. **短函数**：`parseExpUnit` 中识别 `->`；`parseLambda` 支持 `x -> expr`（单参数省略管道）、`|| -> expr`、`-> do ... end` 语句体。\n\n### 阶段 3：AST 与语义层\n- 新节点类型（建议在实现时定稿）：\n  - `?.` → `getfield`/`getindex`/`call`/`getmethod` 的安全导航变体（可加标记字段，如 `safe = true`），语义层需处理 nullable 访问；`obj:method?.` 的检查目标是 method，`obj?.:method` 的检查目标是 obj。\n  - `??` → 复用 `binary`（op = `??`）。\n  - `const` → 复用 `local` 结构并打 const 标记，或新节点类型。\n  - `?:` → `ternary` 节点：`[1]`=条件、`[2]`=b、`[3]`=c（guide childMap 已加 `['ternary'] = {1,2,3}`）。\n- `script/parser/guide.lua`、`script/vm/*`：让类型推断/引用/悬停等理解新节点。\n- 运算符优先级遵循 LuaJIT 官方表（见 references/syntax-extensions.md）。\n\n### 阶段 4：诊断与文案\n- 新错误码直接在 compile.lua 中 `type = 'XXX'` 定义（`script/proto/diagnostic.lua` 的 `getDiagAndErrNameMap` 会自动从源码提取注册）。\n- 需要的新错误码示例：\n  - `?.` 使用但未启用扩展 → 复用 `ERR_NONSTANDARD_SYMBOL` 或新增\n  - `const` 重新赋值 / 重复声明 → 参考测试中的 `assign to const` / `declare const`（注意现已有 `SET_CONST`、`ASSIGN_CONST_GLOBAL`）\n- 各 `locale/*/diagnostic.lua` 补充翻译。\n\n### 阶段 5：测试\n- `test/parser_test/LuaJIT/` 下的 14 个测试文件已包裹为 `return [[...]]`，可作为代码字符串测试源。\n- 需接入现有 parser_test 框架（参考 `test/parser_test/ast/` 的结构），或用语法检查断言（`syntax_check.lua` 的 `TestWith` 模式）。\n- 为开关状态各写测试：未启用时报错、启用后通过。\n\n## 如何测试（实测方法）\n- **直接运行**：`bin/lua-language-server.exe test.lua` —— `lua-language-server.exe` 可直接作为 Lua 运行时执行脚本。\n- **限定测试范围**：`test.lua` 支持 `--name=<pattern>` 参数，`test(name)` 内部用 `name:match(TARGET_TEST_NAME)` 过滤，例如：\n  - `bin/lua-language-server.exe test.lua --name=parser_test` 只跑 `parser_test`\n- **自定义测试脚本**：可自行改造 `test.lua` 或编写独立脚本，直接 `bin/lua-language-server.exe <script>.lua` 运行（需正确设置 `package.path`，见 `test.lua` 开头）。\n- **当前进度验证脚本**：`temp/tmp_test_luajit.lua`（临时，验证 compile 解析 LuaJIT 扩展语法，含开关开关两种状态的断言）。\n- **长耗时命令必须轮询（重要）**：完整测试 / build-doc 后台运行**不会自动通知完成**。运行后须主动轮询日志：`temp/test_full*.log` 出现 `test finish.`（无 error）即通过，full 阶段耗时较长需多次轮询；build-doc 看退出码 `DOC=0`。用 `read_file`/`grep_search` 查日志末尾，不要依赖系统通知。\n\n## 关键文件地图\n\n| 文件 | 职责 |\n|------|------|\n| `script/config/template.lua` | 注册 `Lua.runtime.enableLuaJITExtensions` |\n| `script/files.lua` | 构造 compile options（两处） |\n| `script/parser/tokens.lua` | 词法：新增 token |\n| `script/parser/compile.lua` | 语法：`initState`/`parseExp`/`parseSimple`/`parseLambda`/`expectAssign`/`parseAction`/`parseNumber*`/`dropNumberTail` |\n| `script/parser/guide.lua` | AST 辅助（行号、块、子节点遍历） |\n| `script/vm/*` | 语义/类型推断 |\n| `script/proto/diagnostic.lua` | 错误码注册（自动提取） |\n| `locale/*/setting.lua`、`locale/*/diagnostic.lua` | 文案 |\n| `test/parser_test/LuaJIT/` | LuaJIT 官方测试（已包裹） |\n\n## 注意事项\n- **优先级表**：所有新增运算符的优先级必须严格遵循 LuaJIT 官方定义（见 references/syntax-extensions.md「运算符优先级」）。\n- **三元 `ternary` 已实现（2026-08-13 拆分）**：见 references/syntax-extensions.md 第 3 节；b 部分禁止方法调用（NOCOLON），需括号 `(obj:method())` 绕过；b 部分嵌套三元非法（`d:e` 歧义），c 部分右结合递归合法。**一个 key 只承载一个功能**：三元从 `?:` 拆出，改用 **`ternary`** key（`isLuaJITExt('ternary')`，主开关 enableLuaJITExtensions 也启用，因三元是 LuaJIT 官方语法）；`?:` 专用于无点号安全方法 `obj?:get()`（仅 `nonstandardSymbol['?:']` 门控，主开关不启用）。\n- **`?.` 与 `:` 的歧义**：实现安全导航的方法形式时需小心区分 `obj?.:method`（`?.` + `:`）与 `obj:method?.`（`:` + `?.`），以及普通方法调用 `obj:method`，三者 token 序列不同，需在 `parseSimple` 中分别处理。\n- **`?.` 已拆分为 3 项（2026-08-13）**：`?.`（字段/方法：`a?.b` / `obj?.:method` / `obj:method?.`）、`?.(`（安全调用：`f?.()` / `f?.\"str\"` / `f?.{...}` / `f?.[[...]]`）、`?.[`（安全索引：`t?.[key]`）。parseSimple 在 `?.` 后按后缀选择启用项：`(`/`{`/引号→`?.('`；`[`→看 `Tokens[Index+5]` 是否为 `[`/`=`（长字符串调用 vs 索引）；其他→`?.`。主开关 enableLuaJITExtensions 仍一并启用全部。\n- **无点号可选链 `?(` / `?[`（2026-08-13 新增，非 LuaJIT 语法）**：`f?()` / `t?[1]` 等价于 `f?.()` / `t?.[1]`，AST 一致（`call`/`getindex` 的 `safe=true`）。仅由 `nonstandardSymbol['?(']`/`['?[']` 单独启用，**与 LuaJIT 无关**（主开关 enableLuaJITExtensions 不启用，与版本无关）；与三元（`ternary`）解析冲突（`a ? (x) : c` 会被当作 `a?(x)`），template 注释与 locale 描述均有警告。\n- **无点号安全方法 `obj?:get()`（2026-08-13，非 LuaJIT 语法）**：`?:` 选项只做无点号安全方法（`obj?:get()` = `obj?.:get()`），与三元拆开（三元用 `ternary`）。parseSimple 的 `?` 分支处理 `?`+`:`（需 `nonstandardSymbol['?:']` 且 `not noMethod`，三元 b 部分不消费）。合法三元 `a ? b : c` 的 `?` 后跟表达式不受影响（仅 `?`+`:` 相邻才走方法）。\n- **soft keyword**：`const` 与 `continue` 都是 soft keyword，可作变量名/字段名/函数名，解析时必须先识别上下文。\n- **短路语义**：`??`、`?.` 都有短路行为，虽然语法层不关心求值，但 AST 结构与类型推断需正确表达。\n- **`continue` 与 `repeat`**：`continue` 跳转到循环条件；在 `repeat` 中不能跳入其后声明的局部变量作用域（测试 `stmt_continue.lua` 有覆盖）。\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~500 | Category: copilot-instructions) -->\n# Coding Instructions\n\n## PowerShell 文件编码\n\n- Windows PowerShell 默认编码为 GBK/UTF-16 LE，**严禁**写入项目文件时不指定编码（会导致 UTF8 文件乱码）\n- `Set-Content`、`Out-File`、`echo >`、`Write-Output >` 等写入操作必须加 `-Encoding UTF8`\n- 正确示例：`Set-Content -Path file.ts -Value \"...\" -Encoding UTF8`\n- 项目所有源码文件统一 UTF-8（无 BOM），由 `.editorconfig` 和 ESLint 兜底\n\n## 长耗时命令轮询\n\n- 后台运行的长时间命令（如 `test.lua` 完整测试、`build-doc.lua`）**不会自动通知完成**，必须主动轮询结果文件确认\n- 完整测试：检查 `temp/test_full*.log` 是否出现 `test finish.`（无 error 即通过；full 阶段耗时较长，需多次轮询）\n- build-doc：检查退出码 `DOC=0`\n- 轮询方式：读取日志文件末尾或 `grep` 结束标记，间隔由命令耗时决定，不要依赖系统通知\n- 推荐命令模式（Windows PowerShell + cmd 重定向，规避管道无法捕获输出的问题）：\n  - `cmd /c \"cd /d <repo> && bin\\lua-language-server.exe test.lua --name=<test> > temp\\test_xx.log 2>&1 & echo EXIT=%ERRORLEVEL%\"`\n  - 完成后用 `read_file` / `grep_search` 查日志，或用 `Select-String` 找结束标记\n\n## Pull Request Review Language\n\nWhen performing pull request reviews, write every finding in both Chinese and English.\n\nUse this format for inline comments and review summaries:\n\n**中文**：<审查意见>\n\n**English**: <equivalent review comment>\n\nKeep both versions semantically equivalent. Report only actionable, evidence-based findings.\n</yakaai_skills>"}