@kensio/pinyinjs 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +211 -5
  2. package/data/core.entries +397 -396
  3. package/data/core.freq +0 -0
  4. package/data/core.keys +1 -0
  5. package/data/full.entries +9717 -9717
  6. package/data/manifest.json +12 -12
  7. package/data/script.map +7 -7
  8. package/data/standard.entries +967 -966
  9. package/data/standard.freq +0 -0
  10. package/data/standard.keys +1 -0
  11. package/dist/cli/arguments.d.ts +38 -0
  12. package/dist/cli/arguments.d.ts.map +1 -1
  13. package/dist/cli/arguments.js +46 -0
  14. package/dist/cli/arguments.js.map +1 -1
  15. package/dist/cli/commands.d.ts.map +1 -1
  16. package/dist/cli/commands.js +207 -1
  17. package/dist/cli/commands.js.map +1 -1
  18. package/dist/cli/help.d.ts.map +1 -1
  19. package/dist/cli/help.js +5 -0
  20. package/dist/cli/help.js.map +1 -1
  21. package/dist/decode/convert.d.ts +16 -0
  22. package/dist/decode/convert.d.ts.map +1 -1
  23. package/dist/decode/convert.js +3 -28
  24. package/dist/decode/convert.js.map +1 -1
  25. package/dist/decode/runs.d.ts +11 -0
  26. package/dist/decode/runs.d.ts.map +1 -1
  27. package/dist/decode/runs.js +19 -0
  28. package/dist/decode/runs.js.map +1 -1
  29. package/dist/decode/segment.d.ts +66 -0
  30. package/dist/decode/segment.d.ts.map +1 -0
  31. package/dist/decode/segment.js +79 -0
  32. package/dist/decode/segment.js.map +1 -0
  33. package/dist/dictionary/artifact.d.ts +10 -0
  34. package/dist/dictionary/artifact.d.ts.map +1 -1
  35. package/dist/dictionary/artifact.js +17 -0
  36. package/dist/dictionary/artifact.js.map +1 -1
  37. package/dist/dictionary/corpus-mass.d.ts +68 -0
  38. package/dist/dictionary/corpus-mass.d.ts.map +1 -0
  39. package/dist/dictionary/corpus-mass.js +123 -0
  40. package/dist/dictionary/corpus-mass.js.map +1 -0
  41. package/dist/dictionary/dictionary.d.ts +56 -0
  42. package/dist/dictionary/dictionary.d.ts.map +1 -1
  43. package/dist/dictionary/dictionary.js +116 -1
  44. package/dist/dictionary/dictionary.js.map +1 -1
  45. package/dist/dictionary/erhua.d.ts +4 -0
  46. package/dist/dictionary/erhua.d.ts.map +1 -1
  47. package/dist/dictionary/erhua.js +6 -0
  48. package/dist/dictionary/erhua.js.map +1 -1
  49. package/dist/dictionary/merge.d.ts.map +1 -1
  50. package/dist/dictionary/merge.js +16 -8
  51. package/dist/dictionary/merge.js.map +1 -1
  52. package/dist/dictionary/tiers.d.ts +2 -1
  53. package/dist/dictionary/tiers.d.ts.map +1 -1
  54. package/dist/dictionary/tiers.js +77 -11
  55. package/dist/dictionary/tiers.js.map +1 -1
  56. package/dist/grade/check.d.ts +156 -0
  57. package/dist/grade/check.d.ts.map +1 -0
  58. package/dist/grade/check.js +356 -0
  59. package/dist/grade/check.js.map +1 -0
  60. package/dist/index.d.ts +11 -1
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +5 -0
  63. package/dist/index.js.map +1 -1
  64. package/dist/search/candidates.d.ts +84 -0
  65. package/dist/search/candidates.d.ts.map +1 -0
  66. package/dist/search/candidates.js +290 -0
  67. package/dist/search/candidates.js.map +1 -0
  68. package/dist/search/match.d.ts +72 -0
  69. package/dist/search/match.d.ts.map +1 -0
  70. package/dist/search/match.js +419 -0
  71. package/dist/search/match.js.map +1 -0
  72. package/dist/search/query.d.ts +49 -0
  73. package/dist/search/query.d.ts.map +1 -0
  74. package/dist/search/query.js +115 -0
  75. package/dist/search/query.js.map +1 -0
  76. package/dist/search/reverse-index.d.ts +141 -0
  77. package/dist/search/reverse-index.d.ts.map +1 -0
  78. package/dist/search/reverse-index.js +356 -0
  79. package/dist/search/reverse-index.js.map +1 -0
  80. package/dist/sources/unihan.d.ts +9 -0
  81. package/dist/sources/unihan.d.ts.map +1 -1
  82. package/dist/sources/unihan.js +12 -1
  83. package/dist/sources/unihan.js.map +1 -1
  84. package/package.json +2 -3
  85. package/CHANGELOG.md +0 -634
