chaffjs 0.29.0 → 0.31.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 (681) hide show
  1. package/dist/announced-count.d.ts +2 -0
  2. package/dist/announced-count.d.ts.map +1 -1
  3. package/dist/announced-count.js +4 -3
  4. package/dist/announced-count.js.map +1 -1
  5. package/dist/contacts/addresses.d.ts +21 -0
  6. package/dist/contacts/addresses.d.ts.map +1 -0
  7. package/dist/contacts/addresses.js +144 -0
  8. package/dist/contacts/addresses.js.map +1 -0
  9. package/dist/contacts/emails.d.ts +11 -0
  10. package/dist/contacts/emails.d.ts.map +1 -0
  11. package/dist/contacts/emails.js +51 -0
  12. package/dist/contacts/emails.js.map +1 -0
  13. package/dist/contacts/variants.d.ts +14 -0
  14. package/dist/contacts/variants.d.ts.map +1 -0
  15. package/dist/contacts/variants.js +25 -0
  16. package/dist/contacts/variants.js.map +1 -0
  17. package/dist/derived/battery-runtime.d.ts +85 -0
  18. package/dist/derived/battery-runtime.d.ts.map +1 -0
  19. package/dist/derived/battery-runtime.js +217 -0
  20. package/dist/derived/battery-runtime.js.map +1 -0
  21. package/dist/derived/period-counts.d.ts +48 -0
  22. package/dist/derived/period-counts.d.ts.map +1 -0
  23. package/dist/derived/period-counts.js +128 -0
  24. package/dist/derived/period-counts.js.map +1 -0
  25. package/dist/derived/session-hours.d.ts +26 -0
  26. package/dist/derived/session-hours.d.ts.map +1 -0
  27. package/dist/derived/session-hours.js +29 -0
  28. package/dist/derived/session-hours.js.map +1 -0
  29. package/dist/derived/time-lengths.d.ts +37 -0
  30. package/dist/derived/time-lengths.d.ts.map +1 -0
  31. package/dist/derived/time-lengths.js +171 -0
  32. package/dist/derived/time-lengths.js.map +1 -0
  33. package/dist/derived/unit-counts.d.ts +7 -0
  34. package/dist/derived/unit-counts.d.ts.map +1 -0
  35. package/dist/derived/unit-counts.js +13 -0
  36. package/dist/derived/unit-counts.js.map +1 -0
  37. package/dist/derived/use-interval.d.ts +33 -0
  38. package/dist/derived/use-interval.d.ts.map +1 -0
  39. package/dist/derived/use-interval.js +93 -0
  40. package/dist/derived/use-interval.js.map +1 -0
  41. package/dist/derived/working-hours.d.ts +36 -0
  42. package/dist/derived/working-hours.d.ts.map +1 -0
  43. package/dist/derived/working-hours.js +55 -0
  44. package/dist/derived/working-hours.js.map +1 -0
  45. package/dist/detectors/acronym-expansion.d.ts +1 -1
  46. package/dist/detectors/acronym-expansion.d.ts.map +1 -1
  47. package/dist/detectors/acronym-expansion.js +46 -2
  48. package/dist/detectors/acronym-expansion.js.map +1 -1
  49. package/dist/detectors/announced-count.d.ts.map +1 -1
  50. package/dist/detectors/announced-count.js +1 -0
  51. package/dist/detectors/announced-count.js.map +1 -1
  52. package/dist/detectors/capacity-runtime.d.ts +7 -0
  53. package/dist/detectors/capacity-runtime.d.ts.map +1 -0
  54. package/dist/detectors/capacity-runtime.js +98 -0
  55. package/dist/detectors/capacity-runtime.js.map +1 -0
  56. package/dist/detectors/capitalised-term.d.ts +44 -0
  57. package/dist/detectors/capitalised-term.d.ts.map +1 -0
  58. package/dist/detectors/capitalised-term.js +101 -0
  59. package/dist/detectors/capitalised-term.js.map +1 -0
  60. package/dist/detectors/contact-variant.d.ts +3 -0
  61. package/dist/detectors/contact-variant.d.ts.map +1 -0
  62. package/dist/detectors/contact-variant.js +21 -0
  63. package/dist/detectors/contact-variant.js.map +1 -0
  64. package/dist/detectors/count-over-capacity.d.ts +4 -0
  65. package/dist/detectors/count-over-capacity.d.ts.map +1 -0
  66. package/dist/detectors/count-over-capacity.js +26 -0
  67. package/dist/detectors/count-over-capacity.js.map +1 -0
  68. package/dist/detectors/date-outside-period.d.ts.map +1 -1
  69. package/dist/detectors/date-outside-period.js +5 -1
  70. package/dist/detectors/date-outside-period.js.map +1 -1
  71. package/dist/detectors/date-stamp-order.d.ts +3 -0
  72. package/dist/detectors/date-stamp-order.d.ts.map +1 -0
  73. package/dist/detectors/date-stamp-order.js +24 -0
  74. package/dist/detectors/date-stamp-order.js.map +1 -0
  75. package/dist/detectors/defined-term-form.d.ts.map +1 -1
  76. package/dist/detectors/defined-term-form.js +57 -7
  77. package/dist/detectors/defined-term-form.js.map +1 -1
  78. package/dist/detectors/definition-use.d.ts +1 -1
  79. package/dist/detectors/definition-use.d.ts.map +1 -1
  80. package/dist/detectors/definition-use.js +4 -2
  81. package/dist/detectors/definition-use.js.map +1 -1
  82. package/dist/detectors/derived-numbers.d.ts +11 -1
  83. package/dist/detectors/derived-numbers.d.ts.map +1 -1
  84. package/dist/detectors/derived-numbers.js +74 -20
  85. package/dist/detectors/derived-numbers.js.map +1 -1
  86. package/dist/detectors/due-date.d.ts.map +1 -1
  87. package/dist/detectors/due-date.js +6 -0
  88. package/dist/detectors/due-date.js.map +1 -1
  89. package/dist/detectors/en-shape.d.ts.map +1 -1
  90. package/dist/detectors/en-shape.js +7 -4
  91. package/dist/detectors/en-shape.js.map +1 -1
  92. package/dist/detectors/event-deadline.d.ts +3 -0
  93. package/dist/detectors/event-deadline.d.ts.map +1 -0
  94. package/dist/detectors/event-deadline.js +24 -0
  95. package/dist/detectors/event-deadline.js.map +1 -0
  96. package/dist/detectors/fact-consistency.d.ts +1 -1
  97. package/dist/detectors/fact-consistency.d.ts.map +1 -1
  98. package/dist/detectors/fact-consistency.js +52 -3
  99. package/dist/detectors/fact-consistency.js.map +1 -1
  100. package/dist/detectors/heading-case.d.ts +5 -2
  101. package/dist/detectors/heading-case.d.ts.map +1 -1
  102. package/dist/detectors/heading-case.js +12 -4
  103. package/dist/detectors/heading-case.js.map +1 -1
  104. package/dist/detectors/heading-echo.d.ts +5 -0
  105. package/dist/detectors/heading-echo.d.ts.map +1 -1
  106. package/dist/detectors/heading-echo.js +14 -1
  107. package/dist/detectors/heading-echo.js.map +1 -1
  108. package/dist/detectors/layout-code.d.ts +5 -0
  109. package/dist/detectors/layout-code.d.ts.map +1 -0
  110. package/dist/detectors/layout-code.js +124 -0
  111. package/dist/detectors/layout-code.js.map +1 -0
  112. package/dist/detectors/measured-facts.d.ts +8 -0
  113. package/dist/detectors/measured-facts.d.ts.map +1 -0
  114. package/dist/detectors/measured-facts.js +18 -0
  115. package/dist/detectors/measured-facts.js.map +1 -0
  116. package/dist/detectors/name-variant-model-codes.d.ts +11 -0
  117. package/dist/detectors/name-variant-model-codes.d.ts.map +1 -0
  118. package/dist/detectors/name-variant-model-codes.js +21 -0
  119. package/dist/detectors/name-variant-model-codes.js.map +1 -0
  120. package/dist/detectors/name-variant.d.ts.map +1 -1
  121. package/dist/detectors/name-variant.js +78 -10
  122. package/dist/detectors/name-variant.js.map +1 -1
  123. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  124. package/dist/detectors/orthographic-variant.js +62 -7
  125. package/dist/detectors/orthographic-variant.js.map +1 -1
  126. package/dist/detectors/orthography.d.ts.map +1 -1
  127. package/dist/detectors/orthography.js +12 -6
  128. package/dist/detectors/orthography.js.map +1 -1
  129. package/dist/detectors/party-order.d.ts +3 -0
  130. package/dist/detectors/party-order.d.ts.map +1 -0
  131. package/dist/detectors/party-order.js +46 -0
  132. package/dist/detectors/party-order.js.map +1 -0
  133. package/dist/detectors/quantity-range.d.ts +3 -0
  134. package/dist/detectors/quantity-range.d.ts.map +1 -0
  135. package/dist/detectors/quantity-range.js +35 -0
  136. package/dist/detectors/quantity-range.js.map +1 -0
  137. package/dist/detectors/range-band.d.ts +5 -0
  138. package/dist/detectors/range-band.d.ts.map +1 -0
  139. package/dist/detectors/range-band.js +80 -0
  140. package/dist/detectors/range-band.js.map +1 -0
  141. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts +3 -0
  142. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts.map +1 -0
  143. package/dist/detectors/registry/capacity-runtime-mismatch.js +3 -0
  144. package/dist/detectors/registry/capacity-runtime-mismatch.js.map +1 -0
  145. package/dist/detectors/registry/connection-time-short.d.ts +3 -0
  146. package/dist/detectors/registry/connection-time-short.d.ts.map +1 -0
  147. package/dist/detectors/registry/connection-time-short.js +3 -0
  148. package/dist/detectors/registry/connection-time-short.js.map +1 -0
  149. package/dist/detectors/registry/contact-variant.d.ts +3 -0
  150. package/dist/detectors/registry/contact-variant.d.ts.map +1 -0
  151. package/dist/detectors/registry/contact-variant.js +3 -0
  152. package/dist/detectors/registry/contact-variant.js.map +1 -0
  153. package/dist/detectors/registry/count-over-capacity.d.ts +3 -0
  154. package/dist/detectors/registry/count-over-capacity.d.ts.map +1 -0
  155. package/dist/detectors/registry/count-over-capacity.js +3 -0
  156. package/dist/detectors/registry/count-over-capacity.js.map +1 -0
  157. package/dist/detectors/registry/date-stamp-order.d.ts +3 -0
  158. package/dist/detectors/registry/date-stamp-order.d.ts.map +1 -0
  159. package/dist/detectors/registry/date-stamp-order.js +3 -0
  160. package/dist/detectors/registry/date-stamp-order.js.map +1 -0
  161. package/dist/detectors/registry/duration-product-mismatch.d.ts +3 -0
  162. package/dist/detectors/registry/duration-product-mismatch.d.ts.map +1 -0
  163. package/dist/detectors/registry/duration-product-mismatch.js +3 -0
  164. package/dist/detectors/registry/duration-product-mismatch.js.map +1 -0
  165. package/dist/detectors/registry/event-deadline.d.ts +3 -0
  166. package/dist/detectors/registry/event-deadline.d.ts.map +1 -0
  167. package/dist/detectors/registry/event-deadline.js +3 -0
  168. package/dist/detectors/registry/event-deadline.js.map +1 -0
  169. package/dist/detectors/registry/interval-count-mismatch.d.ts +3 -0
  170. package/dist/detectors/registry/interval-count-mismatch.d.ts.map +1 -0
  171. package/dist/detectors/registry/interval-count-mismatch.js +3 -0
  172. package/dist/detectors/registry/interval-count-mismatch.js.map +1 -0
  173. package/dist/detectors/registry/layout-code-mismatch.d.ts +3 -0
  174. package/dist/detectors/registry/layout-code-mismatch.d.ts.map +1 -0
  175. package/dist/detectors/registry/layout-code-mismatch.js +3 -0
  176. package/dist/detectors/registry/layout-code-mismatch.js.map +1 -0
  177. package/dist/detectors/registry/party-order.d.ts +3 -0
  178. package/dist/detectors/registry/party-order.d.ts.map +1 -0
  179. package/dist/detectors/registry/party-order.js +3 -0
  180. package/dist/detectors/registry/party-order.js.map +1 -0
  181. package/dist/detectors/registry/quantity-range.d.ts +3 -0
  182. package/dist/detectors/registry/quantity-range.d.ts.map +1 -0
  183. package/dist/detectors/registry/quantity-range.js +3 -0
  184. package/dist/detectors/registry/quantity-range.js.map +1 -0
  185. package/dist/detectors/registry/range-band-mismatch.d.ts +3 -0
  186. package/dist/detectors/registry/range-band-mismatch.d.ts.map +1 -0
  187. package/dist/detectors/registry/range-band-mismatch.js +3 -0
  188. package/dist/detectors/registry/range-band-mismatch.js.map +1 -0
  189. package/dist/detectors/registry/rent-multiple-mismatch.d.ts +3 -0
  190. package/dist/detectors/registry/rent-multiple-mismatch.d.ts.map +1 -0
  191. package/dist/detectors/registry/rent-multiple-mismatch.js +3 -0
  192. package/dist/detectors/registry/rent-multiple-mismatch.js.map +1 -0
  193. package/dist/detectors/registry/requirement-lists.d.ts +3 -0
  194. package/dist/detectors/registry/requirement-lists.d.ts.map +1 -0
  195. package/dist/detectors/registry/requirement-lists.js +3 -0
  196. package/dist/detectors/registry/requirement-lists.js.map +1 -0
  197. package/dist/detectors/registry/return-type.d.ts +3 -0
  198. package/dist/detectors/registry/return-type.d.ts.map +1 -0
  199. package/dist/detectors/registry/return-type.js +3 -0
  200. package/dist/detectors/registry/return-type.js.map +1 -0
  201. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts +3 -0
  202. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts.map +1 -0
  203. package/dist/detectors/registry/scaled-quantity-mismatch.js +3 -0
  204. package/dist/detectors/registry/scaled-quantity-mismatch.js.map +1 -0
  205. package/dist/detectors/registry/step-time-sum-mismatch.d.ts +3 -0
  206. package/dist/detectors/registry/step-time-sum-mismatch.d.ts.map +1 -0
  207. package/dist/detectors/registry/step-time-sum-mismatch.js +3 -0
  208. package/dist/detectors/registry/step-time-sum-mismatch.js.map +1 -0
  209. package/dist/detectors/registry/time-range-reversed.d.ts +3 -0
  210. package/dist/detectors/registry/time-range-reversed.d.ts.map +1 -0
  211. package/dist/detectors/registry/time-range-reversed.js +3 -0
  212. package/dist/detectors/registry/time-range-reversed.js.map +1 -0
  213. package/dist/detectors/registry/unlisted-item-used.d.ts +3 -0
  214. package/dist/detectors/registry/unlisted-item-used.d.ts.map +1 -0
  215. package/dist/detectors/registry/unlisted-item-used.js +3 -0
  216. package/dist/detectors/registry/unlisted-item-used.js.map +1 -0
  217. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts +3 -0
  218. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts.map +1 -0
  219. package/dist/detectors/registry/walk-time-distance-mismatch.js +3 -0
  220. package/dist/detectors/registry/walk-time-distance-mismatch.js.map +1 -0
  221. package/dist/detectors/rent-multiple-mismatch.d.ts +3 -0
  222. package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -0
  223. package/dist/detectors/rent-multiple-mismatch.js +39 -0
  224. package/dist/detectors/rent-multiple-mismatch.js.map +1 -0
  225. package/dist/detectors/requirement-lists.d.ts +3 -0
  226. package/dist/detectors/requirement-lists.d.ts.map +1 -0
  227. package/dist/detectors/requirement-lists.js +46 -0
  228. package/dist/detectors/requirement-lists.js.map +1 -0
  229. package/dist/detectors/return-type.d.ts +3 -0
  230. package/dist/detectors/return-type.d.ts.map +1 -0
  231. package/dist/detectors/return-type.js +24 -0
  232. package/dist/detectors/return-type.js.map +1 -0
  233. package/dist/detectors/scaled-quantity.d.ts +3 -0
  234. package/dist/detectors/scaled-quantity.d.ts.map +1 -0
  235. package/dist/detectors/scaled-quantity.js +37 -0
  236. package/dist/detectors/scaled-quantity.js.map +1 -0
  237. package/dist/detectors/signals.d.ts.map +1 -1
  238. package/dist/detectors/signals.js +3 -1
  239. package/dist/detectors/signals.js.map +1 -1
  240. package/dist/detectors/step-time-sum.d.ts +3 -0
  241. package/dist/detectors/step-time-sum.d.ts.map +1 -0
  242. package/dist/detectors/step-time-sum.js +46 -0
  243. package/dist/detectors/step-time-sum.js.map +1 -0
  244. package/dist/detectors/structure-tree.d.ts +2 -0
  245. package/dist/detectors/structure-tree.d.ts.map +1 -1
  246. package/dist/detectors/structure-tree.js +31 -6
  247. package/dist/detectors/structure-tree.js.map +1 -1
  248. package/dist/detectors/structure.js +1 -1
  249. package/dist/detectors/structure.js.map +1 -1
  250. package/dist/detectors/technical-docs.d.ts +5 -0
  251. package/dist/detectors/technical-docs.d.ts.map +1 -1
  252. package/dist/detectors/technical-docs.js +12 -0
  253. package/dist/detectors/technical-docs.js.map +1 -1
  254. package/dist/detectors/time-order.d.ts +2 -0
  255. package/dist/detectors/time-order.d.ts.map +1 -1
  256. package/dist/detectors/time-order.js +33 -5
  257. package/dist/detectors/time-order.js.map +1 -1
  258. package/dist/detectors/undefined-term.d.ts.map +1 -1
  259. package/dist/detectors/undefined-term.js +19 -3
  260. package/dist/detectors/undefined-term.js.map +1 -1
  261. package/dist/detectors/unit-consistency.d.ts +1 -1
  262. package/dist/detectors/unit-consistency.d.ts.map +1 -1
  263. package/dist/detectors/unit-consistency.js +46 -38
  264. package/dist/detectors/unit-consistency.js.map +1 -1
  265. package/dist/detectors/unlisted-items.d.ts +3 -0
  266. package/dist/detectors/unlisted-items.d.ts.map +1 -0
  267. package/dist/detectors/unlisted-items.js +57 -0
  268. package/dist/detectors/unlisted-items.js.map +1 -0
  269. package/dist/detectors/use-count-words.d.ts +8 -0
  270. package/dist/detectors/use-count-words.d.ts.map +1 -0
  271. package/dist/detectors/use-count-words.js +20 -0
  272. package/dist/detectors/use-count-words.js.map +1 -0
  273. package/dist/detectors/use-interval.d.ts +3 -0
  274. package/dist/detectors/use-interval.d.ts.map +1 -0
  275. package/dist/detectors/use-interval.js +39 -0
  276. package/dist/detectors/use-interval.js.map +1 -0
  277. package/dist/detectors/walk-time-distance-mismatch.d.ts +4 -0
  278. package/dist/detectors/walk-time-distance-mismatch.d.ts.map +1 -0
  279. package/dist/detectors/walk-time-distance-mismatch.js +38 -0
  280. package/dist/detectors/walk-time-distance-mismatch.js.map +1 -0
  281. package/dist/document.d.ts.map +1 -1
  282. package/dist/document.js +4 -1
  283. package/dist/document.js.map +1 -1
  284. package/dist/facts/approximate-tolerance.d.ts +7 -0
  285. package/dist/facts/approximate-tolerance.d.ts.map +1 -0
  286. package/dist/facts/approximate-tolerance.js +16 -0
  287. package/dist/facts/approximate-tolerance.js.map +1 -0
  288. package/dist/facts/item-amounts.d.ts +18 -0
  289. package/dist/facts/item-amounts.d.ts.map +1 -0
  290. package/dist/facts/item-amounts.js +67 -0
  291. package/dist/facts/item-amounts.js.map +1 -0
  292. package/dist/facts/labelled-facts.d.ts +4 -0
  293. package/dist/facts/labelled-facts.d.ts.map +1 -1
  294. package/dist/facts/labelled-facts.js +21 -6
  295. package/dist/facts/labelled-facts.js.map +1 -1
  296. package/dist/facts/measure-units.d.ts +5 -0
  297. package/dist/facts/measure-units.d.ts.map +1 -0
  298. package/dist/facts/measure-units.js +28 -0
  299. package/dist/facts/measure-units.js.map +1 -0
  300. package/dist/facts/measures.d.ts +23 -4
  301. package/dist/facts/measures.d.ts.map +1 -1
  302. package/dist/facts/measures.js +68 -19
  303. package/dist/facts/measures.js.map +1 -1
  304. package/dist/facts/retention-periods.d.ts +51 -0
  305. package/dist/facts/retention-periods.d.ts.map +1 -0
  306. package/dist/facts/retention-periods.js +148 -0
  307. package/dist/facts/retention-periods.js.map +1 -0
  308. package/dist/facts/table-facts.d.ts +2 -0
  309. package/dist/facts/table-facts.d.ts.map +1 -1
  310. package/dist/facts/table-facts.js +2 -0
  311. package/dist/facts/table-facts.js.map +1 -1
  312. package/dist/facts/unit-conflicts.d.ts +3 -2
  313. package/dist/facts/unit-conflicts.d.ts.map +1 -1
  314. package/dist/facts/unit-conflicts.js +15 -7
  315. package/dist/facts/unit-conflicts.js.map +1 -1
  316. package/dist/facts/unit-equivalents.d.ts +13 -0
  317. package/dist/facts/unit-equivalents.d.ts.map +1 -0
  318. package/dist/facts/unit-equivalents.js +56 -0
  319. package/dist/facts/unit-equivalents.js.map +1 -0
  320. package/dist/facts/unit-tolerance.d.ts +4 -0
  321. package/dist/facts/unit-tolerance.d.ts.map +1 -0
  322. package/dist/facts/unit-tolerance.js +13 -0
  323. package/dist/facts/unit-tolerance.js.map +1 -0
  324. package/dist/facts/unit-word.d.ts +8 -0
  325. package/dist/facts/unit-word.d.ts.map +1 -0
  326. package/dist/facts/unit-word.js +34 -0
  327. package/dist/facts/unit-word.js.map +1 -0
  328. package/dist/facts/written-precision.d.ts +10 -0
  329. package/dist/facts/written-precision.d.ts.map +1 -0
  330. package/dist/facts/written-precision.js +20 -0
  331. package/dist/facts/written-precision.js.map +1 -0
  332. package/dist/kana-spelling.d.ts +12 -0
  333. package/dist/kana-spelling.d.ts.map +1 -1
  334. package/dist/kana-spelling.js +23 -0
  335. package/dist/kana-spelling.js.map +1 -1
  336. package/dist/model-codes.d.ts +23 -0
  337. package/dist/model-codes.d.ts.map +1 -0
  338. package/dist/model-codes.js +81 -0
  339. package/dist/model-codes.js.map +1 -0
  340. package/dist/name-spelling-chars.d.ts +16 -0
  341. package/dist/name-spelling-chars.d.ts.map +1 -0
  342. package/dist/name-spelling-chars.js +14 -0
  343. package/dist/name-spelling-chars.js.map +1 -0
  344. package/dist/name-variants.d.ts +8 -2
  345. package/dist/name-variants.d.ts.map +1 -1
  346. package/dist/name-variants.js +72 -7
  347. package/dist/name-variants.js.map +1 -1
  348. package/dist/name-word-order.d.ts +49 -0
  349. package/dist/name-word-order.d.ts.map +1 -0
  350. package/dist/name-word-order.js +114 -0
  351. package/dist/name-word-order.js.map +1 -0
  352. package/dist/phone-numbers.d.ts.map +1 -1
  353. package/dist/phone-numbers.js +22 -5
  354. package/dist/phone-numbers.js.map +1 -1
  355. package/dist/place-names.d.ts +12 -5
  356. package/dist/place-names.d.ts.map +1 -1
  357. package/dist/place-names.js +34 -14
  358. package/dist/place-names.js.map +1 -1
  359. package/dist/plugin.d.ts +12 -0
  360. package/dist/plugin.d.ts.map +1 -1
  361. package/dist/preamble-paragraphs.d.ts +2 -1
  362. package/dist/preamble-paragraphs.d.ts.map +1 -1
  363. package/dist/preamble-paragraphs.js +16 -1
  364. package/dist/preamble-paragraphs.js.map +1 -1
  365. package/dist/profile/parse.d.ts.map +1 -1
  366. package/dist/profile/parse.js +11 -0
  367. package/dist/profile/parse.js.map +1 -1
  368. package/dist/return-type.d.ts +33 -0
  369. package/dist/return-type.d.ts.map +1 -0
  370. package/dist/return-type.js +302 -0
  371. package/dist/return-type.js.map +1 -0
  372. package/dist/spacing-code.d.ts +6 -0
  373. package/dist/spacing-code.d.ts.map +1 -0
  374. package/dist/spacing-code.js +16 -0
  375. package/dist/spacing-code.js.map +1 -0
  376. package/dist/structure/amount-range.d.ts +2 -2
  377. package/dist/structure/amount-range.d.ts.map +1 -1
  378. package/dist/structure/amount-range.js +12 -5
  379. package/dist/structure/amount-range.js.map +1 -1
  380. package/dist/structure/capacity-count.d.ts +59 -0
  381. package/dist/structure/capacity-count.d.ts.map +1 -0
  382. package/dist/structure/capacity-count.js +118 -0
  383. package/dist/structure/capacity-count.js.map +1 -0
  384. package/dist/structure/connection-times.d.ts +56 -0
  385. package/dist/structure/connection-times.d.ts.map +1 -0
  386. package/dist/structure/connection-times.js +165 -0
  387. package/dist/structure/connection-times.js.map +1 -0
  388. package/dist/structure/counted-amounts.d.ts +6 -0
  389. package/dist/structure/counted-amounts.d.ts.map +1 -1
  390. package/dist/structure/counted-amounts.js +4 -4
  391. package/dist/structure/counted-amounts.js.map +1 -1
  392. package/dist/structure/date-order-tie.d.ts +13 -0
  393. package/dist/structure/date-order-tie.d.ts.map +1 -0
  394. package/dist/structure/date-order-tie.js +61 -0
  395. package/dist/structure/date-order-tie.js.map +1 -0
  396. package/dist/structure/date-order.d.ts +2 -2
  397. package/dist/structure/date-order.d.ts.map +1 -1
  398. package/dist/structure/date-order.js +26 -9
  399. package/dist/structure/date-order.js.map +1 -1
  400. package/dist/structure/date-outside-period.d.ts +8 -0
  401. package/dist/structure/date-outside-period.d.ts.map +1 -1
  402. package/dist/structure/date-outside-period.js +52 -3
  403. package/dist/structure/date-outside-period.js.map +1 -1
  404. package/dist/structure/date-range.d.ts +6 -2
  405. package/dist/structure/date-range.d.ts.map +1 -1
  406. package/dist/structure/date-range.js +37 -9
  407. package/dist/structure/date-range.js.map +1 -1
  408. package/dist/structure/deadline-after-event.d.ts +23 -0
  409. package/dist/structure/deadline-after-event.d.ts.map +1 -0
  410. package/dist/structure/deadline-after-event.js +42 -0
  411. package/dist/structure/deadline-after-event.js.map +1 -0
  412. package/dist/structure/definition-use.d.ts +1 -1
  413. package/dist/structure/definition-use.d.ts.map +1 -1
  414. package/dist/structure/definition-use.js +55 -2
  415. package/dist/structure/definition-use.js.map +1 -1
  416. package/dist/structure/due-date.d.ts.map +1 -1
  417. package/dist/structure/due-date.js +5 -27
  418. package/dist/structure/due-date.js.map +1 -1
  419. package/dist/structure/labelled-date-order.d.ts +38 -0
  420. package/dist/structure/labelled-date-order.d.ts.map +1 -0
  421. package/dist/structure/labelled-date-order.js +41 -0
  422. package/dist/structure/labelled-date-order.js.map +1 -0
  423. package/dist/structure/layout-codes.d.ts +68 -0
  424. package/dist/structure/layout-codes.d.ts.map +1 -0
  425. package/dist/structure/layout-codes.js +161 -0
  426. package/dist/structure/layout-codes.js.map +1 -0
  427. package/dist/structure/leg-times.d.ts +2 -0
  428. package/dist/structure/leg-times.d.ts.map +1 -1
  429. package/dist/structure/leg-times.js +13 -0
  430. package/dist/structure/leg-times.js.map +1 -1
  431. package/dist/structure/measured-amounts.d.ts +17 -0
  432. package/dist/structure/measured-amounts.d.ts.map +1 -0
  433. package/dist/structure/measured-amounts.js +110 -0
  434. package/dist/structure/measured-amounts.js.map +1 -0
  435. package/dist/structure/party-order.d.ts +33 -0
  436. package/dist/structure/party-order.d.ts.map +1 -0
  437. package/dist/structure/party-order.js +79 -0
  438. package/dist/structure/party-order.js.map +1 -0
  439. package/dist/structure/quantity-range.d.ts +8 -0
  440. package/dist/structure/quantity-range.d.ts.map +1 -0
  441. package/dist/structure/quantity-range.js +36 -0
  442. package/dist/structure/quantity-range.js.map +1 -0
  443. package/dist/structure/range-bands.d.ts +75 -0
  444. package/dist/structure/range-bands.d.ts.map +1 -0
  445. package/dist/structure/range-bands.js +244 -0
  446. package/dist/structure/range-bands.js.map +1 -0
  447. package/dist/structure/rent-multiple.d.ts +76 -0
  448. package/dist/structure/rent-multiple.d.ts.map +1 -0
  449. package/dist/structure/rent-multiple.js +186 -0
  450. package/dist/structure/rent-multiple.js.map +1 -0
  451. package/dist/structure/requirement-lists.d.ts +37 -0
  452. package/dist/structure/requirement-lists.d.ts.map +1 -0
  453. package/dist/structure/requirement-lists.js +89 -0
  454. package/dist/structure/requirement-lists.js.map +1 -0
  455. package/dist/structure/scaled-quantities.d.ts +50 -0
  456. package/dist/structure/scaled-quantities.d.ts.map +1 -0
  457. package/dist/structure/scaled-quantities.js +153 -0
  458. package/dist/structure/scaled-quantities.js.map +1 -0
  459. package/dist/structure/stamp-order.d.ts +16 -0
  460. package/dist/structure/stamp-order.d.ts.map +1 -0
  461. package/dist/structure/stamp-order.js +78 -0
  462. package/dist/structure/stamp-order.js.map +1 -0
  463. package/dist/structure/step-times.d.ts +49 -0
  464. package/dist/structure/step-times.d.ts.map +1 -0
  465. package/dist/structure/step-times.js +128 -0
  466. package/dist/structure/step-times.js.map +1 -0
  467. package/dist/structure/term-deadline.d.ts +19 -0
  468. package/dist/structure/term-deadline.d.ts.map +1 -0
  469. package/dist/structure/term-deadline.js +28 -0
  470. package/dist/structure/term-deadline.js.map +1 -0
  471. package/dist/structure/time-order.d.ts +21 -0
  472. package/dist/structure/time-order.d.ts.map +1 -1
  473. package/dist/structure/time-order.js +5 -6
  474. package/dist/structure/time-order.js.map +1 -1
  475. package/dist/structure/time-range.d.ts +16 -0
  476. package/dist/structure/time-range.d.ts.map +1 -0
  477. package/dist/structure/time-range.js +57 -0
  478. package/dist/structure/time-range.js.map +1 -0
  479. package/dist/structure/total-line.d.ts +16 -0
  480. package/dist/structure/total-line.d.ts.map +1 -0
  481. package/dist/structure/total-line.js +44 -0
  482. package/dist/structure/total-line.js.map +1 -0
  483. package/dist/structure/total.d.ts +7 -1
  484. package/dist/structure/total.d.ts.map +1 -1
  485. package/dist/structure/total.js +28 -5
  486. package/dist/structure/total.js.map +1 -1
  487. package/dist/structure/unlisted-items.d.ts +70 -0
  488. package/dist/structure/unlisted-items.d.ts.map +1 -0
  489. package/dist/structure/unlisted-items.js +152 -0
  490. package/dist/structure/unlisted-items.js.map +1 -0
  491. package/dist/structure/version-order.d.ts +2 -0
  492. package/dist/structure/version-order.d.ts.map +1 -1
  493. package/dist/structure/version-order.js +10 -0
  494. package/dist/structure/version-order.js.map +1 -1
  495. package/dist/structure/walk-time.d.ts +87 -0
  496. package/dist/structure/walk-time.d.ts.map +1 -0
  497. package/dist/structure/walk-time.js +195 -0
  498. package/dist/structure/walk-time.js.map +1 -0
  499. package/dist/surname-near.d.ts +6 -0
  500. package/dist/surname-near.d.ts.map +1 -0
  501. package/dist/surname-near.js +31 -0
  502. package/dist/surname-near.js.map +1 -0
  503. package/dist/table-cells.d.ts +13 -0
  504. package/dist/table-cells.d.ts.map +1 -0
  505. package/dist/table-cells.js +36 -0
  506. package/dist/table-cells.js.map +1 -0
  507. package/dist/table-names.d.ts +42 -0
  508. package/dist/table-names.d.ts.map +1 -0
  509. package/dist/table-names.js +94 -0
  510. package/dist/table-names.js.map +1 -0
  511. package/package.json +3 -3
  512. package/profiles/listing.yaml +20 -0
  513. package/rules/announced-count-mismatch.yaml +2 -2
  514. package/rules/capacity-runtime-mismatch.yaml +66 -0
  515. package/rules/connection-time-short.yaml +52 -0
  516. package/rules/contact-variant.yaml +76 -0
  517. package/rules/count-over-capacity.yaml +55 -0
  518. package/rules/date-outside-period.yaml +4 -4
  519. package/rules/date-stamp-order.yaml +82 -0
  520. package/rules/deadline-after-event.yaml +67 -0
  521. package/rules/defined-name-repeated.yaml +2 -2
  522. package/rules/due-before-issue.yaml +11 -7
  523. package/rules/duration-mismatch.yaml +7 -4
  524. package/rules/duration-product-mismatch.yaml +73 -0
  525. package/rules/fact-conflict.yaml +9 -4
  526. package/rules/heading-echo.yaml +2 -2
  527. package/rules/interval-count-mismatch.yaml +54 -0
  528. package/rules/latin-spacing.yaml +2 -2
  529. package/rules/layout-code-mismatch.yaml +68 -0
  530. package/rules/name-variant.yaml +14 -2
  531. package/rules/no-em-dash.yaml +2 -2
  532. package/rules/party-order.yaml +84 -0
  533. package/rules/preamble-length.yaml +2 -2
  534. package/rules/quantity-range-reversed.yaml +53 -0
  535. package/rules/range-band-mismatch.yaml +70 -0
  536. package/rules/rent-multiple-mismatch.yaml +61 -0
  537. package/rules/required-listed-as-preferred.yaml +102 -0
  538. package/rules/return-type-mismatch.yaml +79 -0
  539. package/rules/scaled-quantity-mismatch.yaml +69 -0
  540. package/rules/step-time-sum-mismatch.yaml +65 -0
  541. package/rules/time-range-reversed.yaml +48 -0
  542. package/rules/title-case-consistency.yaml +2 -2
  543. package/rules/total-mismatch.yaml +2 -2
  544. package/rules/undefined-term.yaml +21 -7
  545. package/rules/unit-mismatch.yaml +12 -4
  546. package/rules/unlisted-item-used.yaml +90 -0
  547. package/rules/unused-definition.yaml +2 -2
  548. package/rules/use-before-definition.yaml +2 -2
  549. package/rules/walk-time-distance-mismatch.yaml +55 -0
  550. package/src/announced-count.ts +6 -3
  551. package/src/contacts/addresses.ts +179 -0
  552. package/src/contacts/emails.ts +63 -0
  553. package/src/contacts/variants.ts +33 -0
  554. package/src/derived/battery-runtime.ts +284 -0
  555. package/src/derived/period-counts.ts +178 -0
  556. package/src/derived/session-hours.ts +62 -0
  557. package/src/derived/time-lengths.ts +229 -0
  558. package/src/derived/unit-counts.ts +18 -0
  559. package/src/derived/use-interval.ts +151 -0
  560. package/src/derived/working-hours.ts +97 -0
  561. package/src/detectors/acronym-expansion.ts +57 -2
  562. package/src/detectors/announced-count.ts +1 -0
  563. package/src/detectors/capacity-runtime.ts +128 -0
  564. package/src/detectors/capitalised-term.ts +147 -0
  565. package/src/detectors/contact-variant.ts +24 -0
  566. package/src/detectors/count-over-capacity.ts +37 -0
  567. package/src/detectors/date-outside-period.ts +5 -1
  568. package/src/detectors/date-stamp-order.ts +28 -0
  569. package/src/detectors/defined-term-form.ts +63 -7
  570. package/src/detectors/definition-use.ts +6 -2
  571. package/src/detectors/derived-numbers.ts +97 -24
  572. package/src/detectors/due-date.ts +10 -2
  573. package/src/detectors/en-shape.ts +11 -4
  574. package/src/detectors/event-deadline.ts +29 -0
  575. package/src/detectors/fact-consistency.ts +64 -3
  576. package/src/detectors/heading-case.ts +13 -4
  577. package/src/detectors/heading-echo.ts +17 -1
  578. package/src/detectors/layout-code.ts +174 -0
  579. package/src/detectors/measured-facts.ts +20 -0
  580. package/src/detectors/name-variant-model-codes.ts +27 -0
  581. package/src/detectors/name-variant.ts +99 -15
  582. package/src/detectors/orthographic-variant.ts +78 -9
  583. package/src/detectors/orthography.ts +16 -6
  584. package/src/detectors/party-order.ts +50 -0
  585. package/src/detectors/quantity-range.ts +47 -0
  586. package/src/detectors/range-band.ts +125 -0
  587. package/src/detectors/registry/capacity-runtime-mismatch.ts +4 -0
  588. package/src/detectors/registry/connection-time-short.ts +4 -0
  589. package/src/detectors/registry/contact-variant.ts +4 -0
  590. package/src/detectors/registry/count-over-capacity.ts +4 -0
  591. package/src/detectors/registry/date-stamp-order.ts +4 -0
  592. package/src/detectors/registry/duration-product-mismatch.ts +4 -0
  593. package/src/detectors/registry/event-deadline.ts +4 -0
  594. package/src/detectors/registry/interval-count-mismatch.ts +4 -0
  595. package/src/detectors/registry/layout-code-mismatch.ts +4 -0
  596. package/src/detectors/registry/party-order.ts +4 -0
  597. package/src/detectors/registry/quantity-range.ts +4 -0
  598. package/src/detectors/registry/range-band-mismatch.ts +4 -0
  599. package/src/detectors/registry/rent-multiple-mismatch.ts +4 -0
  600. package/src/detectors/registry/requirement-lists.ts +4 -0
  601. package/src/detectors/registry/return-type.ts +4 -0
  602. package/src/detectors/registry/scaled-quantity-mismatch.ts +4 -0
  603. package/src/detectors/registry/step-time-sum-mismatch.ts +4 -0
  604. package/src/detectors/registry/time-range-reversed.ts +4 -0
  605. package/src/detectors/registry/unlisted-item-used.ts +4 -0
  606. package/src/detectors/registry/walk-time-distance-mismatch.ts +4 -0
  607. package/src/detectors/rent-multiple-mismatch.ts +56 -0
  608. package/src/detectors/requirement-lists.ts +65 -0
  609. package/src/detectors/return-type.ts +30 -0
  610. package/src/detectors/scaled-quantity.ts +49 -0
  611. package/src/detectors/signals.ts +3 -1
  612. package/src/detectors/step-time-sum.ts +55 -0
  613. package/src/detectors/structure-tree.ts +42 -6
  614. package/src/detectors/structure.ts +1 -1
  615. package/src/detectors/technical-docs.ts +12 -0
  616. package/src/detectors/time-order.ts +45 -6
  617. package/src/detectors/undefined-term.ts +20 -3
  618. package/src/detectors/unit-consistency.ts +50 -38
  619. package/src/detectors/unlisted-items.ts +72 -0
  620. package/src/detectors/use-count-words.ts +32 -0
  621. package/src/detectors/use-interval.ts +51 -0
  622. package/src/detectors/walk-time-distance-mismatch.ts +45 -0
  623. package/src/document.ts +4 -1
  624. package/src/facts/approximate-tolerance.ts +19 -0
  625. package/src/facts/item-amounts.ts +94 -0
  626. package/src/facts/labelled-facts.ts +29 -7
  627. package/src/facts/measure-units.ts +31 -0
  628. package/src/facts/measures.ts +113 -25
  629. package/src/facts/retention-periods.ts +216 -0
  630. package/src/facts/table-facts.ts +3 -0
  631. package/src/facts/unit-conflicts.ts +22 -9
  632. package/src/facts/unit-equivalents.ts +70 -0
  633. package/src/facts/unit-tolerance.ts +17 -0
  634. package/src/facts/unit-word.ts +35 -0
  635. package/src/facts/written-precision.ts +23 -0
  636. package/src/kana-spelling.ts +25 -0
  637. package/src/model-codes.ts +104 -0
  638. package/src/name-spelling-chars.ts +34 -0
  639. package/src/name-variants.ts +83 -7
  640. package/src/name-word-order.ts +149 -0
  641. package/src/phone-numbers.ts +26 -6
  642. package/src/place-names.ts +53 -14
  643. package/src/plugin.ts +7 -0
  644. package/src/preamble-paragraphs.ts +21 -2
  645. package/src/profile/parse.ts +10 -0
  646. package/src/return-type.ts +361 -0
  647. package/src/spacing-code.ts +26 -0
  648. package/src/structure/amount-range.ts +16 -7
  649. package/src/structure/capacity-count.ts +196 -0
  650. package/src/structure/connection-times.ts +226 -0
  651. package/src/structure/counted-amounts.ts +5 -4
  652. package/src/structure/date-order-tie.ts +86 -0
  653. package/src/structure/date-order.ts +41 -14
  654. package/src/structure/date-outside-period.ts +77 -7
  655. package/src/structure/date-range.ts +45 -9
  656. package/src/structure/deadline-after-event.ts +67 -0
  657. package/src/structure/definition-use.ts +59 -2
  658. package/src/structure/due-date.ts +8 -32
  659. package/src/structure/labelled-date-order.ts +84 -0
  660. package/src/structure/layout-codes.ts +210 -0
  661. package/src/structure/leg-times.ts +15 -0
  662. package/src/structure/measured-amounts.ts +138 -0
  663. package/src/structure/party-order.ts +115 -0
  664. package/src/structure/quantity-range.ts +54 -0
  665. package/src/structure/range-bands.ts +316 -0
  666. package/src/structure/rent-multiple.ts +277 -0
  667. package/src/structure/requirement-lists.ts +145 -0
  668. package/src/structure/scaled-quantities.ts +225 -0
  669. package/src/structure/stamp-order.ts +100 -0
  670. package/src/structure/step-times.ts +169 -0
  671. package/src/structure/term-deadline.ts +35 -0
  672. package/src/structure/time-order.ts +8 -7
  673. package/src/structure/time-range.ts +64 -0
  674. package/src/structure/total-line.ts +63 -0
  675. package/src/structure/total.ts +41 -6
  676. package/src/structure/unlisted-items.ts +207 -0
  677. package/src/structure/version-order.ts +10 -0
  678. package/src/structure/walk-time.ts +285 -0
  679. package/src/surname-near.ts +31 -0
  680. package/src/table-cells.ts +41 -0
  681. package/src/table-names.ts +113 -0
