From 5abd885e11d515f57801ff48b93c77155cc4c14e Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 18 Aug 2026 09:26:46 +0900 Subject: [PATCH] data(symbol): a prefix owns its slots only through its own row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit map/symbol.src:12 says "Bare 1-9 choose from a prefix search; keep digit aliases in matching slots", map/README:55 repeats it, and both tests/engine_test.c:1001 and tests/mkemoji_test.py were written to it. None of it happened. dictprefix ends in below(), which returns a node's own words first and then its children in rune order (dict.c:29-39), and under `^` the punctuation sorts ahead of the digits: ^ gave ⁽ ⁾ ⁺ ⁻ ⁰ ¹ ² ³ ⁴ ⁵ ... _ gave ₍ ₎ ₊ ₋ ₀ ₁ ₂ ₃ ₄ ₅ ... < gave ← ♥ 🫰 🫶 ≤ ≠ so the file's own aliases picked the wrong character every time: before after ^ then 1 ⁽ ¹ ^ then 2 ⁾ ² _ then 1 ₍ ₁ < then 3 🫰 ♥ mkemoji has no bare `^` row to emit, because no source row claims `^` as an alias -- the prefix exists in the trie only as the parent of `^1`..`^9` and `^(`..`^n`, and a parent has no words of its own. Giving the nine superscripts `^` as a second alias, the nine subscripts `_`, and ←≤♥≠ `<`, makes build() group them in source order and emit three rows: < ← ≤ ♥ ≠ ^ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ _ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ which is what engine_test.c:1001's fixture has said all along, and the first thing below() now returns. Nothing is lost: `^0`, `^(`, `^i` and their kind still answer their own key, `0` is not a selection key so it still extends the query, and addkouho drops the duplicate when a child repeats what the parent already offered. Three rows on 18953. tests/mkemoji_test.py was asking the wrong question. It checked that the first nine `^` keys in the file are `^1`..`^9` -- true before and after, and decided nothing, because the file's order is not the trie's. It now checks the row the engine actually reads, and fails without this change with a bare KeyError on `^`. What this does not cover: `+1` and `-1`. They are not slot aliases, they are words -- plus one, minus one -- and ➕ and ➖ are the right first answers to `+` and `-`. 👍 is slot 3 of `+` and reachable there. --- map/emoji.dict | 3 +++ map/symbol.src | 44 +++++++++++++++++++++---------------------- tests/mkemoji_test.py | 11 +++++------ 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/map/emoji.dict b/map/emoji.dict index 4072797..ffc5a65 100644 --- a/map/emoji.dict +++ b/map/emoji.dict @@ -8,6 +8,7 @@ :( ☹ :) ☺ <- ← +< ← ≤ ♥ ≠ <= ≤ <3 ♥ 🫰 🫶 <> ≠ @@ -16,6 +17,7 @@ => ⇒ >= ≥ ^1 ¹ +^ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ^2 ² ^3 ³ ^4 ⁴ @@ -25,6 +27,7 @@ ^8 ⁸ ^9 ⁹ _1 ₁ +_ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ _2 ₂ _3 ₃ _4 ₄ diff --git a/map/symbol.src b/map/symbol.src index 841cf29..835c9c9 100644 --- a/map/symbol.src +++ b/map/symbol.src @@ -10,31 +10,31 @@ ☺ :) # Bare 1-9 choose from a prefix search; keep digit aliases in matching slots. -← <- -≤ <= -♥ <3 -≠ <> != +← <- < +≤ <= < +♥ <3 < +≠ <> != < ≡ == ⇒ => ≥ >= -¹ ^1 -² ^2 -³ ^3 -⁴ ^4 -⁵ ^5 -⁶ ^6 -⁷ ^7 -⁸ ^8 -⁹ ^9 -₁ _1 -₂ _2 -₃ _3 -₄ _4 -₅ _5 -₆ _6 -₇ _7 -₈ _8 -₉ _9 +¹ ^1 ^ +² ^2 ^ +³ ^3 ^ +⁴ ^4 ^ +⁵ ^5 ^ +⁶ ^6 ^ +⁷ ^7 ^ +⁸ ^8 ^ +⁹ ^9 ^ +₁ _1 _ +₂ _2 _ +₃ _3 _ +₄ _4 _ +₅ _5 _ +₆ _6 _ +₇ _7 _ +₈ _8 _ +₉ _9 _ ⁽ ^( ⁾ ^) diff --git a/tests/mkemoji_test.py b/tests/mkemoji_test.py index 54cb8b8..ac1c465 100644 --- a/tests/mkemoji_test.py +++ b/tests/mkemoji_test.py @@ -35,12 +35,11 @@ class MkemojiTest(unittest.TestCase): result = generate() self.assertEqual(result.returncode, 0, result.stderr) data = table(result.stdout) - keys = list(data) - self.assertEqual([k for k in keys if k[:1] == "^"][:9], - [f"^{d}" for d in range(1, 10)]) - self.assertEqual([k for k in keys if k[:1] == "_"][:9], - [f"_{d}" for d in range(1, 10)]) - self.assertEqual([k for k in keys if k[:1] == "<"][2], "<3") + # The engine walks a prefix's children in rune order, so only the + # prefix's own row decides which candidate a digit picks. + self.assertEqual(data["^"].split(), list("¹²³⁴⁵⁶⁷⁸⁹")) + self.assertEqual(data["_"].split(), list("₁₂₃₄₅₆₇₈₉")) + self.assertEqual(data["<"].split()[2], "♥") self.assertEqual(data["^1"], "¹") self.assertEqual(data["_2"], "₂") self.assertEqual(data["<3"].split()[0], "♥")