Jump to content

Module talk:Auphen: Difference between revisions

From Yezur Wiki
Khurouan (talk | contribs)
Document the engine's design and provenance
 
Khurouan (talk | contribs)
Log the % fix (D15)
Line 15: Line 15:
* '''D6''' — local conditions inside an affix rule are cleanly false.
* '''D6''' — local conditions inside an affix rule are cleanly false.
* '''D14''' — category→category mapping pairs members by '''declaration order''', not a longest-first sort, so digraph categories map correctly (e.g. palatalization <code>[H]/[J]</code> where <code>[J]</code> holds <code>tʃ, dʒ</code>: declared order gives s→ʃ, t→tʃ, g→ɣ). The JS sorted members longest-first, silently mapping s→tʃ. Longest-first is still used for ''matching'', so multi-character phones still match greedily.
* '''D14''' — category→category mapping pairs members by '''declaration order''', not a longest-first sort, so digraph categories map correctly (e.g. palatalization <code>[H]/[J]</code> where <code>[J]</code> holds <code>tʃ, dʒ</code>: declared order gives s→ʃ, t→tʃ, g→ɣ). The JS sorted members longest-first, silently mapping s→tʃ. Longest-first is still used for ''matching'', so multi-character phones still match greedily.
* '''D15''' — <code>%</code> ("same as the target") substitutes on '''every''' occurrence in a result, so <code>%%</code> doubles the target (e.g. gemination <code>[C]/%%/_4</code>). The JS replaced only the first <code>%</code>. A literal per-cent sign in output is written <code>!%</code>.


Divergences from CWS (documented quirks fixed, not reproduced):
Divergences from CWS (documented quirks fixed, not reproduced):

Revision as of 12:35, 18 July 2026

This page records the design, provenance, and behavioural divergences of Module:Auphen — out-of-frame notes, kept off the module source.

Provenance

Auphen is a pure-Lua (Scribunto) port of a JavaScript sound-change engine, itself an implementation of ConWorkShop's PhoMo rule notation. Rule anatomy (target / change / environment / exception / else), the affix-vs-replace split, category sets, and the operator set follow PhoMo; the divergences below are deliberate. The engine is developed and tested off-wiki: a 334-case local suite covering every operation, ground-truth parity against the original JS engine, and the live YBS-PJ ruleset.

Divergences

Fixed bugs in the source JS engine (each has a proving test off-wiki):

  • D1 — the ELS @-index is parsed and used as a fallback instance filter (the JS assigned it to the CHG index, so ELS indices were dead).
  • D2 — ditto " fires inside the exception as well as the environment (the JS dropped the "previous" flag).
  • D3?-reversal indices are computed on the clean word; negatives are not off by one, and positive ?n starts at char n.
  • D4 — positive-index affix insertion/deletion is not off by one.
  • D5 — circumfix A#B wraps the word (the JS concatenated A+B).
  • D6 — local conditions inside an affix rule are cleanly false.
  • D14 — category→category mapping pairs members by declaration order, not a longest-first sort, so digraph categories map correctly (e.g. palatalization [H]/[J] where [J] holds tʃ, dʒ: declared order gives s→ʃ, t→tʃ, g→ɣ). The JS sorted members longest-first, silently mapping s→tʃ. Longest-first is still used for matching, so multi-character phones still match greedily.
  • D15% ("same as the target") substitutes on every occurrence in a result, so %% doubles the target (e.g. gemination [C]/%%/_4). The JS replaced only the first %. A literal per-cent sign in output is written !%.

Divergences from CWS (documented quirks fixed, not reproduced):

  • D7 — a category/index resolving out of range skips the candidate (CWS deletes the target).
  • D8! escape makes the next character a literal.
  • D9 / D10 — movement > and copy >! use clean semantics; CWS's off-by-one is not reproduced.
  • D11- word operations are opt-in (opts.dashToSpace).
  • D12 — the wildcard * in local conditions is defined and reliable.
  • D13 — a UTF-8 codepoint layer replaces the JS's UTF-16 indexing: combining marks are separate characters, and digraph phones work in categories (CWS bans them).

Kept behaviours: @0/@-0 select nothing; an empty ELS is skip, not delete; only the first % in a result is substituted; capitals never survive to output; candidates are collected against the pre-rule word and applied together (no intra-rule bleeding).

Performance

Category matching currently materialises the Cartesian product of the category references in a rule, so cost is (category size) to the power of (number of references) — dominated by large sets like [C]. A guard (opts.explodeCap, default 5,000,000) skips a pathological rule with a warning rather than exceeding Scribunto's CPU limit. Tracked follow-up: a streaming/positional matcher that tests membership per word-position, removing the ceiling entirely.