chaffjs 0.32.0 → 0.34.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 (535) hide show
  1. package/README.md +7 -0
  2. package/dist/api.d.ts +7 -1
  3. package/dist/api.d.ts.map +1 -1
  4. package/dist/api.js.map +1 -1
  5. package/dist/cli-text.d.ts.map +1 -1
  6. package/dist/cli-text.js +6 -4
  7. package/dist/cli-text.js.map +1 -1
  8. package/dist/commands/grade.d.ts.map +1 -1
  9. package/dist/commands/grade.js +66 -21
  10. package/dist/commands/grade.js.map +1 -1
  11. package/dist/company-kana-spelling.d.ts +6 -1
  12. package/dist/company-kana-spelling.d.ts.map +1 -1
  13. package/dist/company-kana-spelling.js +19 -2
  14. package/dist/company-kana-spelling.js.map +1 -1
  15. package/dist/company-name-ending.d.ts +7 -0
  16. package/dist/company-name-ending.d.ts.map +1 -0
  17. package/dist/company-name-ending.js +44 -0
  18. package/dist/company-name-ending.js.map +1 -0
  19. package/dist/company-names.d.ts +11 -3
  20. package/dist/company-names.d.ts.map +1 -1
  21. package/dist/company-names.js +27 -14
  22. package/dist/company-names.js.map +1 -1
  23. package/dist/cross-run.d.ts.map +1 -1
  24. package/dist/cross-run.js +1 -0
  25. package/dist/cross-run.js.map +1 -1
  26. package/dist/custom/no-rewrite.d.ts +3 -0
  27. package/dist/custom/no-rewrite.d.ts.map +1 -0
  28. package/dist/custom/no-rewrite.js +3 -0
  29. package/dist/custom/no-rewrite.js.map +1 -0
  30. package/dist/custom/parse.d.ts.map +1 -1
  31. package/dist/custom/parse.js +14 -2
  32. package/dist/custom/parse.js.map +1 -1
  33. package/dist/detectors/age-words.d.ts +5 -0
  34. package/dist/detectors/age-words.d.ts.map +1 -0
  35. package/dist/detectors/age-words.js +8 -0
  36. package/dist/detectors/age-words.js.map +1 -0
  37. package/dist/detectors/agreement-slip.d.ts.map +1 -1
  38. package/dist/detectors/agreement-slip.js +4 -0
  39. package/dist/detectors/agreement-slip.js.map +1 -1
  40. package/dist/detectors/annual-pay-mismatch.d.ts.map +1 -1
  41. package/dist/detectors/annual-pay-mismatch.js +2 -12
  42. package/dist/detectors/annual-pay-mismatch.js.map +1 -1
  43. package/dist/detectors/blank-structure.d.ts.map +1 -1
  44. package/dist/detectors/blank-structure.js +4 -3
  45. package/dist/detectors/blank-structure.js.map +1 -1
  46. package/dist/detectors/closing-balance-mismatch.d.ts.map +1 -1
  47. package/dist/detectors/closing-balance-mismatch.js +1 -0
  48. package/dist/detectors/closing-balance-mismatch.js.map +1 -1
  49. package/dist/detectors/code-fences.d.ts +5 -0
  50. package/dist/detectors/code-fences.d.ts.map +1 -1
  51. package/dist/detectors/code-fences.js +9 -0
  52. package/dist/detectors/code-fences.js.map +1 -1
  53. package/dist/detectors/condition-toki.d.ts +16 -0
  54. package/dist/detectors/condition-toki.d.ts.map +1 -0
  55. package/dist/detectors/condition-toki.js +42 -0
  56. package/dist/detectors/condition-toki.js.map +1 -0
  57. package/dist/detectors/conjugated-entry.d.ts +17 -0
  58. package/dist/detectors/conjugated-entry.d.ts.map +1 -0
  59. package/dist/detectors/conjugated-entry.js +50 -0
  60. package/dist/detectors/conjugated-entry.js.map +1 -0
  61. package/dist/detectors/content-morphemes.d.ts +2 -0
  62. package/dist/detectors/content-morphemes.d.ts.map +1 -1
  63. package/dist/detectors/content-morphemes.js +1 -1
  64. package/dist/detectors/content-morphemes.js.map +1 -1
  65. package/dist/detectors/cross-facts.d.ts.map +1 -1
  66. package/dist/detectors/cross-facts.js +2 -1
  67. package/dist/detectors/cross-facts.js.map +1 -1
  68. package/dist/detectors/custom.d.ts.map +1 -1
  69. package/dist/detectors/custom.js +8 -2
  70. package/dist/detectors/custom.js.map +1 -1
  71. package/dist/detectors/document-shape.d.ts.map +1 -1
  72. package/dist/detectors/document-shape.js +6 -0
  73. package/dist/detectors/document-shape.js.map +1 -1
  74. package/dist/detectors/due-date.d.ts.map +1 -1
  75. package/dist/detectors/due-date.js +12 -2
  76. package/dist/detectors/due-date.js.map +1 -1
  77. package/dist/detectors/fact-consistency.d.ts.map +1 -1
  78. package/dist/detectors/fact-consistency.js +12 -13
  79. package/dist/detectors/fact-consistency.js.map +1 -1
  80. package/dist/detectors/form-line.d.ts +9 -0
  81. package/dist/detectors/form-line.d.ts.map +1 -0
  82. package/dist/detectors/form-line.js +36 -0
  83. package/dist/detectors/form-line.js.map +1 -0
  84. package/dist/detectors/formula-amount.d.ts +3 -0
  85. package/dist/detectors/formula-amount.d.ts.map +1 -0
  86. package/dist/detectors/formula-amount.js +31 -0
  87. package/dist/detectors/formula-amount.js.map +1 -0
  88. package/dist/detectors/heading-claim.d.ts +14 -0
  89. package/dist/detectors/heading-claim.d.ts.map +1 -0
  90. package/dist/detectors/heading-claim.js +49 -0
  91. package/dist/detectors/heading-claim.js.map +1 -0
  92. package/dist/detectors/heading-echo.d.ts.map +1 -1
  93. package/dist/detectors/heading-echo.js +29 -1
  94. package/dist/detectors/heading-echo.js.map +1 -1
  95. package/dist/detectors/installment-total-mismatch.d.ts.map +1 -1
  96. package/dist/detectors/installment-total-mismatch.js +28 -19
  97. package/dist/detectors/installment-total-mismatch.js.map +1 -1
  98. package/dist/detectors/list-parallel.d.ts +5 -2
  99. package/dist/detectors/list-parallel.d.ts.map +1 -1
  100. package/dist/detectors/list-parallel.js +45 -7
  101. package/dist/detectors/list-parallel.js.map +1 -1
  102. package/dist/detectors/name-variant.d.ts.map +1 -1
  103. package/dist/detectors/name-variant.js +34 -7
  104. package/dist/detectors/name-variant.js.map +1 -1
  105. package/dist/detectors/number-unit.d.ts +10 -0
  106. package/dist/detectors/number-unit.d.ts.map +1 -1
  107. package/dist/detectors/number-unit.js +28 -8
  108. package/dist/detectors/number-unit.js.map +1 -1
  109. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  110. package/dist/detectors/orthographic-variant.js +4 -3
  111. package/dist/detectors/orthographic-variant.js.map +1 -1
  112. package/dist/detectors/orthography.d.ts.map +1 -1
  113. package/dist/detectors/orthography.js +16 -7
  114. package/dist/detectors/orthography.js.map +1 -1
  115. package/dist/detectors/per-unit-words.d.ts +7 -0
  116. package/dist/detectors/per-unit-words.d.ts.map +1 -0
  117. package/dist/detectors/per-unit-words.js +17 -0
  118. package/dist/detectors/per-unit-words.js.map +1 -0
  119. package/dist/detectors/period-multiple-mismatch.d.ts.map +1 -1
  120. package/dist/detectors/period-multiple-mismatch.js +2 -12
  121. package/dist/detectors/period-multiple-mismatch.js.map +1 -1
  122. package/dist/detectors/reference-number-variant.d.ts.map +1 -1
  123. package/dist/detectors/reference-number-variant.js +21 -3
  124. package/dist/detectors/reference-number-variant.js.map +1 -1
  125. package/dist/detectors/registry/condition-toki.d.ts +3 -0
  126. package/dist/detectors/registry/condition-toki.d.ts.map +1 -0
  127. package/dist/detectors/registry/condition-toki.js +3 -0
  128. package/dist/detectors/registry/condition-toki.js.map +1 -0
  129. package/dist/detectors/registry/formula-amount.d.ts +3 -0
  130. package/dist/detectors/registry/formula-amount.d.ts.map +1 -0
  131. package/dist/detectors/registry/formula-amount.js +3 -0
  132. package/dist/detectors/registry/formula-amount.js.map +1 -0
  133. package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -1
  134. package/dist/detectors/rent-multiple-mismatch.js +2 -12
  135. package/dist/detectors/rent-multiple-mismatch.js.map +1 -1
  136. package/dist/detectors/row-sentence-amounts.d.ts.map +1 -1
  137. package/dist/detectors/row-sentence-amounts.js +5 -10
  138. package/dist/detectors/row-sentence-amounts.js.map +1 -1
  139. package/dist/detectors/stated-amounts.d.ts +4 -0
  140. package/dist/detectors/stated-amounts.d.ts.map +1 -0
  141. package/dist/detectors/stated-amounts.js +12 -0
  142. package/dist/detectors/stated-amounts.js.map +1 -0
  143. package/dist/detectors/step-time-sum.d.ts.map +1 -1
  144. package/dist/detectors/step-time-sum.js +2 -1
  145. package/dist/detectors/step-time-sum.js.map +1 -1
  146. package/dist/detectors/structure-tree.d.ts +1 -1
  147. package/dist/detectors/structure-tree.d.ts.map +1 -1
  148. package/dist/detectors/structure-tree.js +7 -2
  149. package/dist/detectors/structure-tree.js.map +1 -1
  150. package/dist/detectors/team.d.ts.map +1 -1
  151. package/dist/detectors/team.js +10 -6
  152. package/dist/detectors/team.js.map +1 -1
  153. package/dist/detectors/time-order.d.ts.map +1 -1
  154. package/dist/detectors/time-order.js +17 -13
  155. package/dist/detectors/time-order.js.map +1 -1
  156. package/dist/detectors/unknown-word.d.ts +4 -1
  157. package/dist/detectors/unknown-word.d.ts.map +1 -1
  158. package/dist/detectors/unknown-word.js +18 -2
  159. package/dist/detectors/unknown-word.js.map +1 -1
  160. package/dist/detectors/use-interval.d.ts +1 -0
  161. package/dist/detectors/use-interval.d.ts.map +1 -1
  162. package/dist/detectors/use-interval.js +15 -10
  163. package/dist/detectors/use-interval.js.map +1 -1
  164. package/dist/detectors/use-maximum.d.ts.map +1 -1
  165. package/dist/detectors/use-maximum.js +3 -1
  166. package/dist/detectors/use-maximum.js.map +1 -1
  167. package/dist/eval.js +3 -3
  168. package/dist/eval.js.map +1 -1
  169. package/dist/extension/document-view.d.ts +2 -2
  170. package/dist/extension/document-view.d.ts.map +1 -1
  171. package/dist/extension/document-view.js +17 -8
  172. package/dist/extension/document-view.js.map +1 -1
  173. package/dist/extension/entry-genres.d.ts +5 -0
  174. package/dist/extension/entry-genres.d.ts.map +1 -0
  175. package/dist/extension/entry-genres.js +13 -0
  176. package/dist/extension/entry-genres.js.map +1 -0
  177. package/dist/extension/module-detector.d.ts.map +1 -1
  178. package/dist/extension/module-detector.js +4 -4
  179. package/dist/extension/module-detector.js.map +1 -1
  180. package/dist/extension/plugin-parse.d.ts +1 -1
  181. package/dist/extension/plugin-parse.d.ts.map +1 -1
  182. package/dist/extension/plugin-parse.js +24 -7
  183. package/dist/extension/plugin-parse.js.map +1 -1
  184. package/dist/extension/problem-text.d.ts.map +1 -1
  185. package/dist/extension/problem-text.js +2 -0
  186. package/dist/extension/problem-text.js.map +1 -1
  187. package/dist/facts/age-values.d.ts.map +1 -1
  188. package/dist/facts/age-values.js +20 -2
  189. package/dist/facts/age-values.js.map +1 -1
  190. package/dist/facts/column-units.d.ts +31 -0
  191. package/dist/facts/column-units.d.ts.map +1 -0
  192. package/dist/facts/column-units.js +37 -0
  193. package/dist/facts/column-units.js.map +1 -0
  194. package/dist/facts/fact-values.d.ts +4 -0
  195. package/dist/facts/fact-values.d.ts.map +1 -1
  196. package/dist/facts/fact-values.js +3 -1
  197. package/dist/facts/fact-values.js.map +1 -1
  198. package/dist/facts/labelled-facts.d.ts +10 -0
  199. package/dist/facts/labelled-facts.d.ts.map +1 -1
  200. package/dist/facts/labelled-facts.js +6 -1
  201. package/dist/facts/labelled-facts.js.map +1 -1
  202. package/dist/facts/per-unit.d.ts +36 -0
  203. package/dist/facts/per-unit.d.ts.map +1 -0
  204. package/dist/facts/per-unit.js +56 -0
  205. package/dist/facts/per-unit.js.map +1 -0
  206. package/dist/facts/row-sentence-amounts.d.ts +3 -12
  207. package/dist/facts/row-sentence-amounts.d.ts.map +1 -1
  208. package/dist/facts/row-sentence-amounts.js +1 -15
  209. package/dist/facts/row-sentence-amounts.js.map +1 -1
  210. package/dist/facts/table-facts.d.ts +1 -1
  211. package/dist/facts/table-facts.d.ts.map +1 -1
  212. package/dist/facts/table-facts.js +13 -9
  213. package/dist/facts/table-facts.js.map +1 -1
  214. package/dist/facts/term-values.d.ts +13 -1
  215. package/dist/facts/term-values.d.ts.map +1 -1
  216. package/dist/facts/term-values.js +71 -12
  217. package/dist/facts/term-values.js.map +1 -1
  218. package/dist/fix-plan/text.js +2 -2
  219. package/dist/fix-plan/text.js.map +1 -1
  220. package/dist/grade/baseline.d.ts +4 -2
  221. package/dist/grade/baseline.d.ts.map +1 -1
  222. package/dist/grade/baseline.js +13 -11
  223. package/dist/grade/baseline.js.map +1 -1
  224. package/dist/grade/expect-read.d.ts +16 -0
  225. package/dist/grade/expect-read.d.ts.map +1 -0
  226. package/dist/grade/expect-read.js +52 -0
  227. package/dist/grade/expect-read.js.map +1 -0
  228. package/dist/grade/expect.d.ts +50 -0
  229. package/dist/grade/expect.d.ts.map +1 -0
  230. package/dist/grade/expect.js +53 -0
  231. package/dist/grade/expect.js.map +1 -0
  232. package/dist/grade/grade-item.d.ts +1 -1
  233. package/dist/grade/grade-item.d.ts.map +1 -1
  234. package/dist/grade/grade-item.js +13 -3
  235. package/dist/grade/grade-item.js.map +1 -1
  236. package/dist/grade/item.d.ts +9 -1
  237. package/dist/grade/item.d.ts.map +1 -1
  238. package/dist/grade/item.js +32 -7
  239. package/dist/grade/item.js.map +1 -1
  240. package/dist/grade/result.d.ts +5 -0
  241. package/dist/grade/result.d.ts.map +1 -1
  242. package/dist/grade/results-read.d.ts.map +1 -1
  243. package/dist/grade/results-read.js +4 -2
  244. package/dist/grade/results-read.js.map +1 -1
  245. package/dist/grade/rubric-file.d.ts +23 -0
  246. package/dist/grade/rubric-file.d.ts.map +1 -0
  247. package/dist/grade/rubric-file.js +35 -0
  248. package/dist/grade/rubric-file.js.map +1 -0
  249. package/dist/grade/rubric.d.ts +2 -0
  250. package/dist/grade/rubric.d.ts.map +1 -1
  251. package/dist/grade/rubric.js +75 -47
  252. package/dist/grade/rubric.js.map +1 -1
  253. package/dist/grade/setup.d.ts +2 -1
  254. package/dist/grade/setup.d.ts.map +1 -1
  255. package/dist/grade/setup.js +2 -2
  256. package/dist/grade/setup.js.map +1 -1
  257. package/dist/grade/stamp.d.ts +2 -0
  258. package/dist/grade/stamp.d.ts.map +1 -1
  259. package/dist/grade/stamp.js +8 -2
  260. package/dist/grade/stamp.js.map +1 -1
  261. package/dist/grade/task-rubric.d.ts +16 -0
  262. package/dist/grade/task-rubric.d.ts.map +1 -0
  263. package/dist/grade/task-rubric.js +34 -0
  264. package/dist/grade/task-rubric.js.map +1 -0
  265. package/dist/grade/text.d.ts +5 -1
  266. package/dist/grade/text.d.ts.map +1 -1
  267. package/dist/grade/text.js +16 -4
  268. package/dist/grade/text.js.map +1 -1
  269. package/dist/lexicon-genres.d.ts +7 -0
  270. package/dist/lexicon-genres.d.ts.map +1 -0
  271. package/dist/lexicon-genres.js +8 -0
  272. package/dist/lexicon-genres.js.map +1 -0
  273. package/dist/name-char-reading.d.ts +5 -0
  274. package/dist/name-char-reading.d.ts.map +1 -1
  275. package/dist/name-char-reading.js +15 -0
  276. package/dist/name-char-reading.js.map +1 -1
  277. package/dist/name-end-letter.d.ts +19 -0
  278. package/dist/name-end-letter.d.ts.map +1 -0
  279. package/dist/name-end-letter.js +37 -0
  280. package/dist/name-end-letter.js.map +1 -0
  281. package/dist/name-neighbours.d.ts.map +1 -1
  282. package/dist/name-neighbours.js +2 -1
  283. package/dist/name-neighbours.js.map +1 -1
  284. package/dist/name-separator.d.ts +6 -0
  285. package/dist/name-separator.d.ts.map +1 -1
  286. package/dist/name-separator.js +29 -0
  287. package/dist/name-separator.js.map +1 -1
  288. package/dist/name-variants.d.ts +6 -3
  289. package/dist/name-variants.d.ts.map +1 -1
  290. package/dist/name-variants.js +37 -10
  291. package/dist/name-variants.js.map +1 -1
  292. package/dist/number-name.d.ts.map +1 -1
  293. package/dist/number-name.js +11 -2
  294. package/dist/number-name.js.map +1 -1
  295. package/dist/plugin.d.ts +6 -0
  296. package/dist/plugin.d.ts.map +1 -1
  297. package/dist/profile/cue-lines.d.ts +6 -0
  298. package/dist/profile/cue-lines.d.ts.map +1 -0
  299. package/dist/profile/cue-lines.js +13 -0
  300. package/dist/profile/cue-lines.js.map +1 -0
  301. package/dist/profile/parse.d.ts +1 -1
  302. package/dist/profile/parse.d.ts.map +1 -1
  303. package/dist/profile/parse.js +8 -2
  304. package/dist/profile/parse.js.map +1 -1
  305. package/dist/reference-numbers.d.ts +2 -0
  306. package/dist/reference-numbers.d.ts.map +1 -1
  307. package/dist/reference-numbers.js +1 -1
  308. package/dist/reference-numbers.js.map +1 -1
  309. package/dist/run.d.ts.map +1 -1
  310. package/dist/run.js +1 -0
  311. package/dist/run.js.map +1 -1
  312. package/dist/spaced-person-name.d.ts.map +1 -1
  313. package/dist/spaced-person-name.js +26 -1
  314. package/dist/spaced-person-name.js.map +1 -1
  315. package/dist/spacing-code.d.ts.map +1 -1
  316. package/dist/spacing-code.js +10 -1
  317. package/dist/spacing-code.js.map +1 -1
  318. package/dist/spacing-minority.d.ts +7 -0
  319. package/dist/spacing-minority.d.ts.map +1 -1
  320. package/dist/spacing-minority.js +14 -0
  321. package/dist/spacing-minority.js.map +1 -1
  322. package/dist/structure/closing-balance.d.ts +9 -0
  323. package/dist/structure/closing-balance.d.ts.map +1 -1
  324. package/dist/structure/closing-balance.js +34 -7
  325. package/dist/structure/closing-balance.js.map +1 -1
  326. package/dist/structure/date-range.d.ts +3 -2
  327. package/dist/structure/date-range.d.ts.map +1 -1
  328. package/dist/structure/date-range.js +4 -4
  329. package/dist/structure/date-range.js.map +1 -1
  330. package/dist/structure/definition-use.d.ts.map +1 -1
  331. package/dist/structure/definition-use.js +20 -1
  332. package/dist/structure/definition-use.js.map +1 -1
  333. package/dist/structure/document-context.d.ts +2 -0
  334. package/dist/structure/document-context.d.ts.map +1 -0
  335. package/dist/structure/document-context.js +42 -0
  336. package/dist/structure/document-context.js.map +1 -0
  337. package/dist/structure/fenced-lines.d.ts +7 -0
  338. package/dist/structure/fenced-lines.d.ts.map +1 -0
  339. package/dist/structure/fenced-lines.js +27 -0
  340. package/dist/structure/fenced-lines.js.map +1 -0
  341. package/dist/structure/formula-amount.d.ts +38 -0
  342. package/dist/structure/formula-amount.d.ts.map +1 -0
  343. package/dist/structure/formula-amount.js +187 -0
  344. package/dist/structure/formula-amount.js.map +1 -0
  345. package/dist/structure/installment-fee.d.ts +52 -0
  346. package/dist/structure/installment-fee.d.ts.map +1 -0
  347. package/dist/structure/installment-fee.js +110 -0
  348. package/dist/structure/installment-fee.js.map +1 -0
  349. package/dist/structure/installment-total.d.ts +7 -0
  350. package/dist/structure/installment-total.d.ts.map +1 -1
  351. package/dist/structure/installment-total.js +6 -6
  352. package/dist/structure/installment-total.js.map +1 -1
  353. package/dist/structure/labelled-date-order.d.ts +31 -9
  354. package/dist/structure/labelled-date-order.d.ts.map +1 -1
  355. package/dist/structure/labelled-date-order.js +60 -15
  356. package/dist/structure/labelled-date-order.js.map +1 -1
  357. package/dist/structure/labelled-order.d.ts +7 -1
  358. package/dist/structure/labelled-order.d.ts.map +1 -1
  359. package/dist/structure/labelled-order.js +10 -3
  360. package/dist/structure/labelled-order.js.map +1 -1
  361. package/dist/structure/line-amount-label.d.ts.map +1 -1
  362. package/dist/structure/line-amount-label.js +2 -1
  363. package/dist/structure/line-amount-label.js.map +1 -1
  364. package/dist/structure/line-amount.d.ts +0 -5
  365. package/dist/structure/line-amount.d.ts.map +1 -1
  366. package/dist/structure/line-amount.js +1 -10
  367. package/dist/structure/line-amount.js.map +1 -1
  368. package/dist/structure/prose-total.d.ts +8 -0
  369. package/dist/structure/prose-total.d.ts.map +1 -1
  370. package/dist/structure/prose-total.js +70 -2
  371. package/dist/structure/prose-total.js.map +1 -1
  372. package/dist/structure/runs.d.ts.map +1 -1
  373. package/dist/structure/runs.js +9 -1
  374. package/dist/structure/runs.js.map +1 -1
  375. package/dist/structure/signed-date-line.d.ts +15 -0
  376. package/dist/structure/signed-date-line.d.ts.map +1 -0
  377. package/dist/structure/signed-date-line.js +68 -0
  378. package/dist/structure/signed-date-line.js.map +1 -0
  379. package/dist/structure/start-end-range.d.ts +30 -0
  380. package/dist/structure/start-end-range.d.ts.map +1 -0
  381. package/dist/structure/start-end-range.js +79 -0
  382. package/dist/structure/start-end-range.js.map +1 -0
  383. package/dist/structure/tax.d.ts.map +1 -1
  384. package/dist/structure/tax.js +5 -3
  385. package/dist/structure/tax.js.map +1 -1
  386. package/dist/structure/total-line.d.ts.map +1 -1
  387. package/dist/structure/total-line.js +10 -6
  388. package/dist/structure/total-line.js.map +1 -1
  389. package/dist/structure/total.d.ts +2 -0
  390. package/dist/structure/total.d.ts.map +1 -1
  391. package/dist/structure/total.js +34 -3
  392. package/dist/structure/total.js.map +1 -1
  393. package/dist/subject-codes.d.ts +21 -0
  394. package/dist/subject-codes.d.ts.map +1 -0
  395. package/dist/subject-codes.js +79 -0
  396. package/dist/subject-codes.js.map +1 -0
  397. package/dist/unlabelled-references.d.ts +6 -2
  398. package/dist/unlabelled-references.d.ts.map +1 -1
  399. package/dist/unlabelled-references.js +11 -10
  400. package/dist/unlabelled-references.js.map +1 -1
  401. package/genres.yaml +17 -0
  402. package/package.json +4 -4
  403. package/profiles/listing.yaml +1 -1
  404. package/profiles/property-sale.yaml +21 -0
  405. package/rules/closing-balance-mismatch.yaml +3 -3
  406. package/rules/date-range-reversed.yaml +4 -4
  407. package/rules/due-before-issue.yaml +9 -5
  408. package/rules/fact-conflict.yaml +2 -2
  409. package/rules/formula-amount-mismatch.yaml +67 -0
  410. package/rules/heading-echo.yaml +2 -2
  411. package/rules/installment-total-mismatch.yaml +9 -4
  412. package/rules/internal-jargon.yaml +2 -2
  413. package/rules/latin-spacing.yaml +4 -4
  414. package/rules/list-item-form-mix.yaml +1 -1
  415. package/rules/name-variant.yaml +5 -2
  416. package/rules/no-lead.yaml +2 -2
  417. package/rules/number-without-unit.yaml +2 -2
  418. package/rules/orthographic-variant.yaml +3 -3
  419. package/rules/reference-number-variant.yaml +3 -3
  420. package/rules/toki-as-condition.yaml +71 -0
  421. package/rules/total-mismatch.yaml +4 -4
  422. package/rules/unknown-word.yaml +2 -2
  423. package/rules/use-before-definition.yaml +2 -2
  424. package/src/api.ts +11 -2
  425. package/src/cli-text.ts +6 -4
  426. package/src/commands/grade.ts +75 -23
  427. package/src/company-kana-spelling.ts +22 -2
  428. package/src/company-name-ending.ts +50 -0
  429. package/src/company-names.ts +52 -17
  430. package/src/cross-run.ts +1 -0
  431. package/src/custom/no-rewrite.ts +2 -0
  432. package/src/custom/parse.ts +18 -2
  433. package/src/detectors/age-words.ts +12 -0
  434. package/src/detectors/agreement-slip.ts +3 -0
  435. package/src/detectors/annual-pay-mismatch.ts +3 -18
  436. package/src/detectors/blank-structure.ts +4 -3
  437. package/src/detectors/closing-balance-mismatch.ts +1 -0
  438. package/src/detectors/code-fences.ts +10 -0
  439. package/src/detectors/condition-toki.ts +60 -0
  440. package/src/detectors/conjugated-entry.ts +53 -0
  441. package/src/detectors/content-morphemes.ts +2 -1
  442. package/src/detectors/cross-facts.ts +2 -1
  443. package/src/detectors/custom.ts +9 -2
  444. package/src/detectors/document-shape.ts +8 -0
  445. package/src/detectors/due-date.ts +17 -7
  446. package/src/detectors/fact-consistency.ts +13 -17
  447. package/src/detectors/form-line.ts +47 -0
  448. package/src/detectors/formula-amount.ts +43 -0
  449. package/src/detectors/heading-claim.ts +76 -0
  450. package/src/detectors/heading-echo.ts +32 -1
  451. package/src/detectors/installment-total-mismatch.ts +35 -24
  452. package/src/detectors/list-parallel.ts +46 -7
  453. package/src/detectors/name-variant.ts +45 -8
  454. package/src/detectors/number-unit.ts +35 -9
  455. package/src/detectors/orthographic-variant.ts +5 -3
  456. package/src/detectors/orthography.ts +17 -8
  457. package/src/detectors/per-unit-words.ts +24 -0
  458. package/src/detectors/period-multiple-mismatch.ts +2 -18
  459. package/src/detectors/reference-number-variant.ts +27 -5
  460. package/src/detectors/registry/condition-toki.ts +4 -0
  461. package/src/detectors/registry/formula-amount.ts +4 -0
  462. package/src/detectors/rent-multiple-mismatch.ts +2 -18
  463. package/src/detectors/row-sentence-amounts.ts +5 -20
  464. package/src/detectors/stated-amounts.ts +20 -0
  465. package/src/detectors/step-time-sum.ts +2 -1
  466. package/src/detectors/structure-tree.ts +7 -2
  467. package/src/detectors/team.ts +13 -7
  468. package/src/detectors/time-order.ts +6 -3
  469. package/src/detectors/unknown-word.ts +20 -2
  470. package/src/detectors/use-interval.ts +8 -4
  471. package/src/detectors/use-maximum.ts +3 -1
  472. package/src/eval.ts +3 -3
  473. package/src/extension/document-view.ts +18 -9
  474. package/src/extension/entry-genres.ts +15 -0
  475. package/src/extension/module-detector.ts +9 -4
  476. package/src/extension/plugin-parse.ts +29 -7
  477. package/src/extension/problem-text.ts +4 -0
  478. package/src/facts/age-values.ts +24 -2
  479. package/src/facts/column-units.ts +67 -0
  480. package/src/facts/fact-values.ts +11 -1
  481. package/src/facts/labelled-facts.ts +16 -2
  482. package/src/facts/per-unit.ts +105 -0
  483. package/src/facts/row-sentence-amounts.ts +4 -26
  484. package/src/facts/table-facts.ts +7 -4
  485. package/src/facts/term-values.ts +94 -13
  486. package/src/fix-plan/text.ts +2 -2
  487. package/src/grade/baseline.ts +20 -12
  488. package/src/grade/expect-read.ts +65 -0
  489. package/src/grade/expect.ts +74 -0
  490. package/src/grade/grade-item.ts +14 -3
  491. package/src/grade/item.ts +48 -8
  492. package/src/grade/result.ts +5 -0
  493. package/src/grade/results-read.ts +7 -2
  494. package/src/grade/rubric-file.ts +46 -0
  495. package/src/grade/rubric.ts +77 -47
  496. package/src/grade/setup.ts +3 -3
  497. package/src/grade/stamp.ts +10 -2
  498. package/src/grade/task-rubric.ts +39 -0
  499. package/src/grade/text.ts +27 -5
  500. package/src/lexicon-genres.ts +11 -0
  501. package/src/name-char-reading.ts +16 -0
  502. package/src/name-end-letter.ts +52 -0
  503. package/src/name-neighbours.ts +2 -1
  504. package/src/name-separator.ts +32 -0
  505. package/src/name-variants.ts +54 -13
  506. package/src/number-name.ts +12 -3
  507. package/src/plugin.ts +6 -0
  508. package/src/profile/cue-lines.ts +14 -0
  509. package/src/profile/parse.ts +11 -2
  510. package/src/reference-numbers.ts +1 -1
  511. package/src/run.ts +1 -0
  512. package/src/spaced-person-name.ts +27 -2
  513. package/src/spacing-code.ts +13 -2
  514. package/src/spacing-minority.ts +17 -0
  515. package/src/structure/closing-balance.ts +46 -8
  516. package/src/structure/date-range.ts +4 -3
  517. package/src/structure/definition-use.ts +24 -1
  518. package/src/structure/document-context.ts +46 -0
  519. package/src/structure/fenced-lines.ts +28 -0
  520. package/src/structure/formula-amount.ts +236 -0
  521. package/src/structure/installment-fee.ts +201 -0
  522. package/src/structure/installment-total.ts +6 -6
  523. package/src/structure/labelled-date-order.ts +105 -21
  524. package/src/structure/labelled-order.ts +21 -10
  525. package/src/structure/line-amount-label.ts +2 -1
  526. package/src/structure/line-amount.ts +1 -11
  527. package/src/structure/prose-total.ts +81 -2
  528. package/src/structure/runs.ts +10 -1
  529. package/src/structure/signed-date-line.ts +78 -0
  530. package/src/structure/start-end-range.ts +120 -0
  531. package/src/structure/tax.ts +6 -3
  532. package/src/structure/total-line.ts +12 -6
  533. package/src/structure/total.ts +42 -3
  534. package/src/subject-codes.ts +105 -0
  535. package/src/unlabelled-references.ts +24 -16
