data(symbol): a prefix owns its slots only through its own row

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.
This commit is contained in:
2026-08-18 09:26:46 +09:00
parent a351f50308
commit 5abd885e11
3 changed files with 30 additions and 28 deletions

View File

@@ -8,6 +8,7 @@
:( ☹ :( ☹
:) ☺ :) ☺
<- ← <- ←
< ← ≤ ♥ ≠
<= ≤ <= ≤
<3 ♥ 🫰 🫶 <3 ♥ 🫰 🫶
<> ≠ <> ≠
@@ -16,6 +17,7 @@
=> ⇒ => ⇒
>= ≥ >= ≥
^1 ¹ ^1 ¹
^ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹
^2 ² ^2 ²
^3 ³ ^3 ³
^4 ⁴ ^4 ⁴
@@ -25,6 +27,7 @@
^8 ⁸ ^8 ⁸
^9 ⁹ ^9 ⁹
_1 ₁ _1 ₁
_ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉
_2 ₂ _2 ₂
_3 ₃ _3 ₃
_4 ₄ _4 ₄

View File

@@ -10,31 +10,31 @@
☺ :) ☺ :)
# Bare 1-9 choose from a prefix search; keep digit aliases in matching slots. # Bare 1-9 choose from a prefix search; keep digit aliases in matching slots.
← <- ← <- <
≤ <= ≤ <= <
♥ <3 ♥ <3 <
≠ <> != ≠ <> != <
≡ == ≡ ==
⇒ => ⇒ =>
≥ >= ≥ >=
¹ ^1 ¹ ^1 ^
² ^2 ² ^2 ^
³ ^3 ³ ^3 ^
⁴ ^4 ⁴ ^4 ^
⁵ ^5 ⁵ ^5 ^
⁶ ^6 ⁶ ^6 ^
⁷ ^7 ⁷ ^7 ^
⁸ ^8 ⁸ ^8 ^
⁹ ^9 ⁹ ^9 ^
₁ _1 ₁ _1 _
₂ _2 ₂ _2 _
₃ _3 ₃ _3 _
₄ _4 ₄ _4 _
₅ _5 ₅ _5 _
₆ _6 ₆ _6 _
₇ _7 ₇ _7 _
₈ _8 ₈ _8 _
₉ _9 ₉ _9 _
⁽ ^( ⁽ ^(
⁾ ^) ⁾ ^)

View File

@@ -35,12 +35,11 @@ class MkemojiTest(unittest.TestCase):
result = generate() result = generate()
self.assertEqual(result.returncode, 0, result.stderr) self.assertEqual(result.returncode, 0, result.stderr)
data = table(result.stdout) data = table(result.stdout)
keys = list(data) # The engine walks a prefix's children in rune order, so only the
self.assertEqual([k for k in keys if k[:1] == "^"][:9], # prefix's own row decides which candidate a digit picks.
[f"^{d}" for d in range(1, 10)]) self.assertEqual(data["^"].split(), list("¹²³⁴⁵⁶⁷⁸⁹"))
self.assertEqual([k for k in keys if k[:1] == "_"][:9], self.assertEqual(data["_"].split(), list("₁₂₃₄₅₆₇₈₉"))
[f"_{d}" for d in range(1, 10)]) self.assertEqual(data["<"].split()[2], "")
self.assertEqual([k for k in keys if k[:1] == "<"][2], "<3")
self.assertEqual(data["^1"], "¹") self.assertEqual(data["^1"], "¹")
self.assertEqual(data["_2"], "") self.assertEqual(data["_2"], "")
self.assertEqual(data["<3"].split()[0], "") self.assertEqual(data["<3"].split()[0], "")