@@ -0,0 +1,207 @@
1
+ // unlisted-item-used: a numbered step that uses an ingredient or a tool the document's own list of what is needed (材料,
2
+ // 用意するもの, Ingredients, What you need) does not name. Only a noun in a use frame is read: the object of a use verb
3
+ // (塩を加える, add salt), and, when the document lists tools, the thing used (紙やすりで磨く, with a multi-tool) or put into
4
+ // (バケツに沈める, in a bucket). A noun is listed when the document has written it before the step (the list, the title, an
5
+ // earlier step), whole or by its last word; a noun always at hand (水, 手, water) is never reported. The words are the
6
+ // language package's (item-list-heading, item-use, item-amount, item-at-hand, enumeration-joiner).
7
+
8
+ export type ItemToken = { readonly surface: string; readonly pos: string; readonly lemma: string; readonly start: number; readonly end: number };
9
+
10
+ /** A word that marks a use frame. after: it follows the noun (を, に, で); before: it precedes it (add, with, in). */
11
+ export type UseMarker = { readonly word: string; readonly group: UseGroup; readonly position: "before" | "after" };
12
+
13
+ export type UseGroup = "object" | "place" | "instrument";
14
+
15
+ export type ItemWords = {
16
+ readonly ingredientHeadings: readonly string[];
17
+ readonly toolHeadings: readonly string[];
18
+ readonly markers: readonly UseMarker[];
19
+ /** The verbs that must follow an after-marker of each group; a group with none takes any verb. */
20
+ readonly verbs: Readonly<Partial<Record<UseGroup, readonly string[]>>>;
21
+ /** Words right after a verb (ず, ない) or right before it (not) that deny the use. */
22
+ readonly negations: readonly string[];
23
+ /** Verbs the tagger may read as nouns (stir, knead): a coordination ends at one. */
24
+ readonly actions: readonly string[];
25
+ readonly joiners: readonly string[];
26
+ /** A word after which the thing itself starts again (30 ml of soy sauce). */
27
+ readonly partitives: readonly string[];
28
+ readonly amounts: readonly string[];
29
+ readonly atHand: readonly string[];
30
+ };
31
+
32
+ export type ItemStep = { readonly start: number; readonly tokens: readonly ItemToken[] };
33
+
34
+ export type UnlistedItem = { readonly offset: number; readonly item: string; readonly list: string };
35
+
36
+ type ItemList = { readonly heading: string; readonly tools: boolean };
37
+
38
+ type Candidate = { readonly tokens: readonly ItemToken[]; readonly group: UseGroup };
39
+
40
+ const NOUN_POS = new Set(["NOUN", "PROPN"]);
41
+ const SKIPPED_POS = new Set(["DET", "ADJ", "NUM"]);
42
+ const CLAUSE_OPENER_POS = new Set(["PUNCT", "CCONJ", "ADV", "SCONJ", "X"]);
43
+ const HAS_DIGIT = /\p{Nd}/u;
44
+ const HEADING_TAIL_OPENER = /[((::【[]/u;
45
+
46
+ const lower = (text: string): string => text.toLowerCase();
47
+
48
+ /** A heading's words before a bracket or a colon, in lower case: 材料(2~3人分) is 材料. */
49
+ export const headingKey = (heading: string): string => lower((heading.split(HEADING_TAIL_OPENER)[0] ?? "").trim());
50
+
51
+ /** Spaces, hyphens and case are not a different word: multi-tool is multitool. */
52
+ export const squeezed = (text: string): string => lower(text).replace(/[\s\-‐]/gu, "");
53
+
54
+ const listKind = (heading: string, words: ItemWords): ItemList | undefined => {
55
+ const key = headingKey(heading);
56
+ const tools = words.toolHeadings.some((word) => lower(word) === key);
57
+ if (!tools && !words.ingredientHeadings.some((word) => lower(word) === key)) return undefined;
58
+ return { heading: heading.trim(), tools };
59
+ };
60
+
61
+ export const itemLists = (headings: readonly string[], words: ItemWords): ItemList[] => headings.flatMap((heading) => listKind(heading, words) ?? []);
62
+
63
+ const isWord = (token: ItemToken | undefined, words: readonly string[]): boolean =>
64
+ token !== undefined && words.some((word) => word === token.surface || word === lower(token.surface) || word === token.lemma);
65
+
66
+ const isAmount = (token: ItemToken, words: ItemWords): boolean => HAS_DIGIT.test(token.surface) || isWord(token, words.amounts);
67
+
68
+ const isNoun = (token: ItemToken | undefined): boolean => token !== undefined && NOUN_POS.has(token.pos);
69
+
70
+ const isHyphen = (token: ItemToken | undefined): boolean => token !== undefined && /^[-‐]$/u.test(token.surface);
71
+
72
+ const BRACKET_PAIRS: Readonly<Record<string, string>> = { ")": "(", ")": "(", "」": "「", "]": "[" };
73
+
74
+ /** The index of the bracket that opens the aside closing at index, or -1 when it does not close one. */
75
+ const asideOpening = (tokens: readonly ItemToken[], index: number): number => {
76
+ const open = BRACKET_PAIRS[tokens[index]?.surface ?? ""];
77
+ if (open === undefined) return -1;
78
+ return tokens.findLastIndex((token, at) => at < index && token.surface === open);
79
+ };
80
+
81
+ type Reading = { readonly segments: readonly (readonly ItemToken[])[]; readonly current: readonly ItemToken[] };
82
+
83
+ const EMPTY_READING: Reading = { segments: [], current: [] };
84
+
85
+ const closed = (reading: Reading): Reading => (reading.current.length === 0 ? reading : { segments: [...reading.segments, reading.current], current: [] });
86
+
87
+ const stepBack = (tokens: readonly ItemToken[], index: number, reading: Reading, words: ItemWords): Reading => {
88
+ const token = tokens[index];
89
+ if (token === undefined) return closed(reading);
90
+ const aside = asideOpening(tokens, index);
91
+ if (aside >= 0) return stepBack(tokens, aside - 1, reading, words);
92
+ if (isWord(token, words.joiners)) return stepBack(tokens, index - 1, closed(reading), words);
93
+ if (isAmount(token, words)) return stepBack(tokens, index - 1, reading, words);
94
+ if (isNoun(token)) return stepBack(tokens, index - 1, { segments: reading.segments, current: [token, ...reading.current] }, words);
95
+ return closed(reading);
96
+ };
97
+
98
+ /** The nouns joined before an after-marker (合いびき肉、卵、塩、こしょうを), read backwards up to the first other word. */
99
+ export const phrasesBefore = (tokens: readonly ItemToken[], marker: number, words: ItemWords): (readonly ItemToken[])[] => [
100
+ ...stepBack(tokens, marker - 1, EMPTY_READING, words).segments,
101
+ ];
102
+
103
+ const isModifier = (token: ItemToken, words: ItemWords): boolean =>
104
+ SKIPPED_POS.has(token.pos) || isAmount(token, words) || (token.pos === "VERB" && token.surface.endsWith("ed"));
105
+
106
+ const isAction = (token: ItemToken, reading: Reading, words: ItemWords): boolean =>
107
+ reading.current.length === 0 && reading.segments.length > 0 && isWord(token, words.actions);
108
+
109
+ const stepOn = (tokens: readonly ItemToken[], index: number, reading: Reading, words: ItemWords): Reading => {
110
+ const token = tokens[index];
111
+ if (token === undefined) return closed(reading);
112
+ const next = (after: Reading): Reading => stepOn(tokens, index + 1, after, words);
113
+ if (isWord(token, words.partitives)) return next({ segments: reading.segments, current: [] });
114
+ if (isWord(token, words.joiners)) return next(closed(reading));
115
+ if (isNoun(token) && !isAction(token, reading, words)) return next({ segments: reading.segments, current: [...reading.current, token] });
116
+ if (isHyphen(token) && reading.current.length > 0 && isNoun(tokens[index + 1])) return next(reading);
117
+ if (reading.current.length === 0 && isModifier(token, words)) return next(reading);
118
+ return closed(reading);
119
+ };
120
+
121
+ /** The nouns joined after a before-marker (add the egg, salt and pepper), read forwards up to the first other word. */
122
+ export const phrasesAfter = (tokens: readonly ItemToken[], marker: number, words: ItemWords): (readonly ItemToken[])[] => [
123
+ ...stepOn(tokens, marker + 1, EMPTY_READING, words).segments,
124
+ ];
125
+
126
+ const markerLength = (tokens: readonly ItemToken[], index: number, word: string): number => {
127
+ const parts = word.split(" ");
128
+ return parts.every((part, at) => isWord(tokens[index + at], [part])) ? parts.length : 0;
129
+ };
130
+
131
+ const deniedAfter = (tokens: readonly ItemToken[], verb: number, words: ItemWords): boolean => isWord(tokens[verb + 1], words.negations);
132
+
133
+ const followedByUse = (tokens: readonly ItemToken[], index: number, group: UseGroup, words: ItemWords): boolean => {
134
+ const verb = tokens[index + 1];
135
+ if (verb === undefined || verb.pos !== "VERB") return false;
136
+ const verbs = words.verbs[group] ?? [];
137
+ return (verbs.length === 0 || isWord(verb, verbs)) && !deniedAfter(tokens, index + 1, words);
138
+ };
139
+
140
+ /** How far before a verb a negation still denies it (do not ever add). */
141
+ const NEGATION_REACH = 3;
142
+
143
+ const opensClause = (tokens: readonly ItemToken[], index: number, words: ItemWords): boolean => {
144
+ const previous = tokens[index - 1];
145
+ const denied = tokens.slice(Math.max(0, index - NEGATION_REACH), index).some((token) => isWord(token, words.negations));
146
+ return !denied && (previous === undefined || CLAUSE_OPENER_POS.has(previous.pos));
147
+ };
148
+
149
+ const candidatesAt = (tokens: readonly ItemToken[], index: number, marker: UseMarker, words: ItemWords): Candidate[] => {
150
+ if (marker.position === "after") {
151
+ if (tokens[index]?.pos !== "ADP" || !isWord(tokens[index], [marker.word]) || !followedByUse(tokens, index, marker.group, words)) return [];
152
+ return phrasesBefore(tokens, index, words).map((phrase) => ({ tokens: phrase, group: marker.group }));
153
+ }
154
+ const length = markerLength(tokens, index, marker.word);
155
+ const object = marker.group === "object";
156
+ if (length === 0 || (object && !opensClause(tokens, index, words))) return [];
157
+ // The thing a step puts in or works with comes before "of" (in a bucket of water); what it adds, after it (a pinch of salt).
158
+ const read = object ? words : { ...words, partitives: [] };
159
+ return phrasesAfter(tokens, index + length - 1, read).map((phrase) => ({ tokens: phrase, group: marker.group }));
160
+ };
161
+
162
+ /** Every noun phrase a step uses, in the order written. */
163
+ export const usedPhrases = (tokens: readonly ItemToken[], words: ItemWords): Candidate[] =>
164
+ tokens
165
+ .flatMap((_, index) => words.markers.flatMap((marker) => candidatesAt(tokens, index, marker, words)))
166
+ .toSorted((left, right) => (left.tokens[0]?.start ?? 0) - (right.tokens[0]?.start ?? 0));
167
+
168
+ const phraseText = (source: string, phrase: readonly ItemToken[]): string => source.slice(phrase[0]?.start ?? 0, phrase.at(-1)?.end ?? 0);
169
+
170
+ const isAtHand = (source: string, phrase: readonly ItemToken[], words: ItemWords): boolean => {
171
+ const text = squeezed(phraseText(source, phrase));
172
+ const last = phrase.at(-1);
173
+ return words.atHand.some((word) => text.endsWith(squeezed(word)) || (last !== undefined && lower(last.lemma) === lower(word)));
174
+ };
175
+
176
+ /** The document as written and as the tagger read it: the source, and every word with its lemma (berries is berry). */
177
+ export type ItemText = { readonly source: string; readonly words: readonly ItemToken[] };
178
+
179
+ /** Written before: the whole phrase, or its last word as written or as its lemma (eggs, egg). */
180
+ const isWrittenBefore = (text: ItemText, phrase: readonly ItemToken[]): boolean => {
181
+ const start = phrase[0]?.start ?? 0;
182
+ const earlier = squeezed(text.source.slice(0, start));
183
+ const last = phrase.at(-1);
184
+ const forms = [phraseText(text.source, phrase), last?.surface ?? "", last?.lemma ?? ""].map(squeezed).filter((form) => form !== "");
185
+ const lemma = lower(last?.lemma ?? "");
186
+ return forms.some((form) => earlier.includes(form)) || text.words.some((word) => word.start < start && lower(word.lemma) === lemma);
187
+ };
188
+
189
+ const unlistedIn = (text: ItemText, step: ItemStep, lists: readonly ItemList[], words: ItemWords): UnlistedItem[] => {
190
+ const tools = lists.some((list) => list.tools);
191
+ const listNames = lists.map((list) => list.heading).join(" / ");
192
+ return usedPhrases(step.tokens, words)
193
+ .filter((candidate) => (candidate.group === "object" || tools) && candidate.tokens.at(-1)?.pos === "NOUN")
194
+ .filter((candidate) => !isAtHand(text.source, candidate.tokens, words) && !isWrittenBefore(text, candidate.tokens))
195
+ .map((candidate) => ({ offset: candidate.tokens[0]?.start ?? step.start, item: phraseText(text.source, candidate.tokens), list: listNames }));
196
+ };
197
+
198
+ /**
199
+ * The things the numbered steps use that the document's list of what is needed does not name. Nothing when the document
200
+ * has no such list: a document that promises no list cannot leave something off it.
201
+ */
202
+ export const unlistedItems = (text: ItemText, headings: readonly string[], steps: readonly ItemStep[], words: ItemWords): UnlistedItem[] => {
203
+ const lists = itemLists(headings, words);
204
+ if (lists.length === 0) return [];
205
+ const found = steps.flatMap((step) => unlistedIn(text, step, lists, words));
206
+ return found.filter((item, index) => found.findIndex((other) => squeezed(other.item) === squeezed(item.item)) === index);
207
+ };
@@ -32,6 +32,16 @@ const compareVersions = (left: Version, right: Version): number => {
32
32
  return differing === -1 ? comparePrerelease(left.prerelease, right.prerelease) : Math.sign((left.parts[differing] ?? 0) - (right.parts[differing] ?? 0));
33
33
  };
34
34
 
35
+ /** Whether every label is a version and no step goes against direction (equal versions do not break it). */
36
+ export const versionsAlong = (labels: readonly (string | undefined)[], direction: number): boolean => {
37
+ const versions = labels.flatMap((label) => {
38
+ const version = label === undefined ? undefined : parse(label);
39
+ return version === undefined ? [] : [version];
40
+ });
41
+ if (versions.length !== labels.length) return false;
42
+ return versions.slice(1).every((version, index) => compareVersions(version, versions[index] ?? version) !== -direction);
43
+ };
44
+
35
45
  /** The longest run along direction ending at each version (equal versions do not break it). */
36
46
  const longestEndingAt = (versions: readonly Version[], direction: number): number[] =>
37
47
  versions.reduce<number[]>((lengths, version, index) => {
@@ -0,0 +1,285 @@
1
+ import type { StructureIssue } from "./issues.ts";
2
+
3
+ /**
4
+ * 書いた距離と合わない徒歩の分数(駅 徒歩3分(約600m)、a 3-minute walk (600 m))。徒歩の語に付いた分数と、そのすぐ前かすぐ後ろの
5
+ * 距離だけを組にし、文書の速さで歩いた分数と比べる。速さは文書に書いたもの(道路距離80mを1分、80 m per minute、5 km/h)を使い、
6
+ * 書いていなければ呼ぶ側が渡す速さ(文書の種類の速さ。不動産の広告は 80m で1分、切り上げ)を使う。どちらも無ければ速さを推し量らない。
7
+ * 約の付いた距離は、書いた一番下の桁の半分だけ幅を持たせる(約1.2km は 1.15〜1.25km)。小数の距離は書いた桁の半分の幅を持つ。
8
+ * 範囲(5〜7分)、上限(10分以内)、約の付いた分数は比べない。語は言語パッケージの語彙表から受け取る。
9
+ */
10
+
11
+ /** 語彙表の一語。position は数のどちら側に書くか。weight は単位の倍率(長さは m、時間は秒)。 */
12
+ export type WalkMark = {
13
+ readonly pattern: string;
14
+ readonly position?: "before" | "after" | undefined;
15
+ readonly weight?: number | undefined;
16
+ };
17
+
18
+ export type WalkWords = {
19
+ /** 分数を徒歩の時間にする語(徒歩、walk)。position は分数のどちら側か。 */
20
+ readonly walks: readonly WalkMark[];
21
+ /** 徒歩の語と分数の間に挟んでよい語(徒歩で5分の で)。 */
22
+ readonly gaps: readonly string[];
23
+ /** 分数を上限にする語(以内、within)。position は分数のどちら側か。 */
24
+ readonly bounds: readonly WalkMark[];
25
+ /** 二つの数を範囲にする語(5から7分、8 to 10 minutes)。〜 や - の記号は語彙表に書かなくても読む。 */
26
+ readonly ranges: readonly string[];
27
+ /** 約や上限の語と数の間で読み飛ばす語(about a 5-minute walk の a)。 */
28
+ readonly fillers: readonly string[];
29
+ /** 長さの単位。weight は m への倍率。 */
30
+ readonly lengths: readonly WalkMark[];
31
+ /** 時間の単位。weight は秒への倍率。分より短い単位は分数に使わない。 */
32
+ readonly times: readonly WalkMark[];
33
+ /** 数を概数にする語(約、about)。 */
34
+ readonly hedges: readonly string[];
35
+ /** 長さを速さにする語(を1分、per minute、/h、分速)。weight はその時間の秒。 */
36
+ readonly perTimes: readonly WalkMark[];
37
+ /** 速さの行にあれば、端数を切り上げると言う語(切り上げ、rounded up)。 */
38
+ readonly roundUps: readonly string[];
39
+ /** 速さの行にあれば、切り上げの語があっても切り上げではないと言う語(切り上げません、not rounded up)。 */
40
+ readonly notRoundUps: readonly string[];
41
+ };
42
+
43
+ /** 1分に歩く m と、端数の扱い。up は切り上げだけ、any は切り上げでも切り捨てでも合う。 */
44
+ export type WalkRate = { readonly metresPerMinute: number; readonly rounding: "up" | "any" };
45
+
46
+ /** 読んだ距離。metres は書いた値、margin は書いた桁から来る幅の半分(m)。 */
47
+ export type Distance = { readonly start: number; readonly end: number; readonly metres: number; readonly margin: number };
48
+
49
+ /** 徒歩の分数。lead は前に書いた徒歩の語の始まり(徒歩5分 の 徒歩)、後ろに書くなら数の始まり。 */
50
+ type Minutes = { readonly start: number; readonly end: number; readonly lead: number; readonly value: number };
51
+
52
+ type Reading = { readonly start: number; readonly end: number; readonly digits: string; readonly unit: WalkMark; readonly unitEnd: number };
53
+
54
+ const DIGIT = "[0-90-9]";
55
+ const NUMBER = new RegExp(`(?<!${DIGIT}|[.,,.])${DIGIT}+(?:[,,]${DIGIT}{3})*(?:[..]${DIGIT}+)?(?!${DIGIT}|[..,,]${DIGIT})`, "gu");
56
+ const LETTER = /[A-Za-z]/u;
57
+ /** 単位のすぐ後ろにあれば、その単位ではない(m のあとの in、㎡ の 2、m² の ²)。 */
58
+ const UNIT_CONTINUES = /^[A-Za-z0-90-9²³]/u;
59
+ /** 数と単位の間に置けるもの(1.2 km、5-minute)。 */
60
+ const NUMBER_UNIT_GAP = /^[ \u00a0-]?/u;
61
+ /** 分数と後ろの徒歩の語の間に置けるもの(5 minutes' walk、5 min. walk)。 */
62
+ const MINUTES_WALK_GAP = /^[\s'’.]*/u;
63
+ /**
64
+ * 分数と距離の間に置けるもの。開き括弧か・か / の一つだけ(徒歩5分(400m)、400m(徒歩5分)、徒歩5分・400m)。
65
+ * 読点では組にしない(標高600m、徒歩3分 の 600m は道のりではない)。
66
+ */
67
+ const PAIR_GAP = /^\s*[(([[【・//]\s*$/u;
68
+ const RANGE_MARKS: readonly string[] = ["〜", "~", "~", "–", "—", "-"];
69
+ const DIGIT_START = new RegExp(`^${DIGIT}`, "u");
70
+ const DIGIT_END = new RegExp(`${DIGIT}$`, "u");
71
+ /** 浮動小数の誤差(1150 / 80 など)で切り上げが一つずれないための小さな数。 */
72
+ const EPSILON = 1e-9;
73
+ const MINUTE_SECONDS = 60;
74
+ /** 時速から出した速さ(4km/h は 66.66…m/分)を、言うときに小数一桁にする。 */
75
+ const RATE_DECIMALS = 10;
76
+
77
+ const normal = (text: string): string => text.normalize("NFKC").toLowerCase();
78
+
79
+ const valueOf = (digits: string): number => Number(digits.normalize("NFKC").replaceAll(",", ""));
80
+
81
+ const isWordEdge = (text: string, at: number): boolean => !(LETTER.test(text.charAt(at - 1)) && LETTER.test(text.charAt(at)));
82
+
83
+ const startsWithWord = (text: string, word: string): boolean => normal(text.slice(0, word.length)) === normal(word) && isWordEdge(text, word.length);
84
+
85
+ const endsWithWord = (text: string, word: string): boolean =>
86
+ text.length >= word.length && normal(text.slice(-word.length)) === normal(word) && isWordEdge(text, text.length - word.length);
87
+
88
+ /** 末尾の空白と、読み飛ばす語を除く(about a の a)。 */
89
+ const trimFillers = (text: string, fillers: readonly string[]): string => {
90
+ const trimmed = text.trimEnd();
91
+ const filler = fillers.find((word) => endsWithWord(trimmed, word));
92
+ return filler === undefined ? trimmed : trimFillers(trimmed.slice(0, -filler.length), fillers);
93
+ };
94
+
95
+ /** 位置 at から始まる単位。㎞ のような一字の単位も NFKC で照らす。長い語から。 */
96
+ const unitAt = (line: string, at: number, units: readonly WalkMark[]): { readonly unit: WalkMark; readonly end: number } | undefined =>
97
+ units
98
+ .toSorted((left, right) => right.pattern.length - left.pattern.length)
99
+ .flatMap((unit) =>
100
+ Array.from({ length: unit.pattern.length }, (_, index) => at + index + 1)
101
+ .filter((end) => normal(line.slice(at, end)) === normal(unit.pattern) && !UNIT_CONTINUES.test(line.slice(end)))
102
+ .map((end) => ({ unit, end })),
103
+ )[0];
104
+
105
+ /** 一行の中の、単位の付いた数。 */
106
+ const readingsIn = (line: string, units: readonly WalkMark[]): Reading[] =>
107
+ [...line.matchAll(NUMBER)].flatMap((match) => {
108
+ const numberEnd = match.index + match[0].length;
109
+ const unitStart = numberEnd + (NUMBER_UNIT_GAP.exec(line.slice(numberEnd))?.[0].length ?? 0);
110
+ const found = unitAt(line, unitStart, units);
111
+ return found === undefined ? [] : [{ start: match.index, end: numberEnd, digits: match[0], unit: found.unit, unitEnd: found.end }];
112
+ });
113
+
114
+ const endsWithAny = (text: string, words: readonly string[]): boolean => words.some((word) => endsWithWord(text, word));
115
+
116
+ /** 範囲の片側の数か(5〜7分 の 7、5分から10分 の 5分、400m〜600m の 400m、8 to 10 minutes の 10)。 */
117
+ const isRangeEnd = (line: string, reading: Reading, units: readonly WalkMark[], rangeWords: readonly string[]): boolean => {
118
+ const joins = [...RANGE_MARKS, ...rangeWords];
119
+ const after = line.slice(reading.unitEnd).trimStart();
120
+ const next = joins.find((word) => startsWithWord(after, word));
121
+ if (next !== undefined && DIGIT_START.test(after.slice(next.length).trimStart())) return true;
122
+ const before = line.slice(0, reading.start).trimEnd();
123
+ const previous = joins.find((word) => endsWithWord(before, word));
124
+ if (previous === undefined) return false;
125
+ const left = before.slice(0, -previous.length).trimEnd();
126
+ const unit = units.find((mark) => endsWithWord(left, mark.pattern));
127
+ return DIGIT_END.test(unit === undefined ? left : left.slice(0, -unit.pattern.length).trimEnd());
128
+ };
129
+
130
+ /** 数のすぐ前(読み飛ばす語の後ろ)に約の語があるか。 */
131
+ const isHedged = (line: string, start: number, words: WalkWords): boolean =>
132
+ endsWithAny(trimFillers(line.slice(0, start).replace(/[(([[]$/u, ""), words.fillers), words.hedges);
133
+
134
+ const boundBefore = (prefix: string, words: WalkWords): boolean =>
135
+ endsWithAny(
136
+ trimFillers(prefix, words.fillers),
137
+ words.bounds.filter((bound) => bound.position === "before").map((bound) => bound.pattern),
138
+ );
139
+
140
+ const boundAfter = (rest: string, words: WalkWords): boolean =>
141
+ words.bounds.filter((bound) => bound.position !== "before").some((bound) => startsWithWord(rest.trimStart(), bound.pattern));
142
+
143
+ /** 前に書く徒歩の語(徒歩5分、徒歩で5分)。あれば、その語の始まりの位置。 */
144
+ const walkBeforeStart = (prefix: string, words: WalkWords): number | undefined => {
145
+ const trimmed = prefix.trimEnd();
146
+ const gap = words.gaps.find((word) => trimmed.endsWith(word));
147
+ const head = gap === undefined ? trimmed : trimmed.slice(0, -gap.length).trimEnd();
148
+ const walk = words.walks.find((mark) => mark.position === "before" && endsWithWord(head, mark.pattern));
149
+ return walk === undefined ? undefined : head.length - walk.pattern.length;
150
+ };
151
+
152
+ /** 後ろに書く徒歩の語(a 5-minute walk)。あれば、その語の終わりの位置。 */
153
+ const walkAfterEnd = (line: string, from: number, words: WalkWords): number | undefined => {
154
+ const at = from + (MINUTES_WALK_GAP.exec(line.slice(from))?.[0].length ?? 0);
155
+ const walk = words.walks.find((mark) => mark.position === "after" && startsWithWord(line.slice(at), mark.pattern));
156
+ return walk === undefined ? undefined : at + walk.pattern.length;
157
+ };
158
+
159
+ const minuteUnits = (words: WalkWords): WalkMark[] => words.times.filter((unit) => (unit.weight ?? 0) >= MINUTE_SECONDS);
160
+
161
+ /** 一行の中の、徒歩の分数。範囲、上限、約の付いた分数は読まない。 */
162
+ export const walkMinutesIn = (line: string, words: WalkWords): Minutes[] =>
163
+ readingsIn(line, minuteUnits(words)).flatMap((reading) => {
164
+ const prefix = line.slice(0, reading.start);
165
+ const leadStart = walkBeforeStart(prefix, words);
166
+ const afterEnd = walkAfterEnd(line, reading.unitEnd, words);
167
+ if (leadStart === undefined && afterEnd === undefined) return [];
168
+ if (isRangeEnd(line, reading, words.times, words.ranges) || isHedged(line, reading.start, words)) return [];
169
+ if (boundBefore(prefix, words) || boundAfter(line.slice(afterEnd ?? reading.unitEnd), words)) return [];
170
+ const value = (valueOf(reading.digits) * (reading.unit.weight ?? 0)) / MINUTE_SECONDS;
171
+ if (!Number.isInteger(value) || value <= 0) return [];
172
+ return [{ start: reading.start, end: afterEnd ?? reading.unitEnd, lead: leadStart ?? reading.start, value }];
173
+ });
174
+
175
+ /** 書いた数の桁から来る幅の半分(書いた単位で)。小数は最後の桁、約の付いた整数は最後の 0 でない桁。それ以外は 0。 */
176
+ export const marginOf = (digits: string, hedged: boolean): number => {
177
+ const plain = digits.normalize("NFKC").replaceAll(",", "");
178
+ const point = plain.indexOf(".");
179
+ if (point !== -1) return 10 ** -(plain.length - point - 1) / 2;
180
+ if (!hedged) return 0;
181
+ const lastNonZero = [...plain].findLastIndex((digit) => digit !== "0");
182
+ const zeros = plain.length - 1 - lastNonZero;
183
+ return 10 ** zeros / 2;
184
+ };
185
+
186
+ /** 一行の中の距離。範囲の片側は読まない。 */
187
+ export const distancesIn = (line: string, words: WalkWords): Distance[] =>
188
+ readingsIn(line, words.lengths)
189
+ .filter((reading) => !isRangeEnd(line, reading, words.lengths, words.ranges))
190
+ .flatMap((reading) => {
191
+ const weight = reading.unit.weight ?? 0;
192
+ const metres = valueOf(reading.digits) * weight;
193
+ const margin = marginOf(reading.digits, isHedged(line, reading.start, words)) * weight;
194
+ return metres > 0 ? [{ start: reading.start, end: reading.unitEnd, metres, margin }] : [];
195
+ });
196
+
197
+ /**
198
+ * 分数と組になる距離。すぐ後ろ(徒歩5分(400m))を先に、無ければすぐ前(400m(徒歩5分))を見る。間に置けるのは記号と約の語だけ。
199
+ * 一つの距離は一つの分数とだけ組にする。
200
+ */
201
+ export const walkPairs = (line: string, words: WalkWords): { readonly minutes: Minutes; readonly distance: Distance }[] => {
202
+ const distances = distancesIn(line, words);
203
+ const between = (from: number, to: number): boolean => from <= to && PAIR_GAP.test(trimFillers(line.slice(from, to), [...words.hedges, ...words.fillers]));
204
+ const used = new Set<Distance>();
205
+ return walkMinutesIn(line, words).flatMap((minutes) => {
206
+ const after = distances.find((distance) => !used.has(distance) && between(minutes.end, distance.start));
207
+ const before = after ?? distances.findLast((distance) => !used.has(distance) && between(distance.end, minutes.lead));
208
+ if (before === undefined) return [];
209
+ used.add(before);
210
+ return [{ minutes, distance: before }];
211
+ });
212
+ };
213
+
214
+ /** 一行に書いた速さ(m/分)。距離のすぐ後ろ(80mを1分、80 m per minute)かすぐ前(分速80m)に時間の語がある距離。 */
215
+ export const ratesIn = (line: string, words: WalkWords): number[] =>
216
+ distancesIn(line, words).flatMap((distance) => {
217
+ const rest = line.slice(distance.end).trimStart();
218
+ const head = line.slice(0, distance.start).trimEnd();
219
+ const per =
220
+ words.perTimes.find((mark) => mark.position !== "before" && startsWithWord(rest, mark.pattern)) ??
221
+ words.perTimes.find((mark) => mark.position === "before" && endsWithWord(head, mark.pattern));
222
+ return per === undefined ? [] : [distance.metres / ((per.weight ?? MINUTE_SECONDS) / MINUTE_SECONDS)];
223
+ });
224
+
225
+ const containsAny = (text: string, words: readonly string[]): boolean => words.some((word) => normal(text).includes(normal(word)));
226
+
227
+ /** 速さを読む行。徒歩の語(Walking も walk の語として)を含む行。 */
228
+ const hasWalkWord = (line: string, words: WalkWords): boolean =>
229
+ containsAny(
230
+ line,
231
+ words.walks.map((walk) => walk.pattern),
232
+ );
233
+
234
+ const linesOf = (source: string): { readonly text: string; readonly start: number }[] => {
235
+ const starts = [0, ...[...source.matchAll(/\n/gu)].map((match) => match.index + 1)];
236
+ return starts.map((start, index) => ({ text: source.slice(start, (starts[index + 1] ?? source.length + 1) - 1), start }));
237
+ };
238
+
239
+ /**
240
+ * 文書の速さ。徒歩の語のある行に書いた速さが一つ(一つの値)だけなら、それを使う。端数は、その行に切り上げの語があれば切り上げだけ、
241
+ * 無ければどちらでもよい。二つ以上の値があれば、どの分数がどれで計ったか決められないので比べない。書いていなければ fallback。
242
+ */
243
+ export const statedRate = (source: string, words: WalkWords, fallback?: WalkRate): WalkRate | undefined => {
244
+ const stated = linesOf(source)
245
+ .filter((line) => hasWalkWord(line.text, words))
246
+ .flatMap((line) =>
247
+ ratesIn(line.text, words).map((rate) => ({ rate, up: containsAny(line.text, words.roundUps) && !containsAny(line.text, words.notRoundUps) })),
248
+ );
249
+ const first = stated[0];
250
+ if (first === undefined) return fallback;
251
+ if (stated.some((other) => other.rate !== first.rate)) return undefined;
252
+ return { metresPerMinute: first.rate, rounding: stated.some((other) => other.up) ? "up" : "any" };
253
+ };
254
+
255
+ /** その距離を歩いて合う分数の幅。margin の幅の上の端は含まない(1.25km は 1.3km と書く)。 */
256
+ export const expectedMinutes = (distance: Pick<Distance, "metres" | "margin">, rate: WalkRate): { readonly low: number; readonly high: number } => {
257
+ const low = (distance.metres - distance.margin) / rate.metresPerMinute;
258
+ const high = (distance.metres + distance.margin) / rate.metresPerMinute;
259
+ return { low: rate.rounding === "up" ? Math.ceil(low - EPSILON) : Math.floor(low + EPSILON), high: Math.ceil(high - EPSILON) };
260
+ };
261
+
262
+ const rangeText = (low: number, high: number): string => (low === high ? String(low) : `${String(low)}–${String(high)}`);
263
+
264
+ /** 距離と合わない徒歩の分数。分数の位置を指す。 */
265
+ export const walkTimeMismatches = (source: string, words: WalkWords, fallback?: WalkRate): StructureIssue[] => {
266
+ const rate = statedRate(source, words, fallback);
267
+ if (rate === undefined) return [];
268
+ return linesOf(source).flatMap((line) =>
269
+ walkPairs(line.text, words).flatMap(({ minutes, distance }) => {
270
+ const { low, high } = expectedMinutes(distance, rate);
271
+ if (minutes.value >= low && minutes.value <= high) return [];
272
+ return [
273
+ {
274
+ offset: line.start + minutes.lead,
275
+ values: {
276
+ minutes: line.text.slice(minutes.lead, minutes.end),
277
+ distance: line.text.slice(distance.start, distance.end),
278
+ expected: rangeText(Math.max(low, 1), Math.max(high, 1)),
279
+ rate: Math.round(rate.metresPerMinute * RATE_DECIMALS) / RATE_DECIMALS,
280
+ },
281
+ },
282
+ ];
283
+ }),
284
+ );
285
+ };
@@ -0,0 +1,31 @@
1
+ // 名が同じ人の名前の、姓だけの書き損じ(Whitford と Whitfield)。一字違い(isNearWord)より遠い違いを、英字の長い姓に限って見る。
2
+
3
+ const LATIN_WORD = /^[a-z]+$/u;
4
+
5
+ /** 二字違いまで見る姓の長さ。短い姓(Martin と Marten、Hansen と Hanson)は別の姓のことが多い。 */
6
+ const MIN_SURNAME_LENGTH = 7;
7
+ /** 頭からそろう字の数。頭が違えば別の姓(Robertson と Robinson)。 */
8
+ const MIN_SURNAME_PREFIX = 4;
9
+ /** 頭と終わりのそろった字のあいだに残る字の数。短いほうで二字まで、長いほうで三字まで(Whitford の or と Whitfield の iel)。 */
10
+ const MAX_SURNAME_GAP = 2;
11
+
12
+ const sharedPrefix = (left: string, right: string): number => {
13
+ const differ = [...left].findIndex((char, index) => char !== right.charAt(index));
14
+ return differ < 0 ? left.length : differ;
15
+ };
16
+
17
+ /**
18
+ * 二つの英字の姓が、頭が四字以上そろい、終わりの字もそろい、あいだの二字(長いほうは三字)までだけ違うか。どちらも七字以上で。
19
+ * 終わりだけの違い(Whitford と Whitfords)は語の形の違い。
20
+ */
21
+ export const isNearSurname = (left: string, right: string): boolean => {
22
+ if (left === right || !LATIN_WORD.test(left) || !LATIN_WORD.test(right) || Math.min(left.length, right.length) < MIN_SURNAME_LENGTH) return false;
23
+ const prefix = sharedPrefix(left, right);
24
+ if (prefix < MIN_SURNAME_PREFIX) return false;
25
+ const [leftRest, rightRest] = [left.slice(prefix), right.slice(prefix)];
26
+ const suffix = sharedPrefix([...leftRest].toReversed().join(""), [...rightRest].toReversed().join(""));
27
+ if (suffix === 0) return false;
28
+ const gaps = [leftRest.length - suffix, rightRest.length - suffix].toSorted((first, second) => first - second);
29
+ const [shorter = 0, longer = 0] = gaps;
30
+ return shorter <= MAX_SURNAME_GAP && longer <= MAX_SURNAME_GAP + 1;
31
+ };
@@ -0,0 +1,41 @@
1
+ import type { ProseDocument, Segmentation, Span, TableCell, Token } from "./plugin.ts";
2
+ import { proseAndTablesOf } from "./table-text.ts";
3
+ import { tableBodyCells, type Cell } from "./facts/table-facts.ts";
4
+
5
+ // 表の升の語。本文は表を覆うので、升の字は文の分割も品詞解析も通らない。升ごとに adapter に渡し、語の位置を source の上に戻す。
6
+
7
+ const LETTER = /\p{L}/u;
8
+
9
+ const shiftToken = (token: Token, by: number): Token => ({ ...token, span: { start: token.span.start + by, end: token.span.end + by } });
10
+
11
+ /** 升一つの語。字の無い升(数だけ、空)は解析しない。adapter が語を返さなければ tokens は無い。 */
12
+ export const cellWithTokens = (cell: Cell, segment: (text: string) => Segmentation): TableCell => {
13
+ if (!LETTER.test(cell.text)) return cell;
14
+ const sentences = segment(cell.text).sentences;
15
+ if (!sentences.some((sentence) => sentence.tokens !== undefined)) return cell;
16
+ return { ...cell, tokens: sentences.flatMap((sentence) => (sentence.tokens ?? []).map((token) => shiftToken(token, cell.start))) };
17
+ };
18
+
19
+ /** 文書の表(引用とコードの外)の本体の升と、その語。 */
20
+ export const tableCellsOf = (doc: ProseDocument, segment: (text: string) => Segmentation): TableCell[] =>
21
+ tableBodyCells(proseAndTablesOf(doc)).map((cell) => cellWithTokens(cell, segment));
22
+
23
+ /** prose に、升の字だけを書き戻したもの(表の見出しの行と区切りの | は覆ったまま)。位置は変えない。 */
24
+ export const proseWithCells = (prose: string, cells: readonly (Span & { readonly text: string })[]): string => {
25
+ const pieces: string[] = [];
26
+ const end = cells
27
+ .toSorted((left, right) => left.start - right.start)
28
+ .reduce((from, cell) => {
29
+ if (cell.start < from || cell.end - cell.start !== cell.text.length) return from;
30
+ pieces.push(prose.slice(from, cell.start), cell.text);
31
+ return cell.end;
32
+ }, 0);
33
+ pieces.push(prose.slice(end));
34
+ return pieces.join("");
35
+ };
36
+
37
+ /** 表の升の語は、読む rule があるときに一度だけ作る。升ごとの解析を、表を読まない rule に払わせない。 */
38
+ export const lazyCells = (make: () => readonly TableCell[]): (() => readonly TableCell[]) => {
39
+ const cells: { value?: readonly TableCell[] } = {};
40
+ return () => (cells.value ??= make());
41
+ };