@@ -2,7 +2,7 @@ import type { Detector, Finding, ProseDocument, StructureNode } from "../plugin.
2
2
  import { countedFacts, countedPhraseAt, type CountedPhrase } from "../facts/counted-facts.ts";
3
3
  import { nameSpans } from "../compare/proper-nouns.ts";
4
4
  import { factValues, type FactValue } from "../facts/fact-values.ts";
5
- import { labelledFacts, type AttributePhrase, type Fact, type FactWords } from "../facts/labelled-facts.ts";
5
+ import { attributePhrasesOf, labelledFacts, type Fact, type FactWords } from "../facts/labelled-facts.ts";
6
6
  import { tableFacts, type TotalColumns } from "../facts/table-facts.ts";
7
7
  import { withCellKeys } from "../facts/cell-keys.ts";
8
8
  import { verbQuantityFacts, type VerbQuantityWords } from "../facts/verb-quantity-facts.ts";
@@ -21,11 +21,13 @@ import { overlapsAny, spanIndex } from "../compare/spans.ts";
21
21
  import { documentTermConflicts, isTermName, termHomes, type TermWord, type TermWords } from "../facts/document-terms.ts";
22
22
  import { termValues, type PlacedWord, type TermValueWords } from "../facts/term-values.ts";
23
23
  import { inDocumentOrder } from "../structure/issues.ts";