@@ -1,4 +1,5 @@
1
1
  import { isSingleCharacter, toCharacters } from "../script/characters.js";
2
+ import { traditionalForms } from "./artifact.js";
2
3
  /**
3
4
  * The dictionary tiers, smallest first and each contained in the next.
4
5
  *
@@ -65,6 +66,71 @@ function charactersInUse(entries) {
65
66
  }
66
67
  return inUse;
67
68
  }
69
+ /**
70
+ * The characters a tier holds: the ones in use, and the ones it has to answer.
71
+ *
72
+ * "In use" alone is not enough, because a tier is keyed by more than the
73
+ * headwords it holds — an entry claims every 繁體 spelling attested for it, and
74
+ * some of those spellings are rare characters with entries of their own. A tier
75
+ * holding 卒 is keyed for 䘚 whether or not it holds 䘚, so without the
76
+ * character's own entry it answers that key from the 卒 that reached it
77
+ * sideways and reads `zú`, where `full` lets 䘚's own entry win the key and
78
+ * reads `zhú`. Neither reading is wrong — 䘚 written for 卒 really is `zú` —
79
+ * but which one you get must not depend on how much of the dictionary has
80
+ * loaded, or the documented upgrade path contradicts itself mid-paragraph.
81
+ *
82
+ * Admitting the entry is all it takes: the artifact's `claimKeys` puts
83
+ * headwords ahead of 繁體 aliases in every tier, so once 䘚 is present it wins
84
+ * its own key exactly as it does in `full`.
85
+ *
86
+ * It repeats to a fixpoint because an admitted character claims spellings of
87
+ * its own, and one of those can be another character with an entry behind it.
88
+ * On the current sources it settles at once — 242 characters admitted, then a
89
+ * confirming pass that admits none — but nothing in the merge rules a chain
90
+ * out, and the pass is a set lookup per entry against a build that reads four
91
+ * source files.
92
+ */
93
+ function charactersHeld(entries, words) {
94
+ const headed = new Set();
95
+ for (const entry of entries) {
96
+ if (isSingleCharacter(entry.hans)) {
97
+ headed.add(entry.hans);
98
+ }
99
+ }
100
+ const inUse = charactersInUse(entries);
101
+ const held = new Set();
102
+ for (const character of headed) {
103
+ if (inUse.has(character)) {
104
+ held.add(character);
105
+ }
106
+ }
107
+ /**
108
+ * Admit the characters this entry claims the key of, reporting any news.
109
+ */
110
+ const admitClaims = (entry) => {
111
+ let admitted = false;
112
+ for (const form of traditionalForms(entry)) {
113
+ if (headed.has(form) && !held.has(form)) {
114
+ held.add(form);
115
+ admitted = true;
116
+ }
117
+ }
118
+ return admitted;
119
+ };
120
+ for (const entry of words) {
121
+ admitClaims(entry);
122
+ }
123
+ let growing = true;
124
+ while (growing) {
125
+ growing = false;
126
+ for (const entry of entries) {
127
+ if (isSingleCharacter(entry.hans) && held.has(entry.hans)) {
128
+ growing = admitClaims(entry) || growing;
129
+ }
130
+ }
131
+ }
132
+ return held;
133
+ }
68
134
  /**
69
135
  * The entries a tier contains.
70
136
  *
@@ -72,7 +138,8 @@ function charactersInUse(entries) {
72
138
  * makes a dictionary usable at all: they cover roughly half of running text on
73
139
  * their own, and they are the fallback for every position no word matches.
74
140
  * Tiering cuts the phrase tail and the unused characters, never a character
75
- * that some word is written with.
141
+ * that some word is written with — and never one the tier is keyed for anyway,
142
+ * which is what {@link charactersHeld} adds on top.
76
143
  *
77
144
  * `full` holds everything, including the characters no source uses in a word.
78
145
  * They cost little there and are the difference between reading a rare
@@ -82,15 +149,14 @@ export function selectTier(entries, tier) {
82
149
  if (tier === "full") {
83
150
  return entries;
84
151
  }
85
- const inUse = charactersInUse(entries);
86
- const characters = entries.filter((entry) => isSingleCharacter(entry.hans) && inUse.has(entry.hans));
87
- if (tier === "core") {
88
- return characters;
89
- }
90
- const words = entries
91
- .filter((entry) => !isSingleCharacter(entry.hans))
92
- .toSorted(byFrequency)
93
- .slice(0, STANDARD_TIER_WORDS);
94
- return [...characters, ...words];
152
+ const words = tier === "core"
153
+ ? []
154
+ : entries
155
+ .filter((entry) => !isSingleCharacter(entry.hans))
156
+ .toSorted(byFrequency)
157
+ .slice(0, STANDARD_TIER_WORDS);
158
+ const held = charactersHeld(entries, words);
159
+ const characters = entries.filter((entry) => isSingleCharacter(entry.hans) && held.has(entry.hans));
160
+ return tier === "core" ? characters : [...characters, ...words];
95
161
  }
96
162
  //# sourceMappingURL=tiers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/dictionary/tiers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAG1E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAU,CAAC;AAO3D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAqB,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAE1C;;GAEG;AACH,SAAS,WAAW,CAAC,IAAqB,EAAE,KAAsB;IAChE,IAAI,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,eAAe,CACtB,OAAmC;IAEnC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;gBACxB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxB,CAAC;YACD,SAAS;QACX,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,KAAK,MAAM,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3C,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CACxB,OAAmC,EACnC,IAAU;IAEV,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAC/B,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAClE,CAAC;IACF,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO;SAClB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;SACjD,QAAQ,CAAC,WAAW,CAAC;SACrB,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC;IACjC,OAAO,CAAC,GAAG,UAAU,EAAE,GAAG,KAAK,CAAC,CAAC;AACnC,CAAC"}
1
+ {"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/dictionary/tiers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ import { type ConvertOptions } from "../decode/convert.js";
2
+ import type { Dictionary } from "../dictionary/dictionary.js";
3
+ import { type Syllable } from "../syllable/syllable.js";
4
+ /**
5
+ * What one syllable of a typed transcription turned out to be.
6
+ *
7
+ * `toneless` is deliberately separate from `tone`: {@link Syllable.tone} says
8
+ * whether a tone was written at all, so "you left the tones off" is a different
9
+ * thing to report than "you wrote the wrong one", and only the second is a
10
+ * mistake about the language. {@link CheckOptions.tones} decides which of them
11
+ * counts against the score.
12
+ */
13
+ export type PinyinVerdict = "correct" | "toneless" | "tone" | "wrong" | "missing" | "extra";
14
+ /**
15
+ * Where a typed syllable falls against the word boundaries the reading has.
16
+ *
17
+ * `split` is a word written as two — `yín háng` for 银行 — and `joined` is two
18
+ * words run into one. Both are reported whatever {@link CheckOptions.spacing}
19
+ * says, so an application can show them without having to grade them.
20
+ */
21
+ export type SpacingVerdict = "correct" | "split" | "joined";
22
+ /**
23
+ * Whether one dimension of the writing has to be right for a perfect score.
24
+ *
25
+ * `optional` still reports the verdict and counts the syllable correct anyway;
26
+ * `required` counts it wrong. Both dimensions this applies to — tones and word
27
+ * spacing — are things an exercise may or may not be teaching yet.
28
+ */
29
+ export type CheckRequirement = "optional" | "required";
30
+ /**
31
+ * How a transcription should be checked.
32
+ *
33
+ * Every {@link ConvertOptions} field is accepted and passed to the conversion
34
+ * the answer is read from, which is what `readings` is worth having here for:
35
+ * an exercise knows which sense of 长 its own sentence uses, where the decoder
36
+ * can only weigh the evidence.
37
+ */
38
+ export interface CheckOptions extends ConvertOptions {
39
+ /** Whether a tone has to be written. Defaults to `optional`. */
40
+ readonly tones?: CheckRequirement;
41
+ /**
42
+ * Whether the word spacing has to be right. Defaults to `optional`.
43
+ *
44
+ * Off by default because the 分词连写 this grades against is not a complete
45
+ * 正词法 implementation — see [orthography](../../docs/orthography/) for
46
+ * where it stops — so a learner can write a word the standard writes
47
+ * differently from the way this does. On for an exercise teaching the
48
+ * spacing, where getting it right is the point.
49
+ */
50
+ readonly spacing?: CheckRequirement;
51
+ }
52
+ /**
53
+ * One syllable of the check: what was expected there, and what was typed.
54
+ */
55
+ export interface CheckedSyllable {
56
+ readonly verdict: PinyinVerdict;
57
+ /**
58
+ * Where this syllable falls against the word boundaries, where both a reading
59
+ * and a typed syllable met here.
60
+ *
61
+ * A separate axis from {@link CheckedSyllable.verdict}, because it is a
62
+ * separate mistake: `yín háng` reads 银行 perfectly and writes it as two
63
+ * words. Undefined for a syllable that was only expected or only typed.
64
+ */
65
+ readonly spacing: SpacingVerdict | undefined;
66
+ /** Whether this counts as right in {@link PinyinCheck.score}. */
67
+ readonly isCorrect: boolean;
68
+ /** The reading expected here, or undefined where nothing was expected. */
69
+ readonly expected: Syllable | undefined;
70
+ /**
71
+ * What was typed here, parsed, or undefined where nothing was typed.
72
+ *
73
+ * Also undefined for something typed that is not a syllable at all, which
74
+ * {@link CheckedSyllable.text} still reports as written.
75
+ */
76
+ readonly actual: Syllable | undefined;
77
+ /** What was typed here, exactly as written, or empty where nothing was. */
78
+ readonly text: string;
79
+ /**
80
+ * The characters the expected syllable reads.
81
+ *
82
+ * Undefined where a syllable reads on into the characters named before it,
83
+ * exactly as {@link ConvertedPiece.source} is, and where nothing was
84
+ * expected at all.
85
+ */
86
+ readonly source: string | undefined;
87
+ /**
88
+ * Where those characters start, in code points from the start of the text.
89
+ *
90
+ * What highlighting the mistake in the text itself needs, and counted the way
91
+ * {@link import("../decode/segment.js").Segment.at} counts.
92
+ */
93
+ readonly at: number | undefined;
94
+ }
95
+ /**
96
+ * A typed transcription marked against the text it was written for.
97
+ */
98
+ export interface PinyinCheck {
99
+ /** One entry per syllable expected or typed, in order. */
100
+ readonly syllables: readonly CheckedSyllable[];
101
+ /** Whether every syllable counted as correct. */
102
+ readonly isCorrect: boolean;
103
+ /**
104
+ * The share of the syllables that counted as correct, from 0 to 1.
105
+ *
106
+ * Over the entries reported rather than over the expected reading, so that
107
+ * inventing a syllable costs as much as dropping one: an answer with an extra
108
+ * syllable in it has more entries than the reading has syllables.
109
+ */
110
+ readonly score: number;
111
+ /** The reading that was expected, as the conversion writes it. */
112
+ readonly reading: readonly Syllable[];
113
+ }
114
+ /**
115
+ * Mark a typed pinyin transcription against the text it was written for.
116
+ *
117
+ * ```ts
118
+ * check(dictionary, "北京", "bei3jing3").syllables.map((one) => one.verdict);
119
+ * // ["correct", "tone"]
120
+ * ```
121
+ *
122
+ * **The point of doing this with a dictionary is being fair in the ways a
123
+ * string comparison cannot be**, and every one of these is a learner being
124
+ * marked wrong for being right:
125
+ *
126
+ * - **Either notation, mixed freely.** `běi` and `bei3` are the same syllable
127
+ * and both parse, so `bei3jīng` is not a spelling mistake.
128
+ * - **A reading the decoder itself was unsure of.** Where another reading of
129
+ * the same character was there for the taking, it is accepted — see
130
+ * {@link rejectedReadings}. 银行 is not one of those: the word settles both
131
+ * syllables, so `yínxíng` really is wrong.
132
+ * - **Sandhi either way.** 你好 is written `nǐ hǎo` and said `ní hǎo`, and 不是
133
+ * is `bú shì` written and `bù shì` underneath. Both pass.
134
+ * - **Tones written or not.** A syllable typed with no tone is `toneless`
135
+ * rather than `tone`, and {@link CheckOptions.tones} says whether that counts
136
+ * against the score. A neutral tone typed without a mark is simply correct,
137
+ * because that is how pinyin writes it.
138
+ * - **Apostrophes.** Not a word boundary and not a sound: `nǐhǎo`, `nǐ hǎo` and
139
+ * `nǐ'hǎo` all read the same, and check the same.
140
+ *
141
+ * Word spacing is graded on its own axis, reported as
142
+ * {@link CheckedSyllable.spacing} and counted only where
143
+ * {@link CheckOptions.spacing} asks for it — and tolerant in the same spirit,
144
+ * since 分词连写 and the words the dictionary knows are two conventions this
145
+ * package writes and a learner may have been taught either.
146
+ *
147
+ * What comes back is one {@link CheckedSyllable} per syllable expected or
148
+ * typed, in order, each naming the characters it reads and where they are, so
149
+ * that a mistake can be shown against the text rather than against the answer.
150
+ *
151
+ * This is {@link import("../accuracy/score.js").scoreCase} — the scorer that
152
+ * marks the decoder against the gold corpus — pointed at a person instead, and
153
+ * it costs three conversions of the text.
154
+ */
155
+ export declare function check(dictionary: Dictionary, text: string, typed: string, options?: CheckOptions): PinyinCheck;
156
+ //# sourceMappingURL=check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/grade/check.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,yBAAyB,CAAC;AAGjC;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,UAAU,GACV,MAAM,GACN,OAAO,GACP,SAAS,GACT,OAAO,CAAC;AAEZ;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,UAAU,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,gEAAgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC;IAClC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,SAAS,CAAC;IAC7C,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IACtC,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;CACvC;AAqaD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,KAAK,CACnB,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,YAAiB,GACzB,WAAW,CAuBb"}
@@ -0,0 +1,356 @@
1
+ import { alignSequences } from "../accuracy/align.js";
2
+ import { tokenisePinyin } from "../accuracy/tokenise.js";
3
+ import { convertPieces, convertPiecesUnscored, } from "../decode/convert.js";
4
+ import { READING_CHARGE } from "../decode/lattice.js";
5
+ import { toCharacters } from "../script/characters.js";
6
+ import { readSyllable, writeSyllableSpelling, } from "../syllable/syllable.js";
7
+ import { NEUTRAL_TONE } from "../tone/tone.js";
8
+ /**
9
+ * The other corner of the sandhi square.
10
+ *
11
+ * A learner may write the underlying tones or the tones actually said, and both
12
+ * are right — 你好 is written `nǐ hǎo` and said `ní hǎo`. Rather than convert
13
+ * four times for the four combinations of {@link SandhiOptions}, this converts
14
+ * twice and inverts both switches at once, which reaches every tone: 一 and 不
15
+ * sandhi applies to syllables in the first and fourth tones, third-tone sandhi
16
+ * only to syllables in the third, so the two passes never touch the same
17
+ * syllable and each one's two forms both appear across the two corners.
18
+ */
19
+ function oppositeSandhi(sandhi) {
20
+ return {
21
+ yiBu: !(sandhi?.yiBu ?? true),
22
+ thirdTone: !(sandhi?.thirdTone ?? false),
23
+ };
24
+ }
25
+ /**
26
+ * Where each piece's source characters start, in code points.
27
+ *
28
+ * Read off the sources rather than tracked through the conversion, which works
29
+ * because every character of the text is named by exactly one piece — a piece
30
+ * naming none is either pinyin orthography the source has no trace of, such as
31
+ * the space between two words, or a syllable reading on into the characters the
32
+ * piece before it named.
33
+ */
34
+ function sourcePositions(pieces) {
35
+ const positions = [];
36
+ let at = 0;
37
+ for (const piece of pieces) {
38
+ positions.push(at);
39
+ at += toCharacters(piece.source ?? "").length;
40
+ }
41
+ return positions;
42
+ }
43
+ /**
44
+ * What a piece written between two syllables makes of the join.
45
+ *
46
+ * Only a hyphen written where the syllables would otherwise have run together
47
+ * is 分词连写's internal boundary; anything else — a space, punctuation, a
48
+ * stretch that was never Han — separates two words.
49
+ */
50
+ function junctionOver(text, held) {
51
+ return text === "-" && held === "join" ? "hyphen" : "break";
52
+ }
53
+ /**
54
+ * The syllables a conversion writes, with their positions and what joins them.
55
+ *
56
+ * A conversion writes its syllables as adjacent pieces within a word — the
57
+ * 隔音符号 is part of a syllable's own spelling rather than a piece of its own —
58
+ * so anything standing between two of them is a boundary, and the hyphen of
59
+ * 分词连写 is the one that is not a word boundary.
60
+ */
61
+ function readingSyllables(pieces) {
62
+ const positions = sourcePositions(pieces);
63
+ const syllables = [];
64
+ // The first syllable of a text begins a word, whatever comes before it.
65
+ let junction = "break";
66
+ for (const [at, piece] of pieces.entries()) {
67
+ if (piece.syllable === undefined) {
68
+ junction = junctionOver(piece.text, junction);
69
+ continue;
70
+ }
71
+ syllables.push({
72
+ piece: { ...piece, syllable: piece.syllable },
73
+ /* c8 ignore next -- one position is recorded for each piece there is */
74
+ at: positions[at] ?? 0,
75
+ junction,
76
+ });
77
+ junction = "join";
78
+ }
79
+ return syllables;
80
+ }
81
+ /**
82
+ * The readings the decode turned down without much reason to.
83
+ *
84
+ * The decode charges {@link READING_CHARGE} for a word boundary, so a rejected
85
+ * reading costing less than that was available without breaking any dictionary
86
+ * word apart — see {@link import("../decode/confidence.js").isUncertain}, which
87
+ * is the same threshold read as a question rather than as a filter. The library
88
+ * knows it was guessing there, so a learner who guessed the other way is not
89
+ * wrong.
90
+ *
91
+ * Only a rejected reading of exactly the one character this syllable reads is
92
+ * taken. An alternative can cover a different stretch — 玩儿 read as `wánr`
93
+ * competes with 玩 `wán` and 儿 `ér` — and a claim about other characters says
94
+ * nothing about what belongs in this position.
95
+ */
96
+ function rejectedReadings(piece) {
97
+ const { confidence, source } = piece;
98
+ if (source === undefined || toCharacters(source).length !== 1) {
99
+ return [];
100
+ }
101
+ return (confidence?.alternatives ?? []).flatMap((alternative) => {
102
+ const [syllable] = alternative.reading;
103
+ return alternative.cost < READING_CHARGE &&
104
+ alternative.to - alternative.from === 1 &&
105
+ alternative.reading.length === 1 &&
106
+ syllable !== undefined
107
+ ? [syllable]
108
+ : [];
109
+ });
110
+ }
111
+ /**
112
+ * Gather readings into the spellings and tones they allow.
113
+ *
114
+ * The tone is allowed to be undefined only because {@link Syllable.tone} is: a
115
+ * reading that came out of the dictionary always carries one.
116
+ */
117
+ function acceptedReadings(readings) {
118
+ const accepted = new Map();
119
+ for (const syllable of readings) {
120
+ const base = writeSyllableSpelling(syllable);
121
+ const tones = accepted.get(base) ?? new Set();
122
+ tones.add(syllable.tone);
123
+ accepted.set(base, tones);
124
+ }
125
+ return accepted;
126
+ }
127
+ /**
128
+ * Read the text, and work out what a typed syllable is allowed to be.
129
+ *
130
+ * Three conversions of the same text, and each one answers a different question.
131
+ * The first is the answer itself, with the confidence report beside it. The
132
+ * second is the other corner of the sandhi square, which is where the second
133
+ * form of every tone comes from. The third is the same text written under the
134
+ * other spacing convention, which is where a boundary's tolerance comes from:
135
+ * 分词连写 is what puts 了 on its verb and separates 市 from 南京, and a learner
136
+ * who wrote the words the dictionary knows instead — `tā kàn le`, `Nánjīngshì`
137
+ * — has written one of the two conventions this package implements rather than
138
+ * made a mistake.
139
+ *
140
+ * All three decode identically. Sandhi rewrites tones and 分词连写 moves
141
+ * boundaries; neither touches the syllables underneath, so all three line up one
142
+ * for one.
143
+ */
144
+ function expectedReading(dictionary, text, options) {
145
+ const written = readingSyllables(convertPieces(dictionary, text, options));
146
+ const said = readingSyllables(convertPiecesUnscored(dictionary, text, {
147
+ ...options,
148
+ sandhi: oppositeSandhi(options.sandhi),
149
+ }));
150
+ const spaced = readingSyllables(convertPiecesUnscored(dictionary, text, {
151
+ ...options,
152
+ grouping: !(options.grouping ?? true),
153
+ }));
154
+ return written.map(({ piece, at, junction }, index) => {
155
+ // Sliced rather than indexed, so that a conversion somehow reporting fewer
156
+ // syllables than the others contributes nothing rather than needing a
157
+ // fallback: the caller's own conversion decides alone.
158
+ const alternate = said
159
+ .slice(index, index + 1)
160
+ .map((one) => one.piece.syllable);
161
+ const other = spaced.slice(index, index + 1).map((one) => one.junction);
162
+ return {
163
+ syllable: piece.syllable,
164
+ accepted: acceptedReadings([
165
+ piece.syllable,
166
+ ...alternate,
167
+ ...rejectedReadings(piece),
168
+ ]),
169
+ // A hyphen is neither a break nor a join, and so allows both: it is a
170
+ // boundary the standard writes inside a word, and a learner rendering it
171
+ // as a space has not invented one.
172
+ acceptsBreak: junction !== "join" || other.some((one) => one !== "join"),
173
+ acceptsJoin: junction !== "break" || other.some((one) => one !== "break"),
174
+ source: piece.source,
175
+ at,
176
+ };
177
+ });
178
+ }
179
+ /**
180
+ * Read what was typed, one syllable at a time.
181
+ *
182
+ * {@link tokenisePinyin} is what the decoder is scored with, and it is tolerant
183
+ * in the two ways that matter here: word spacing is discarded, and the
184
+ * apostrophes and hyphens inside a word are read as the syllable boundaries
185
+ * they are and dropped. Both are orthography rather than pronunciation.
186
+ *
187
+ * A stretch that is not pinyin at all comes back whole, and is reported as
188
+ * written rather than thrown away.
189
+ */
190
+ function typedReading(typed) {
191
+ const { syllables, wordStarts } = tokenisePinyin(typed);
192
+ return syllables.map((text, at) => {
193
+ const syllable = readSyllable(text);
194
+ return {
195
+ text,
196
+ syllable,
197
+ base: syllable === undefined
198
+ ? text.toLowerCase().normalize("NFC")
199
+ : writeSyllableSpelling(syllable),
200
+ tone: syllable?.tone,
201
+ startsWord: wordStarts.has(at),
202
+ };
203
+ });
204
+ }
205
+ /**
206
+ * Match the expected syllables up with the typed ones.
207
+ *
208
+ * Aligned on the toneless spellings, for the reason
209
+ * {@link import("../accuracy/score.js").scoreCase} aligns on them: a syllable
210
+ * dropped or invented halfway through would otherwise knock everything after it
211
+ * out of step, and be reported as a mistake on every syllable rather than on
212
+ * the one.
213
+ *
214
+ * What the alignment leaves over is then paired off within each gap, which is
215
+ * what turns two unrelated stretches into substitutions: 银行 typed `yínxíng`
216
+ * anchors on `yín`, leaving one expected `háng` against one typed `xíng` — one
217
+ * wrong syllable rather than one missing and one invented.
218
+ */
219
+ function pairSyllables(expected, actual) {
220
+ const pairs = [];
221
+ let held = 0;
222
+ let typed = 0;
223
+ const gap = (untilExpected, untilActual) => {
224
+ while (held < untilExpected || typed < untilActual) {
225
+ const takesExpected = held < untilExpected;
226
+ const takesActual = typed < untilActual;
227
+ pairs.push({
228
+ expected: takesExpected ? held : undefined,
229
+ actual: takesActual ? typed : undefined,
230
+ });
231
+ held += takesExpected ? 1 : 0;
232
+ typed += takesActual ? 1 : 0;
233
+ }
234
+ };
235
+ for (const anchor of alignSequences(expected, actual)) {
236
+ gap(anchor.expected, anchor.actual);
237
+ pairs.push({ expected: held, actual: typed });
238
+ held++;
239
+ typed++;
240
+ }
241
+ gap(expected.length, actual.length);
242
+ return pairs;
243
+ }
244
+ /**
245
+ * What a typed syllable is, against what was expected in its place.
246
+ *
247
+ * The neutral tone is the one tone pinyin writes with no mark at all, so a
248
+ * syllable expected in it and typed without one has had its tone written
249
+ * correctly rather than left off.
250
+ */
251
+ function verdictFor(expected, actual) {
252
+ const tones = expected.accepted.get(actual.base);
253
+ if (tones === undefined) {
254
+ return "wrong";
255
+ }
256
+ if (actual.tone === undefined) {
257
+ return tones.has(NEUTRAL_TONE) ? "correct" : "toneless";
258
+ }
259
+ return tones.has(actual.tone) ? "correct" : "tone";
260
+ }
261
+ /**
262
+ * Where a typed syllable falls against the word boundaries.
263
+ */
264
+ function spacingFor(expected, actual) {
265
+ if (actual.startsWord) {
266
+ return expected.acceptsBreak ? "correct" : "split";
267
+ }
268
+ return expected.acceptsJoin ? "correct" : "joined";
269
+ }
270
+ /**
271
+ * Mark one pairing.
272
+ *
273
+ * The two axes are graded separately and reported separately, and a syllable
274
+ * only counts as right where both of them are: `yín háng` reads 银行 perfectly
275
+ * and writes it as two words, which is one mistake rather than none.
276
+ */
277
+ function checkPair(expected, actual, required) {
278
+ const verdict = expected === undefined
279
+ ? "extra"
280
+ : actual === undefined
281
+ ? "missing"
282
+ : verdictFor(expected, actual);
283
+ const spacing = expected === undefined || actual === undefined
284
+ ? undefined
285
+ : spacingFor(expected, actual);
286
+ const readRight = verdict === "correct" ||
287
+ (verdict === "toneless" && required.tones === "optional");
288
+ const spacedRight = spacing !== "split" && spacing !== "joined"
289
+ ? true
290
+ : required.spacing === "optional";
291
+ return {
292
+ verdict,
293
+ spacing,
294
+ isCorrect: readRight && spacedRight,
295
+ expected: expected?.syllable,
296
+ actual: actual?.syllable,
297
+ text: actual?.text ?? "",
298
+ source: expected?.source,
299
+ at: expected?.at,
300
+ };
301
+ }
302
+ /**
303
+ * Mark a typed pinyin transcription against the text it was written for.
304
+ *
305
+ * ```ts
306
+ * check(dictionary, "北京", "bei3jing3").syllables.map((one) => one.verdict);
307
+ * // ["correct", "tone"]
308
+ * ```
309
+ *
310
+ * **The point of doing this with a dictionary is being fair in the ways a
311
+ * string comparison cannot be**, and every one of these is a learner being
312
+ * marked wrong for being right:
313
+ *
314
+ * - **Either notation, mixed freely.** `běi` and `bei3` are the same syllable
315
+ * and both parse, so `bei3jīng` is not a spelling mistake.
316
+ * - **A reading the decoder itself was unsure of.** Where another reading of
317
+ * the same character was there for the taking, it is accepted — see
318
+ * {@link rejectedReadings}. 银行 is not one of those: the word settles both
319
+ * syllables, so `yínxíng` really is wrong.
320
+ * - **Sandhi either way.** 你好 is written `nǐ hǎo` and said `ní hǎo`, and 不是
321
+ * is `bú shì` written and `bù shì` underneath. Both pass.
322
+ * - **Tones written or not.** A syllable typed with no tone is `toneless`
323
+ * rather than `tone`, and {@link CheckOptions.tones} says whether that counts
324
+ * against the score. A neutral tone typed without a mark is simply correct,
325
+ * because that is how pinyin writes it.
326
+ * - **Apostrophes.** Not a word boundary and not a sound: `nǐhǎo`, `nǐ hǎo` and
327
+ * `nǐ'hǎo` all read the same, and check the same.
328
+ *
329
+ * Word spacing is graded on its own axis, reported as
330
+ * {@link CheckedSyllable.spacing} and counted only where
331
+ * {@link CheckOptions.spacing} asks for it — and tolerant in the same spirit,
332
+ * since 分词连写 and the words the dictionary knows are two conventions this
333
+ * package writes and a learner may have been taught either.
334
+ *
335
+ * What comes back is one {@link CheckedSyllable} per syllable expected or
336
+ * typed, in order, each naming the characters it reads and where they are, so
337
+ * that a mistake can be shown against the text rather than against the answer.
338
+ *
339
+ * This is {@link import("../accuracy/score.js").scoreCase} — the scorer that
340
+ * marks the decoder against the gold corpus — pointed at a person instead, and
341
+ * it costs three conversions of the text.
342
+ */
343
+ export function check(dictionary, text, typed, options = {}) {
344
+ const { tones = "optional", spacing = "optional", ...converting } = options;
345
+ const expected = expectedReading(dictionary, text, converting);
346
+ const actual = typedReading(typed);
347
+ const syllables = pairSyllables(expected.map((one) => writeSyllableSpelling(one.syllable)), actual.map((one) => one.base)).map((pair) => checkPair(pair.expected === undefined ? undefined : expected[pair.expected], pair.actual === undefined ? undefined : actual[pair.actual], { tones, spacing }));
348
+ const correct = syllables.filter((one) => one.isCorrect).length;
349
+ return {
350
+ syllables,
351
+ isCorrect: correct === syllables.length,
352
+ score: syllables.length === 0 ? 1 : correct / syllables.length,
353
+ reading: expected.map((one) => one.syllable),
354
+ };
355
+ }
356
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/grade/check.ts"],"names":[],"mappings":"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"}