The realm's client is not stock 1.12, and its Lua grammar is not 1.12's either
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
Vendored
+25
-3
@@ -193,9 +193,30 @@ static void trydecpoint (LexState *ls, SemInfo *seminfo) {
|
||||
/* LUA_NUMBER */
|
||||
static void read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
lua_assert(isdigit(ls->current));
|
||||
do {
|
||||
/* benilla: hexadecimal literals, one of the five constructs ClassicAPI's 5.1 syntax backport
|
||||
** adds to the OctoWoW client (`third_party/lua-src/BENILLA.md'). Stock 1.12's read_numeral
|
||||
** (0x6ffee0) has a digit loop and nothing else, so it reads `0xFF' as the number 0 followed by
|
||||
** the name `xFF' and the statement around it does not parse. This arm is its own because it
|
||||
** cannot share the decimal loop: 5.1's way of reaching hex was a trailing
|
||||
** `while (isalnum(c) || c == '_')' slurp, and that slurp is the very thing that has to stay
|
||||
** deleted -- it would also swallow the word glued to a numeral's tail, costing us `1then'
|
||||
** (`AdvancedTradeSkillWindow2.lua:3878'), which the client's own lexer compiles. Taking hex on
|
||||
** an explicit `0x' prefix instead gives both: `0xFF' is 255 and `1then' is still two tokens.
|
||||
** luaO_str2d does the conversion (5.1's own hex path, `strtoul' base 16). */
|
||||
if (ls->current == '0') {
|
||||
save_and_next(ls);
|
||||
if (ls->current == 'x' || ls->current == 'X') {
|
||||
save_and_next(ls);
|
||||
while (isxdigit(ls->current))
|
||||
save_and_next(ls);
|
||||
save(ls, '\0');
|
||||
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r))
|
||||
luaX_lexerror(ls, "malformed number", TK_NUMBER);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (isdigit(ls->current) || ls->current == '.')
|
||||
save_and_next(ls);
|
||||
} while (isdigit(ls->current) || ls->current == '.');
|
||||
/* benilla: 5.1's trailing `while (isalnum(ls->current) || ls->current == '_')
|
||||
** save_and_next(ls);' is replaced by 5.0's own exponent-digit loop, which is what the client's
|
||||
** read_numeral (0x6ffee0) has: it saves the `E', takes an optional sign (the 0x2b/0x2d test at
|
||||
@@ -203,7 +224,8 @@ static void read_numeral (LexState *ls, SemInfo *seminfo) {
|
||||
** the ctype digit mask 0x4 at 0x6fff47, the only character-class test in the whole routine --
|
||||
** and stops. Nothing slurps the following word, so the client reads `1then' as the number 1 and
|
||||
** the name `then', which is why `if Item.ReplaceWords == 1then' compiles there; and nothing
|
||||
** slurps an `x' either, so 1.12 has no hex literals, reading `0xFF' as 0 and the name `xFF'.
|
||||
** slurps an `x' either -- which is why stock 1.12 has no hex literals at all, and why the
|
||||
** `0x' arm above this loop is the one place benilla takes them.
|
||||
** 5.1 gave both runs to the numeral and then failed it. Its slurp also carried the exponent's
|
||||
** own digits, so deleting it alone would have cost us `1e2' -- hence the loop, not a deletion.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user