From 57518578903a44c1632b60e8633894f236a7e8ed Mon Sep 17 00:00:00 2001 From: grom Date: Fri, 30 Oct 2026 09:02:27 -0600 Subject: [PATCH] The realm's client is not stock 1.12, and its Lua grammar is not 1.12's either MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SoftSpoken (z1xel/SoftSpoken, a 1.12 narration port) failed to load: nine files across six addons would not compile, and the three that did then raised at runtime indexing namespaces the failed chunks never built. Every one of the nine is valid Lua 5.1 and invalid Lua 5.0 — `#`, `%`, `0x3040`, `local NAME = ...`. `third_party/lua-src` is a fork that deliberately 5.0-ifies the grammar: three arms deleted from `lparser.c` (`simpleexp`'s `TK_DOTS`, `getunopr`'s `'#'`, `getbinopr`'s `'%'`) and `read_numeral` replaced with 5.0's, which has no hex. Every deletion is byte-read out of `WoW.exe`'s own parser and every one is right about stock 1.12. No OctoWoW client runs stock 1.12. ClassicAPI.dll is in the realm's bundle and rewrites every chunk at the `luaL_loadbuffer` chokepoint — file scripts, `loadstring` and XML `OnLoad` alike — adding exactly five constructs: `#`, `%`, `...` as an expression, `0x` hex and `[=[ ]=]`. Confirmed three ways: the repo's own highlights line, PR #28, and the DLL's string pool, which twice says "Our 5.1 syntax backport makes the probe pass". `docs/ADDON_COMPAT.md`'s row for this said "free — benilla's VM is mlua/Lua 5.1 already". That was the error. So the target grammar is the union, and the fork now carries it: * the three parser arms restored, as stock 5.1 writes them; * `read_numeral` grown an explicit `0x` arm. It could not take 5.1's version back wholesale: 5.1 reached hex through a trailing `isalnum` slurp, and that slurp has to stay gone or `1then` breaks again (`AdvancedTradeSkillWindow2.lua:3878`, which the client compiles). The prefix arm gives both; * addon file chunks now called with `(addonName, addonPrivateTable)` (`UiScript::run_addon_chunk`, four call sites). Taking `...` without passing the arguments only moves the failure one line later, which is what ClassicAPI's own notes record; * `string.reverse` added to the stdlib backport — SoftSpoken calls it. 1.12's own dialect is untouched. `LUA_COMPAT_VARARG` withholds the `arg` table only from a function that *uses* `...` as a value, so the shipped FrameXML's `function f(...) … arg.n … end` still gets `arg`: the two dialects coexist one function at a time, which is what the client-plus-ClassicAPI does. The cost is the era probe. 170 corpus `loadstring` sites ask `loadstring("return function(...) return ... end")` and 92 library files in 24 folders branch on the answer; they now take the 5.1 branch, as they do on the real client. Two branch wrongly there and ClassicAPI ships a compensation for each — Compost-2.0 rev 17406+'s no-argument `Erase` stub (bundled by BigWigs and QuickHeal; it silently stops clearing in place) and Cartographer_Notes' `WorldMapDeathRelease`. Both are now carried in `lua51_probe_fallout.rs`, probed by behaviour rather than by revision and re-checked on every `ADDON_LOADED`, because AceLibrary can upgrade a library mid-session. Measured on grom's own AddOns folder: SoftSpoken, SoftSpoken_Stories, SoftSpoken_Places, SoftSpoken_Writings, SpokenZonesAudio, SpokenBooksAudio, SpokenQuestsAudio{Horde,Alliance,Gossip,Shared}, SSS_VOFallback and AI_VoiceOverData_Turtle all load with zero load errors and zero session errors, 98 `Spoken*` globals, `/softspoken` registered, and PLAYER_ENTERING_WORLD, QUEST_DETAIL, GOSSIP_SHOW and ITEM_TEXT_BEGIN reaching 108/108, 1/1, 6/6 and 2/2 handlers clean. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn --- crates/benilla-app/src/addon_harness/mod.rs | 5 +- crates/benilla-app/src/ui_script/addons.rs | 11 +- .../benilla-app/src/ui_script/shape_gate.rs | 5 +- crates/benilla-ui/src/loader/mod.rs | 26 ++- crates/benilla-ui/src/script/addon.rs | 15 +- .../compat/classic_api/lua51_probe_fallout.rs | 189 ++++++++++++++++++ .../src/script/compat/classic_api/lua_ext.rs | 3 +- .../src/script/compat/classic_api/mod.rs | 27 ++- .../compat/classic_api/stdlib_backport.rs | 10 +- .../compat/classic_api/string_methods.rs | 28 ++- crates/benilla-ui/src/script/lua50.rs | 124 +++++++----- crates/benilla-ui/src/script/mod.rs | 52 ++++- crates/benilla-ui/src/script/raise_prof.rs | 7 +- crates/benilla-ui/src/script/tests/stdlib.rs | 19 +- docs/ADDON_COMPAT.md | 68 ++++++- third_party/lua-src/BENILLA.md | 55 +++-- third_party/lua-src/lua-5.1.5/llex.c | 28 ++- third_party/lua-src/lua-5.1.5/lparser.c | 76 ++++--- 18 files changed, 582 insertions(+), 166 deletions(-) create mode 100644 crates/benilla-ui/src/script/compat/classic_api/lua51_probe_fallout.rs diff --git a/crates/benilla-app/src/addon_harness/mod.rs b/crates/benilla-app/src/addon_harness/mod.rs index 801efd17..ee933499 100644 --- a/crates/benilla-app/src/addon_harness/mod.rs +++ b/crates/benilla-app/src/addon_harness/mod.rs @@ -1645,7 +1645,10 @@ fn load_addon_files(script: &mut UiScript, root: &Path, name: &str, toc: &Toc) - // Named as the 1.12 client names it (`@Interface\AddOns\\`, from the // resolved path, as the loader names `