24
- import { ageValues, type AgeWord, type AgeWords } from "../facts/age-values.ts";
24
+ import { ageValues } from "../facts/age-values.ts";
25
+ import { ageWordsOf } from "./age-words.ts";
25
26
  import { qualifiedKeyOf, qualifiedKeys, type QualifierWords } from "../facts/qualified-labels.ts";
26
27
  import { rowSentenceFindings } from "./row-sentence-amounts.ts";
27
28
  import type { RateWord, RateWords } from "../facts/rate-values.ts";
28
29
  import { wholeCountFindings } from "./whole-count-findings.ts";
30
+ import { perUnitsOf, perUnitValues } from "./per-unit-words.ts";
29
31
 
30
32
  const patternsOf = (doc: ProseDocument, id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
31
33
 
@@ -51,7 +53,7 @@ export const factWordsOf = (doc: ProseDocument): FactWords => ({
51
53
  leads: patternsOf(doc, "fact-label-lead"),
52
54
  rates: rateWordsOf(doc),
53
55
  readings: { verbs: patternsOf(doc, "fact-reading-verb"), gauges: patternsOf(doc, "fact-reading-gauge"), clauses: patternsOf(doc, "fact-reading-clause") },
54
- attributes: (doc.lexicons["fact-attribute"] ?? []).map((entry): AttributePhrase => ({ pattern: entry.pattern, position: entry.position ?? "before" })),
56
+ attributes: attributePhrasesOf(doc.lexicons["fact-attribute"] ?? []),
55
57
  });
56
58
 
57
59
  const placedWordsOf = (doc: ProseDocument, id: string): PlacedWord[] =>
@@ -71,6 +73,10 @@ const termValueWordsOf = (doc: ProseDocument): TermValueWords => ({
71
73
  selves: placedWordsOf(doc, "fact-term-self"),
72
74
  starts: placedWordsOf(doc, "fact-term-start"),
73
75
  valueEnds: patternsOf(doc, "fact-value-end"),
76
+ restating: patternsOf(doc, "fact-term-restating"),
77
+ frames: patternsOf(doc, "fact-term-frame"),
78
+ conditions: patternsOf(doc, "fact-amount-condition"),
79
+ qualifiers: qualifierWordsOf(doc),
74
80
  });
75
81
 
76
82
  const totalColumnsOf = (doc: ProseDocument): TotalColumns => {
@@ -89,15 +95,6 @@ const durationWordsOf = (doc: ProseDocument): DurationWord[] => [
89
95
  ),
90
96
  ];
91
97
 
92
- const ageWordsAt = (doc: ProseDocument, id: string): AgeWord[] =>
93
- (doc.lexicons[id] ?? []).map((entry): AgeWord => ({ pattern: entry.pattern, position: entry.position ?? "before" }));
94
-
95
- const ageWordsOf = (doc: ProseDocument): AgeWords => ({
96
- marks: ageWordsAt(doc, "fact-age-mark"),
97
- limits: ageWordsAt(doc, "fact-age-limit"),
98
- joiners: patternsOf(doc, "fact-age-joiner"),
99
- });
100
-
101
98
  const qualifierWordsOf = (doc: ProseDocument): QualifierWords => ({
102
99
  templates: patternsOf(doc, "fact-label-qualifier"),
103
100
  determiners: patternsOf(doc, "fact-label-drop"),
@@ -123,6 +120,7 @@ const withQualifiedKeys = <T extends Fact>(doc: ProseDocument, facts: readonly T
123
120
  /**
124
121
  * 単位の語彙表の量(410 g、1.2 kg)と期間(3 months)と年齢(満70歳まで、aged 20 to 70)も値として読む。木が単位を読まない量は、
125
122
  * 数だけでは升や文の値にならない。年齢と重なる期間(30 years old の 30 years)と、期間と重なる量(3 months の 3 m)は読まない。
123
+ * 数量には何あたりか(1kmあたり15円)を付ける。何あたりの値は、それを掛けた金額とは比べない。
126
124
  */
127
125
  const readFacts = (doc: ProseDocument, tree: StructureNode): ScopedFact[] => {
128
126
  const tagged = factValues(tree, doc.source, nameSpans(doc));
@@ -134,7 +132,7 @@ const readFacts = (doc: ProseDocument, tree: StructureNode): ScopedFact[] => {
134
132
  durationWordsOf(doc),
135
133
  );
136
134
  const taken = spanIndex([...ages, ...durations]);
137
- const values = valuesWith(tree, doc, [...measuredOf(doc).filter((value) => !overlapsAny(taken, value)), ...ages, ...durations]);
135
+ const values = perUnitsOf(doc, valuesWith(tree, doc, [...measuredOf(doc).filter((value) => !overlapsAny(taken, value)), ...ages, ...durations]));
138
136
  const terms = termValues(doc.source, values, termValueWordsOf(doc));
139
137
  const termed = new Set(terms.map((fact) => fact.value.start));
140
138
  const labelled = labelledFacts(doc.source, values, { ...factWordsOf(doc), qualifiers: qualifierWordsOf(doc) }).filter(
@@ -263,8 +261,7 @@ const countedPhrasesOf = (doc: ProseDocument): CountedPhrase[] =>
263
261
  const countedFactsOf = (doc: ProseDocument): ScopedFact[] => {
264
262
  const tree = doc.structure;
265
263
  if (tree === undefined) return [];
266
- const values = factValues(tree, doc.source, nameSpans(doc));
267
- return scopedFacts(countedFacts(doc.source, values, countedPhrasesOf(doc)), tree, doc.source, patternsOf(doc, "summary-heading"));
264
+ return scopedFacts(countedFacts(doc.source, perUnitValues(doc, tree), countedPhrasesOf(doc)), tree, doc.source, patternsOf(doc, "summary-heading"));
268
265
  };
269
266
 
270
267
  const wordsAt = (doc: ProseDocument, id: string): WordAt[] =>
@@ -319,8 +316,7 @@ const pairFinding =
319
316
  const conditionPairFindings = (doc: ProseDocument): Finding[] => {
320
317
  const tree = doc.structure;
321
318
  if (tree === undefined) return [];
322
- const values = factValues(tree, doc.source, nameSpans(doc));
323
- return conditionPairConflicts(doc.source, changeSentencesOf(doc, tree), values, changeWordsOf(doc)).map(pairFinding(doc));
319
+ return conditionPairConflicts(doc.source, changeSentencesOf(doc, tree), perUnitValues(doc, tree), changeWordsOf(doc)).map(pairFinding(doc));
324
320
  };
325
321
 
326
322
  const eventWordsOf = (doc: ProseDocument): EventWord[] =>
@@ -0,0 +1,47 @@
1
+ // Lines at the top of a form or a letter that are not sentences: a field ("Invoice number: INV-2026-0318",
2
+ // 「見積番号:RF-731852」) or a name standing alone (a letterhead, 「有限会社つばめ野リフォーム」). Pure.
3
+ import { labelLine } from "./number-unit.ts";
4
+ import type { Token } from "../plugin.ts";
5
+
6
+ /** Parts of speech a name is made of: no verb, adjective, determiner or particle. */
7
+ const NAME_POS: ReadonlySet<string> = new Set(["NOUN", "PROPN", "NUM", "PUNCT", "SYM", "X"]);
8
+ /** A value with one of these says something ("Note: this page is a sample."); it is a sentence behind a label. */
9
+ const PREDICATE_POS: ReadonlySet<string> = new Set(["VERB", "AUX"]);
10
+
11
+ const isNameOnly = (tokens: readonly Token[]): boolean => tokens.some((token) => token.pos !== "PUNCT") && tokens.every((token) => NAME_POS.has(token.pos));
12
+
13
+ /** "Label: value" whose value states nothing: a number, a date, a name. */
14
+ const isField = (line: string, start: number, tokens: readonly Token[]): boolean => {
15
+ const field = labelLine(line, start);
16
+ return field !== undefined && !tokens.some((token) => token.span.start >= field.at && PREDICATE_POS.has(token.pos));
17
+ };
18
+
19
+ type Line = { readonly text: string; readonly start: number };
20
+
21
+ const linesOf = (text: string, start: number): Line[] =>
22
+ text.split("\n").reduce<Line[]>((lines, line) => {
23
+ const last = lines.at(-1);
24
+ return [...lines, { text: line, start: last === undefined ? start : last.start + last.text.length + 1 }];
25
+ }, []);
26
+
27
+ const isFormLine = (line: Line, tokens: readonly Token[]): boolean => {
28
+ const inLine = tokens.filter((token) => token.span.start >= line.start && token.span.start < line.start + line.text.length);
29
+ return isNameOnly(inLine) || isField(line.text, line.start, inLine);
30
+ };
31
+
32
+ /**
33
+ * Every line of the block gives a field and its value, or only a name: the top of a form or a letterhead, not a sentence.
34
+ * `start` is where the text starts in the coordinates of the tokens' spans. Without tokens no block is read as a form.
35
+ */
36
+ export const isFormBlock = (text: string, start: number, tokens: readonly Token[] | undefined): boolean => {
37
+ const lines = linesOf(text, start).filter((line) => line.text.trim() !== "");
38
+ return tokens !== undefined && lines.length > 0 && lines.every((line) => isFormLine(line, tokens));
39
+ };
40
+
41
+ /** What a form block gives: the tokens after each field's label, and every token of a name line. */
42
+ export const formValueTokens = (text: string, start: number, tokens: readonly Token[]): Token[] =>
43
+ linesOf(text, start).flatMap((line) => {
44
+ const field = labelLine(line.text, line.start);
45
+ const from = field?.at ?? line.start;
46
+ return tokens.filter((token) => token.span.start >= from && token.span.start < line.start + line.text.length);
47
+ });
@@ -0,0 +1,43 @@
1
+ // formula-amount-mismatch: the reading half. The words of a formula come from the lexicon formula-amount-word (name-rate,
2
+ // rate-name, plus, not-included) and percent-unit; the tax words are tax-price-column's, as tax-mismatch reads them, with tax-label and
3
+ // tax-included-word for a tax named as included (消費税8%込み). The amounts are
4
+ // currency-notation's, read with amount-multiplier's words of scale. The profile may give the tax rate a document leaves out.
5
+ import type { Detector, Finding, ProseDocument } from "../plugin.ts";
6
+ import { formulaAmountMismatches, type FormulaWords } from "../structure/formula-amount.ts";
7
+ import type { TaxPriceRole, TaxPriceWord } from "../structure/tax-price.ts";
8
+ import { proseAndTablesOf } from "../table-text.ts";
9
+ import { quoteAround } from "./quote-around.ts";
10
+ import { statedAmountsOf } from "./stated-amounts.ts";
11
+
12
+ const TAX_ROLES: readonly string[] = ["excluded", "included", "rate"] satisfies readonly TaxPriceRole[];
13
+ const isTaxRole = (group: string | undefined): group is TaxPriceRole => group !== undefined && TAX_ROLES.includes(group);
14
+
15
+ const patternsOf = (doc: ProseDocument, lexicon: string, group?: string): string[] =>
16
+ (doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group).map((entry) => entry.pattern);
17
+
18
+ const taxWordsOf = (doc: ProseDocument): TaxPriceWord[] =>
19
+ (doc.lexicons["tax-price-column"] ?? []).flatMap((entry) => (isTaxRole(entry.group) ? [{ pattern: entry.pattern, role: entry.group }] : []));
20
+
21
+ const wordsOf = (doc: ProseDocument): FormulaWords => ({
22
+ nameRate: patternsOf(doc, "formula-amount-word", "name-rate"),
23
+ rateName: patternsOf(doc, "formula-amount-word", "rate-name"),
24
+ plus: patternsOf(doc, "formula-amount-word", "plus"),
25
+ percent: patternsOf(doc, "percent-unit"),
26
+ tax: taxWordsOf(doc),
27
+ taxNames: patternsOf(doc, "tax-label"),
28
+ includedWords: patternsOf(doc, "tax-included-word"),
29
+ notIncluded: patternsOf(doc, "formula-amount-word", "not-included"),
30
+ });
31
+
32
+ export const formulaAmount: Detector = (doc): Finding[] => {
33
+ const text = proseAndTablesOf(doc);
34
+ const input = { text, amounts: statedAmountsOf(text, doc), defaultTaxRate: doc.profile?.taxRate };
35
+ return formulaAmountMismatches(input, wordsOf(doc)).map((issue) => ({
36
+ rule: "formula-amount-mismatch",
37
+ severity: "warning",
38
+ line: 0,
39
+ column: 0,
40
+ quote: quoteAround(text, issue.offset),
41
+ values: { ...issue.values, offset: issue.offset },
42
+ }));
43
+ };
@@ -0,0 +1,76 @@
1
+ // Whether a first sentence says something about its heading's subject, rather than announcing or restating it. Pure;
2
+ // heading-echo reads it. The words that only announce or point (covers, below, 説明, 以下), the copulas and the
3
+ // negations come from the language package's heading-announcer lexicon.
4
+ import { isContent } from "./content-morphemes.ts";
5
+ import type { LexiconEntry, Token } from "../plugin.ts";
6
+
7
+ type Side = "before" | "after";
8
+
9
+ export type AnnouncerWords = {
10
+ /** Words that announce the section or point at it: they carry no claim of their own. */
11
+ readonly announcing: ReadonlySet<string>;
12
+ /** Copulas (be, です), each with the side its complement stands on. */
13
+ readonly copulas: ReadonlyMap<string, Side>;
14
+ /** Words that deny what a copula links (There are no issues, is not in the manual): the denial is the claim. */
15
+ readonly negations: ReadonlySet<string>;
16
+ };
17
+
18
+ const patternsOf = (entries: readonly LexiconEntry[], group: string | undefined): Set<string> =>
19
+ new Set(entries.filter((entry) => entry.group === group).map((entry) => entry.pattern.toLowerCase()));
20
+
21
+ export const announcerWords = (entries: readonly LexiconEntry[]): AnnouncerWords => ({
22
+ announcing: patternsOf(entries, undefined),
23
+ copulas: new Map(entries.flatMap((entry): [string, Side][] => (entry.group === "copula" ? [[entry.pattern.toLowerCase(), entry.position ?? "after"]] : []))),
24
+ negations: patternsOf(entries, "negation"),
25
+ });
26
+
27
+ const baseForm = (token: Token): string => (token.lemma ?? token.surface).toLowerCase();
28
+
29
+ /** Between a copula and its complement: not, very, the (is not the, are very). */
30
+ const BRIDGING_POS = new Set(["ADV", "PART", "DET"]);
31
+
32
+ /** What a copula at `at` links to, on its complement's side: the bridging words, then the complement when there is one. */
33
+ const linkedBy = (tokens: readonly Token[], at: number, side: Side): Token[] => {
34
+ const outward = side === "after" ? tokens.slice(at + 1) : tokens.slice(0, at).reverse();
35
+ const complementAt = outward.findIndex((token) => !BRIDGING_POS.has(token.pos));
36
+ return complementAt === -1 ? outward : outward.slice(0, complementAt + 1);
37
+ };
38
+
39
+ /** An -ing form or a past participle names or modifies (Moving through the process, An object representing a server); it predicates only after a copula. */
40
+ const NON_FINITE = new Set(["Ger", "Part"]);
41
+
42
+ const isFiniteVerb = (token: Token): boolean => token.pos === "VERB" && !NON_FINITE.has(token.features?.["VerbForm"] ?? "");
43
+
44
+ type Predicate = { readonly words: readonly Token[]; readonly negations: readonly Token[] };
45
+
46
+ const copulaPredicate = (tokens: readonly Token[], at: number, side: Side, words: AnnouncerWords): Predicate => {
47
+ const linked = linkedBy(tokens, at, side);
48
+ return { words: linked.filter((word) => !BRIDGING_POS.has(word.pos)), negations: linked.filter((word) => words.negations.has(baseForm(word))) };
49
+ };
50
+
51
+ /** A finite verb that is no copula, a word turned into a verb by a light verb (変更し), or a copula's complement. */
52
+ const predicatesOf = (tokens: readonly Token[], words: AnnouncerWords): Predicate[] =>
53
+ tokens.flatMap((token, index): Predicate[] => {
54
+ const side = words.copulas.get(baseForm(token));
55
+ if (side !== undefined) return [copulaPredicate(tokens, index, side, words)];
56
+ const lightVerbNext = tokens[index + 1]?.features?.["VerbType"] === "Light";
57
+ return isFiniteVerb(token) || lightVerbNext ? [{ words: [token], negations: [] }] : [];
58
+ });
59
+
60
+ /**
61
+ * The sentence has a predicate of its own: a word that is not one of the heading's words and does not only announce or
62
+ * point (covers, below, 説明, 以下), or a denial the heading does not make. "All charges include sales tax." says
63
+ * something about charges; "This section covers installation." and "The full reference is in the manual." do not.
64
+ */
65
+ /** A code name in a heading holds words of its own (restoreFixtures, cache_key, dry-run): the sentence that spells them out repeats them. */
66
+ const CODE_NAME_PART = /\p{Lu}?[\p{Ll}\p{Nd}]+|\p{Lu}+(?!\p{Ll})/gu;
67
+
68
+ const headingWords = (heading: readonly Token[]): Set<string> =>
69
+ new Set(heading.flatMap((token) => [baseForm(token), ...(token.surface.match(CODE_NAME_PART) ?? []).map((part) => part.toLowerCase())]));
70
+
71
+ export const makesClaim = (heading: readonly Token[], sentence: readonly Token[], words: AnnouncerWords): boolean => {
72
+ const said = headingWords(heading);
73
+ const unsaid = (token: Token): boolean => !said.has(baseForm(token));
74
+ const ownWord = (token: Token): boolean => isContent(token) && unsaid(token) && !words.announcing.has(baseForm(token)) && !words.copulas.has(baseForm(token));
75
+ return predicatesOf(sentence, words).some((predicate) => predicate.negations.some(unsaid) || predicate.words.some(ownWord));
76
+ };
@@ -1,7 +1,9 @@
1
1
  import { lengthOf, proseText } from "../measure.ts";
2
2
  import { newContentMorphemes } from "./content-morphemes.ts";
3
3
  import { echoedHeadingUnits, trigrams } from "./heading-overlap.ts";
4
+ import { formValueTokens, isFormBlock } from "./form-line.ts";
4
5
  import { titleTokens } from "../heading-label.ts";
6
+ import { announcerWords, makesClaim } from "./heading-claim.ts";
5
7
  import { handsOver } from "./lead-in.ts";
6
8
  import { holdsLink, namesLocation } from "./location-pointer.ts";
7
9
  import { withoutQuotedVariants } from "./quoted-variant.ts";
@@ -65,6 +67,17 @@ const pointsElsewhere = (doc: ProseDocument, section: Section): boolean => {
65
67
  return first !== undefined && (holdsLink(doc.source, first.span, doc.links) || namesLocation(doc.source.slice(first.span.start, first.span.end)));
66
68
  };
67
69
 
70
+ /**
71
+ * 最初の文が、見出しに無い述語で見出しの主題について何かを述べている(## Charges → All charges include sales tax.)。
72
+ * 予告や指示の語(covers, below, 説明, 以下)だけを述語にする文は、見出しを言い直しているだけで繰り返し。
73
+ */
74
+ const statesClaim = (doc: ProseDocument, section: Section): boolean => {
75
+ const first = section.firstSentence;
76
+ if (first?.tokens === undefined || section.headingTokens === undefined) return false;
77
+ const heading = titleTokens(section.heading, measuredHeading(section), section.headingTokens);
78
+ return makesClaim(heading, first.tokens, announcerWords(doc.lexicons["heading-announcer"] ?? []));
79
+ };
80
+
68
81
  /** A figure, with its thousands separators (1,320,000), compared without them. */
69
82
  const FIGURE = /\p{Nd}+(?:[,,]\p{Nd}{3})*/gu;
70
83
  /** Note marks are not figures: [^1], ※1, <sup>1</sup>. */
@@ -81,6 +94,17 @@ export const statesFigure = (heading: string, sentence: string): boolean => {
81
94
  return figuresOf(sentence).some((figure) => !inHeading.has(figure));
82
95
  };
83
96
 
97
+ /**
98
+ * 最初の文が、見出しに無い名前や値を書いた、名前だけの行(レターヘッド)か項目と値の行(Customer: Laura Pennington)。
99
+ * 書式の頭で、見出しを言い直す文ではない。見出しをそのまま写した行(### 環境情報学研究科 → 環境情報学研究科、詳細:〈見出し〉)は繰り返し。
100
+ */
101
+ const opensForm = (section: Section): boolean => {
102
+ const first = section.firstSentence;
103
+ if (first?.tokens === undefined || section.headingTokens === undefined || !isFormBlock(first.text, first.span.start, first.tokens)) return false;
104
+ const heading = titleTokens(section.heading, measuredHeading(section), section.headingTokens);
105
+ return newContentMorphemes(heading, formValueTokens(first.text, first.span.start, first.tokens)) > 0;
106
+ };
107
+
84
108
  /** 見出しとの重なりを測る文。用語集や表記の手引きは見出しの語の別の書き方を引用する(Not “datacentre”)ので、それは数えない。 */
85
109
  const echoedText = (section: Section): string => withoutQuotedVariants(section.firstSentence?.text ?? "", measuredHeading(section));
86
110
 
@@ -88,9 +112,16 @@ export const headingEcho: Detector = (doc, options): Finding[] => {
88
112
  const leadIns = (doc.lexicons["lead-in"] ?? []).map((entry) => entry.pattern);
89
113
  return doc.sections
90
114
  .filter((section) => section.heading.length > 0 && section.firstSentence !== undefined && addsLittle(section, doc.lengthUnit))
91
- .filter((section) => !leadsIn(doc, section, leadIns) && !pointsElsewhere(doc, section) && !statesFigure(section.heading, section.firstSentence?.text ?? ""))
115
+ .filter(
116
+ (section) =>
117
+ !leadsIn(doc, section, leadIns) &&
118
+ !pointsElsewhere(doc, section) &&
119
+ !statesFigure(section.heading, section.firstSentence?.text ?? "") &&
120
+ !opensForm(section),
121
+ )
92
122
  .map((section) => ({ section, overlap: Math.round(containment(trigrams(measuredHeading(section)), trigrams(echoedText(section))) * 100) }))
93
123
  .filter(({ overlap }) => overlap >= options.limit)
124
+ .filter(({ section }) => !statesClaim(doc, section))
94
125
  .map(({ section, overlap }) => ({
95
126
  rule: "heading-echo",
96
127
  severity: "warning",
@@ -3,11 +3,12 @@
3
3
  // labels are installment-label, the other words installment-word, approximate-marker, number-word (digit), range-connector
4
4
  // and amount-range-word.
5
5
  import type { Detector, Finding, Lexicon, ProseDocument } from "../plugin.ts";
6
- import { installmentTotalMismatches, type InstallmentAmount, type InstallmentWords, type PositionedWord } from "../structure/installment-total.ts";
7
- import { scaledAmountOf, scaleWordsOf } from "./amount-scale.ts";
8
- import { amountsIn, formsOf } from "./currency-notation.ts";
6
+ import { installmentFeeMismatches, type InstallmentSeriesWords } from "../structure/installment-fee.ts";
7
+ import { installmentTotalMismatches, type InstallmentWords, type PositionedWord } from "../structure/installment-total.ts";
8
+ import type { StructureIssue } from "../structure/issues.ts";
9
9
  import { proseAndTablesOf } from "../table-text.ts";
10
10
  import { quoteAround } from "./quote-around.ts";
11
+ import { statedAmountsOf } from "./stated-amounts.ts";
11
12
 
12
13
  const entriesOf = (doc: ProseDocument, lexicon: string, group?: string): Lexicon =>
13
14
  (doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group);
@@ -41,28 +42,38 @@ const wordsOf = (doc: ProseDocument): InstallmentWords => ({
41
42
  numberWords: numberWordsOf(doc),
42
43
  });
43
44
 
44
- const amountsOf = (text: string, doc: ProseDocument): InstallmentAmount[] => {
45
- const scales = scaleWordsOf(doc);
46
- return amountsIn(
47
- text,
48
- formsOf(doc),
49
- scales.map((scale) => scale.word),
50
- ).flatMap((amount) => {
51
- const read = scaledAmountOf(text, amount, scales);
52
- return read === undefined
53
- ? []
54
- : [{ offset: read.offset, end: amount.offset + amount.written.length, currency: read.currency, value: read.value, scale: read.unit }];
55
- });
56
- };
45
+ const weightedOf = (doc: ProseDocument, lexicon: string, group: string): InstallmentSeriesWords["ordinals"] =>
46
+ entriesOf(doc, lexicon, group).flatMap((entry) => (entry.weight === undefined ? [] : [{ word: entry.pattern, value: entry.weight }]));
47
+
48
+ const seriesWordsOf = (doc: ProseDocument): InstallmentSeriesWords => ({
49
+ numbered: patternsOf(doc, "installment-label", "numbered"),
50
+ finals: patternsOf(doc, "installment-label", "final"),
51
+ deposits: patternsOf(doc, "installment-label", "deposit"),
52
+ ordinals: [...weightedOf(doc, "installment-word", "ordinal"), ...numberWordsOf(doc)],
53
+ ordinalSuffixes: [...new Set(patternsOf(doc, "ordinal-suffix"))],
54
+ payments: patternsOf(doc, "installment-word", "payment"),
55
+ fees: patternsOf(doc, "installment-word", "fee"),
56
+ feeLinks: patternsOf(doc, "installment-word", "fee-link"),
57
+ });
58
+
59
+ const findingOf = (text: string, issue: StructureIssue, variant?: string): Finding => ({
60
+ rule: "installment-total-mismatch",
61
+ severity: "warning",
62
+ line: 0,
63
+ column: 0,
64
+ quote: quoteAround(text, issue.offset),
65
+ values: { ...issue.values, offset: issue.offset },
66
+ ...(variant === undefined ? {} : { variant }),
67
+ });
57
68
 
58
69
  export const installmentTotalMismatch: Detector = (doc): Finding[] => {
59
70
  const text = proseAndTablesOf(doc);
60
- return installmentTotalMismatches(text, amountsOf(text, doc), wordsOf(doc)).map((issue) => ({
61
- rule: "installment-total-mismatch",
62
- severity: "warning",
63
- line: 0,
64
- column: 0,
65
- quote: quoteAround(text, issue.offset),
66
- values: { ...issue.values, offset: issue.offset },
67
- }));
71
+ const amounts = statedAmountsOf(text, doc);
72
+ const words = wordsOf(doc);
73
+ const loans = installmentTotalMismatches(text, amounts, words);
74
+ const headings = (doc.markup?.headings ?? []).map((heading) => heading.start);
75
+ const fees = installmentFeeMismatches(text, amounts, headings, words, seriesWordsOf(doc)).filter(
76
+ (issue) => !loans.some((loan) => loan.offset === issue.offset),
77
+ );
78
+ return [...loans.map((issue) => findingOf(text, issue)), ...fees.map((issue) => findingOf(text, issue, "fee"))];
68
79
  };
@@ -63,18 +63,56 @@ const isTitleCase = (text: string): boolean =>
63
63
  .split(/\s+/u)
64
64
  .every((word) => TITLE_SMALL_WORDS.has(word) || /^\p{Lu}/u.test(word));
65
65
 
66
- const englishHead = (sentences: readonly Sentence[]): string | undefined => {
67
- const first = (sentences[0]?.tokens ?? []).find((token) => !SKIPPED_AT_END.has(token.pos));
68
- if (first === undefined || isUnsure(first) || isTitleCase(sentences[0]?.text ?? "")) return undefined;
66
+ /** A determiner that opens an object (the, a, your): no noun phrase goes on from its head with one. */
67
+ const opensObject = (token: Token | undefined): boolean => token?.features?.["PronType"] === "Art" || token?.features?.["Poss"] === "Yes";
68
+
69
+ const AFTER_ADJECTIVE = new Set(["ADP", "PART"]);
70
+
71
+ /** A noun or adjective, or an ordinary word the tagger took for a name because it is capitalised (Toggle). */
72
+ const isNounOrAdjective = (token: Token): boolean =>
73
+ token.pos === "NOUN" || token.pos === "ADJ" || (token.pos === "PROPN" && token.features?.["OrdinaryWord"] === "Yes");
74
+
75
+ /**
76
+ * The head of an item whose first two words overrule the first word's tag, or undefined when they do not. "Open the console"
77
+ * and "Press the button" open with a verb. "Clean up after closing" and "Bake in the oven" may open with a verb or an
78
+ * adjective phrase (Close to the station), and "Washing machine space" with a compound noun or a gerund and its object
79
+ * (Washing dishes): neither is judged.
80
+ */
81
+ const headFromNextWord = (first: Token, next: Token | undefined): "verb" | "unjudged" | undefined => {
82
+ if (isNounOrAdjective(first) && opensObject(next)) return "verb";
83
+ if (first.pos === "ADJ" && AFTER_ADJECTIVE.has(next?.pos ?? "")) return "unjudged";
84
+ return first.features?.["VerbForm"] === "Ger" && next?.pos === "NOUN" ? "unjudged" : undefined;
85
+ };
86
+
87
+ /** The head as the first word's tag gives it. */
88
+ const headFromTag = (first: Token): string | undefined => {
89
+ if (isUnsure(first)) return undefined;
69
90
  // A participle (Generated files, Nested lists) modifies the noun after it; the item is a noun phrase, but the tag does not say so.
70
91
  if (first.pos === "VERB") return first.features?.["VerbForm"] === "Part" ? undefined : "verb";
71
92
  return NOUN_HEAD.has(first.pos) ? "noun" : undefined;
72
93
  };
73
94
 
74
- /** An item's form on an axis, or undefined when its words do not tell. Japanese items have no head axis (the verb comes last). */
75
- export const formOf = (axis: Axis, sentences: readonly Sentence[], language: string): string | undefined => {
95
+ const englishHead = (sentences: readonly Sentence[], instructionVerbs: ReadonlySet<string>): string | undefined => {
96
+ const tokens = sentences[0]?.tokens ?? [];
97
+ const at = tokens.findIndex((token) => !SKIPPED_AT_END.has(token.pos));
98
+ const first = tokens[at];
99
+ if (at === -1 || first === undefined || isTitleCase(sentences[0]?.text ?? "")) return undefined;
100
+ // The word right after, punctuation included: in "Next, the candidate" the comma ends the opener.
101
+ const next = tokens[at + 1];
102
+ // Before a common noun the word may head a compound (Click fraud, Scroll bar), so its tag decides.
103
+ if (instructionVerbs.has(first.surface.toLowerCase()) && next?.pos !== "NOUN") return "verb";
104
+ const overruled = headFromNextWord(first, next);
105
+ if (overruled === undefined) return headFromTag(first);
106
+ return overruled === "verb" ? "verb" : undefined;
107
+ };
108
+
109
+ /**
110
+ * An item's form on an axis, or undefined when its words do not tell. Japanese items have no head axis (the verb comes last).
111
+ * instructionVerbs: verbs the tagger does not know as verbs (click), from the adapter's instruction-verb word list.
112
+ */
113
+ export const formOf = (axis: Axis, sentences: readonly Sentence[], language: string, instructionVerbs: ReadonlySet<string> = new Set()): string | undefined => {
76
114
  if (language === "ja") return axis === "ending" ? japaneseEnding(sentences) : undefined;
77
- return axis === "ending" ? englishEnding(sentences) : englishHead(sentences);
115
+ return axis === "ending" ? englishEnding(sentences) : englishHead(sentences, instructionVerbs);
78
116
  };
79
117
 
80
118
  const within = (inner: Span, outer: Span): boolean => inner.start >= outer.start && inner.end <= outer.end;
@@ -131,9 +169,10 @@ const listFindings = (doc: ProseDocument, list: BulletList, limit: number): Find
131
169
  const span = list.itemSpans[index];
132
170
  return span !== undefined && isJudged(doc.source.slice(span.start, sentences.at(-1)?.span.end ?? span.end), sentences);
133
171
  });
172
+ const instructionVerbs = new Set((doc.lexicons["instruction-verb"] ?? []).map((entry) => entry.pattern.toLowerCase()));
134
173
  return AXES.flatMap((axis) => {
135
174
  const items = judged.flatMap((sentences) => {
136
- const form = formOf(axis, sentences, doc.language);
175
+ const form = formOf(axis, sentences, doc.language, instructionVerbs);
137
176
  return form === undefined ? [] : [{ sentences, form }];
138
177
  });
139
178
  const odd = minorityWithin(items, (item) => item.form === FORMS[axis][0], limit);
@@ -1,12 +1,13 @@
1
1
  import type { Detector, Finding, ProseDocument } from "../plugin.ts";
2
2
  import { cuedNamesIn, mentionsIn, nameVariants, suffixedNamesIn, type NameMention, type VariantChars } from "../name-variants.ts";
3
+ import type { WordUse } from "../name-end-letter.ts";
3
4
  import { withKnownNeighbours } from "../name-neighbours.ts";
4
5
  import type { SpellingInput } from "../name-spelling-chars.ts";
5
6
  import { lookalikeSlipsIn, type LookalikeChars } from "../name-lookalike-chars.ts";
6
- import { readingOfWords, type CharReadings } from "../name-char-reading.ts";
7
+ import { givenNameReadingOf, readingOfWords, type CharReadings } from "../name-char-reading.ts";
7
8
  import { nameCueAt, type NameCues } from "../name-cue.ts";
8
9
  import { quoteAt } from "./structure-tree.ts";
9
- import { companyMentionsIn, companyVariants, type CompanyForm, type IsProper } from "../company-names.ts";
10
+ import { companyMentionsIn, companyVariants, type CompanyForm, type IsProper, type PredicateEnds } from "../company-names.ts";
10
11
  import { kanaSpelledCompanies, type ReadWord } from "../company-kana-spelling.ts";
11
12
  import { proseAndTablesOf } from "../table-text.ts";
12
13
  import { tableBodyCells } from "../facts/table-facts.ts";
@@ -33,6 +34,18 @@ const variantCharsOf = (doc: ProseDocument): VariantChars =>
33
34
  const charReadingsOf = (doc: ProseDocument): CharReadings =>
34
35
  new Map((doc.lexicons["name-char-reading"] ?? []).flatMap((entry): [string, string][] => (entry.group === undefined ? [] : [[entry.pattern, entry.group]])));
35
36
 
37
+ /**
38
+ * 名の中での字の読み(建太 の 建 を ケン)は、語彙表 given-name-char-reading が組(group)に言う。解析器が読めない字の読み
39
+ * (name-char-reading)も名の中で同じに読む。
40
+ */
41
+ const givenCharReadingsOf = (doc: ProseDocument, chars: CharReadings): CharReadings =>
42
+ new Map([
43
+ ...chars,
44
+ ...(doc.lexicons["given-name-char-reading"] ?? []).flatMap((entry): [string, string][] =>
45
+ entry.group === undefined ? [] : [[entry.pattern, entry.group]],
46
+ ),
47
+ ]);
48
+
36
49
  const patternsOf = (doc: ProseDocument, id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
37
50
 
38
51
  /** 会社の形の語(株式会社、Inc)は語彙表 company-form が、同じ形の組(group)と名前のどちら側に立つか(position)とともに言う。 */
@@ -124,6 +137,11 @@ const kanaStopsOf = (doc: ProseDocument): Parameters<typeof companyMentionsIn>[3
124
137
  return { particles: inGroup("particle"), openers: inGroup("opener"), ends: inGroup("end") };
125
138
  };
126
139
 
140
+ /** 品詞解析が形容詞・動詞・助動詞と読んだ語の終わり。い で終わるひらがな(ながい食品)を会社の名前の頭に入れないため。 */
141
+ const PREDICATE_POS: ReadonlySet<string> = new Set(["ADJ", "VERB", "AUX"]);
142
+ const predicateEndsOf = (doc: ProseDocument): PredicateEnds =>
143
+ new Set(tokensOf(doc).flatMap((token) => (PREDICATE_POS.has(token.pos) ? [token.span.end] : [])));
144
+
127
145
  const readWordsOf = (doc: ProseDocument): ReadWord[] =>
128
146
  tokensOf(doc).map((token) => ({ start: token.span.start, end: token.span.end, surface: token.surface, reading: token.reading }));
129
147
 
@@ -133,7 +151,7 @@ const readWordsOf = (doc: ProseDocument): ReadWord[] =>
133
151
  */
134
152
  const companyFindings = (doc: ProseDocument, prose: string, reported: readonly Reported[]): Reported[] => {
135
153
  const stops = kanaStopsOf(doc);
136
- const mentions = companyMentionsIn(prose, [...companyFormsOf(doc), ...foreignFormsOf(doc)], properOf(doc), stops);
154
+ const mentions = companyMentionsIn(prose, [...companyFormsOf(doc), ...foreignFormsOf(doc)], properOf(doc), stops, predicateEndsOf(doc));
137
155
  const variants = companyVariants(mentions).map(({ mention, usual, kind }) => ({
138
156
  offset: mention.offset,
139
157
  name: mention.surface,
@@ -220,21 +238,25 @@ const wordsCovering = (tokens: readonly Token[], startAt: ReadonlyMap<number, nu
220
238
 
221
239
  /**
222
240
  * 空白を挟んだ姓と名で、敬称か肩書きが付くか、人を書く欄にあるもの(高瀬 誠部長、氏名:森川 大輔)。語は解析器の切り方のまま、
223
- * 読みは語の読みをつなぐ。解析器の語の切れ目と姓・名の端が合わないものは読まない。
241
+ * 読みは語の読みをつなぐ。名を名の中での字の読みで読み直した読みも持つ(givenNameReading)。解析器の語の切れ目と姓・名の端が
242
+ * 合わないものは読まない。
224
243
  */
225
244
  const spacedNameMentionsOf = (doc: ProseDocument, prose: string, charReadings: CharReadings): NameMention[] => {
226
245
  const cues = { suffixes: patternsOf(doc, "person-suffix"), titles: patternsOf(doc, "person-title"), labels: patternsOf(doc, "person-label") };
227
246
  const tokens = tokensOf(doc);
228
247
  const startAt = new Map(tokens.map((token, index) => [token.span.start, index]));
248
+ const givenChars = givenCharReadingsOf(doc, charReadings);
229
249
  return spacedPersonNamesIn(prose, cues).flatMap(({ surname, given }): NameMention[] => {
230
250
  const surnameWords = wordsCovering(tokens, startAt, surname);
231
251
  const givenWords = wordsCovering(tokens, startAt, given);
232
252
  if (surnameWords === undefined || givenWords === undefined) return [];
233
253
  const words = [...surnameWords, ...givenWords];
234
254
  const surface = `${prose.slice(surname.start, surname.end)} ${prose.slice(given.start, given.end)}`;
235
- return [
236
- { surface, offset: surname.start, reading: readingOfWords(words, charReadings), words: words.map((word) => word.surface), person: true, cue: "person" },
237
- ];
255
+ const surnameReading = readingOfWords(surnameWords, charReadings);
256
+ const givenReading = givenNameReadingOf(givenWords, givenChars, charReadings);
257
+ const givenNameReading = surnameReading === undefined || givenReading === undefined ? undefined : surnameReading + givenReading;
258
+ const reading = readingOfWords(words, charReadings);
259
+ return [{ surface, offset: surname.start, reading, givenNameReading, words: words.map((word) => word.surface), person: true, cue: "person" }];
238
260
  });
239
261
  };
240
262
 
@@ -311,12 +333,27 @@ const labelledNamesOf = (doc: ProseDocument, mentions: readonly NameMention[]):
311
333
  );
312
334
  };
313
335
 
336
+ const LATIN_WORD = /^[A-Za-z]+$/u;
337
+ const LOWERCASE_WORD = /^[a-z]+$/u;
338
+
339
+ /** 文書が小文字で書くか、品詞解析が普通名詞と読む英字の語(文頭の Hurricane)。 */
340
+ const isCommonToken = (token: Token): boolean => LOWERCASE_WORD.test(token.surface) || (token.pos === "NOUN" && LATIN_WORD.test(token.surface));
341
+
342
+ /** 文書での英字の語の使い方。名前と読んだ語のうち小文字で書けばふつうの語と、普通名詞として書いた語。 */
343
+ const wordUseOf = (doc: ProseDocument): WordUse => {
344
+ const tokens = tokensOf(doc);
345
+ return {
346
+ ordinary: new Set(tokens.flatMap((token) => (isOrdinaryToken(token) ? [token.surface.toLowerCase()] : []))),
347
+ common: new Set(tokens.flatMap((token) => (isCommonToken(token) ? [token.surface.toLowerCase()] : []))),
348
+ };
349
+ };
350
+
314
351
  /** 同じ名前を、文書の中で少しだけ違う形に書いた所(GitHub と Github、山田太郎 と 山田太朗)。少ないほうを指す。 */
315
352
  export const nameVariant: Detector = (doc): Finding[] => {
316
353
  const prose = doc.prose ?? doc.source;
317
354
  const chars = variantCharsOf(doc);
318
355
  const mentions = labelledNamesOf(doc, ordinaryNamesOf(doc, nameMentionsOf(doc, prose, chars)));
319
- const variants = nameVariants(mentions, chars, spellingInputOf(doc))
356
+ const variants = nameVariants(mentions, chars, spellingInputOf(doc), wordUseOf(doc))
320
357
  .map(({ mention, usual, kind }): Reported => ({ offset: mention.offset, name: mention.surface, usual, kind }))
321
358
  .filter(notSpacingOnly);
322
359
  const lookalikes = lookalikeSlipsIn(mentions, lookalikeCharsOf(doc)).map(({ mention, usual }) => ({