chaffjs 0.30.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 (530) 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/time-lengths.d.ts.map +1 -1
  26. package/dist/derived/time-lengths.js +12 -9
  27. package/dist/derived/time-lengths.js.map +1 -1
  28. package/dist/derived/use-interval.d.ts +33 -0
  29. package/dist/derived/use-interval.d.ts.map +1 -0
  30. package/dist/derived/use-interval.js +93 -0
  31. package/dist/derived/use-interval.js.map +1 -0
  32. package/dist/detectors/announced-count.d.ts.map +1 -1
  33. package/dist/detectors/announced-count.js +1 -0
  34. package/dist/detectors/announced-count.js.map +1 -1
  35. package/dist/detectors/capacity-runtime.d.ts +7 -0
  36. package/dist/detectors/capacity-runtime.d.ts.map +1 -0
  37. package/dist/detectors/capacity-runtime.js +98 -0
  38. package/dist/detectors/capacity-runtime.js.map +1 -0
  39. package/dist/detectors/contact-variant.d.ts +3 -0
  40. package/dist/detectors/contact-variant.d.ts.map +1 -0
  41. package/dist/detectors/contact-variant.js +21 -0
  42. package/dist/detectors/contact-variant.js.map +1 -0
  43. package/dist/detectors/count-over-capacity.d.ts +4 -0
  44. package/dist/detectors/count-over-capacity.d.ts.map +1 -0
  45. package/dist/detectors/count-over-capacity.js +26 -0
  46. package/dist/detectors/count-over-capacity.js.map +1 -0
  47. package/dist/detectors/date-outside-period.d.ts.map +1 -1
  48. package/dist/detectors/date-outside-period.js +2 -1
  49. package/dist/detectors/date-outside-period.js.map +1 -1
  50. package/dist/detectors/date-stamp-order.d.ts +3 -0
  51. package/dist/detectors/date-stamp-order.d.ts.map +1 -0
  52. package/dist/detectors/date-stamp-order.js +24 -0
  53. package/dist/detectors/date-stamp-order.js.map +1 -0
  54. package/dist/detectors/defined-term-form.d.ts.map +1 -1
  55. package/dist/detectors/defined-term-form.js +57 -7
  56. package/dist/detectors/defined-term-form.js.map +1 -1
  57. package/dist/detectors/definition-use.d.ts +1 -1
  58. package/dist/detectors/definition-use.d.ts.map +1 -1
  59. package/dist/detectors/definition-use.js +4 -2
  60. package/dist/detectors/definition-use.js.map +1 -1
  61. package/dist/detectors/derived-numbers.d.ts +9 -1
  62. package/dist/detectors/derived-numbers.d.ts.map +1 -1
  63. package/dist/detectors/derived-numbers.js +2 -2
  64. package/dist/detectors/derived-numbers.js.map +1 -1
  65. package/dist/detectors/due-date.d.ts.map +1 -1
  66. package/dist/detectors/due-date.js +6 -0
  67. package/dist/detectors/due-date.js.map +1 -1
  68. package/dist/detectors/en-shape.d.ts.map +1 -1
  69. package/dist/detectors/en-shape.js +7 -4
  70. package/dist/detectors/en-shape.js.map +1 -1
  71. package/dist/detectors/event-deadline.d.ts +3 -0
  72. package/dist/detectors/event-deadline.d.ts.map +1 -0
  73. package/dist/detectors/event-deadline.js +24 -0
  74. package/dist/detectors/event-deadline.js.map +1 -0
  75. package/dist/detectors/fact-consistency.d.ts +1 -1
  76. package/dist/detectors/fact-consistency.d.ts.map +1 -1
  77. package/dist/detectors/fact-consistency.js +52 -3
  78. package/dist/detectors/fact-consistency.js.map +1 -1
  79. package/dist/detectors/heading-case.d.ts +5 -2
  80. package/dist/detectors/heading-case.d.ts.map +1 -1
  81. package/dist/detectors/heading-case.js +12 -4
  82. package/dist/detectors/heading-case.js.map +1 -1
  83. package/dist/detectors/heading-echo.d.ts +5 -0
  84. package/dist/detectors/heading-echo.d.ts.map +1 -1
  85. package/dist/detectors/heading-echo.js +14 -1
  86. package/dist/detectors/heading-echo.js.map +1 -1
  87. package/dist/detectors/layout-code.d.ts +5 -0
  88. package/dist/detectors/layout-code.d.ts.map +1 -0
  89. package/dist/detectors/layout-code.js +124 -0
  90. package/dist/detectors/layout-code.js.map +1 -0
  91. package/dist/detectors/measured-facts.d.ts +8 -0
  92. package/dist/detectors/measured-facts.d.ts.map +1 -0
  93. package/dist/detectors/measured-facts.js +18 -0
  94. package/dist/detectors/measured-facts.js.map +1 -0
  95. package/dist/detectors/name-variant-model-codes.d.ts +11 -0
  96. package/dist/detectors/name-variant-model-codes.d.ts.map +1 -0
  97. package/dist/detectors/name-variant-model-codes.js +21 -0
  98. package/dist/detectors/name-variant-model-codes.js.map +1 -0
  99. package/dist/detectors/name-variant.d.ts.map +1 -1
  100. package/dist/detectors/name-variant.js +25 -4
  101. package/dist/detectors/name-variant.js.map +1 -1
  102. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  103. package/dist/detectors/orthographic-variant.js +62 -7
  104. package/dist/detectors/orthographic-variant.js.map +1 -1
  105. package/dist/detectors/orthography.d.ts.map +1 -1
  106. package/dist/detectors/orthography.js +12 -6
  107. package/dist/detectors/orthography.js.map +1 -1
  108. package/dist/detectors/quantity-range.d.ts +3 -0
  109. package/dist/detectors/quantity-range.d.ts.map +1 -0
  110. package/dist/detectors/quantity-range.js +35 -0
  111. package/dist/detectors/quantity-range.js.map +1 -0
  112. package/dist/detectors/range-band.d.ts +5 -0
  113. package/dist/detectors/range-band.d.ts.map +1 -0
  114. package/dist/detectors/range-band.js +80 -0
  115. package/dist/detectors/range-band.js.map +1 -0
  116. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts +3 -0
  117. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts.map +1 -0
  118. package/dist/detectors/registry/capacity-runtime-mismatch.js +3 -0
  119. package/dist/detectors/registry/capacity-runtime-mismatch.js.map +1 -0
  120. package/dist/detectors/registry/contact-variant.d.ts +3 -0
  121. package/dist/detectors/registry/contact-variant.d.ts.map +1 -0
  122. package/dist/detectors/registry/contact-variant.js +3 -0
  123. package/dist/detectors/registry/contact-variant.js.map +1 -0
  124. package/dist/detectors/registry/count-over-capacity.d.ts +3 -0
  125. package/dist/detectors/registry/count-over-capacity.d.ts.map +1 -0
  126. package/dist/detectors/registry/count-over-capacity.js +3 -0
  127. package/dist/detectors/registry/count-over-capacity.js.map +1 -0
  128. package/dist/detectors/registry/date-stamp-order.d.ts +3 -0
  129. package/dist/detectors/registry/date-stamp-order.d.ts.map +1 -0
  130. package/dist/detectors/registry/date-stamp-order.js +3 -0
  131. package/dist/detectors/registry/date-stamp-order.js.map +1 -0
  132. package/dist/detectors/registry/event-deadline.d.ts +3 -0
  133. package/dist/detectors/registry/event-deadline.d.ts.map +1 -0
  134. package/dist/detectors/registry/event-deadline.js +3 -0
  135. package/dist/detectors/registry/event-deadline.js.map +1 -0
  136. package/dist/detectors/registry/interval-count-mismatch.d.ts +3 -0
  137. package/dist/detectors/registry/interval-count-mismatch.d.ts.map +1 -0
  138. package/dist/detectors/registry/interval-count-mismatch.js +3 -0
  139. package/dist/detectors/registry/interval-count-mismatch.js.map +1 -0
  140. package/dist/detectors/registry/layout-code-mismatch.d.ts +3 -0
  141. package/dist/detectors/registry/layout-code-mismatch.d.ts.map +1 -0
  142. package/dist/detectors/registry/layout-code-mismatch.js +3 -0
  143. package/dist/detectors/registry/layout-code-mismatch.js.map +1 -0
  144. package/dist/detectors/registry/quantity-range.d.ts +3 -0
  145. package/dist/detectors/registry/quantity-range.d.ts.map +1 -0
  146. package/dist/detectors/registry/quantity-range.js +3 -0
  147. package/dist/detectors/registry/quantity-range.js.map +1 -0
  148. package/dist/detectors/registry/range-band-mismatch.d.ts +3 -0
  149. package/dist/detectors/registry/range-band-mismatch.d.ts.map +1 -0
  150. package/dist/detectors/registry/range-band-mismatch.js +3 -0
  151. package/dist/detectors/registry/range-band-mismatch.js.map +1 -0
  152. package/dist/detectors/registry/rent-multiple-mismatch.d.ts +3 -0
  153. package/dist/detectors/registry/rent-multiple-mismatch.d.ts.map +1 -0
  154. package/dist/detectors/registry/rent-multiple-mismatch.js +3 -0
  155. package/dist/detectors/registry/rent-multiple-mismatch.js.map +1 -0
  156. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts +3 -0
  157. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts.map +1 -0
  158. package/dist/detectors/registry/scaled-quantity-mismatch.js +3 -0
  159. package/dist/detectors/registry/scaled-quantity-mismatch.js.map +1 -0
  160. package/dist/detectors/registry/step-time-sum-mismatch.d.ts +3 -0
  161. package/dist/detectors/registry/step-time-sum-mismatch.d.ts.map +1 -0
  162. package/dist/detectors/registry/step-time-sum-mismatch.js +3 -0
  163. package/dist/detectors/registry/step-time-sum-mismatch.js.map +1 -0
  164. package/dist/detectors/registry/time-range-reversed.d.ts +3 -0
  165. package/dist/detectors/registry/time-range-reversed.d.ts.map +1 -0
  166. package/dist/detectors/registry/time-range-reversed.js +3 -0
  167. package/dist/detectors/registry/time-range-reversed.js.map +1 -0
  168. package/dist/detectors/registry/unlisted-item-used.d.ts +3 -0
  169. package/dist/detectors/registry/unlisted-item-used.d.ts.map +1 -0
  170. package/dist/detectors/registry/unlisted-item-used.js +3 -0
  171. package/dist/detectors/registry/unlisted-item-used.js.map +1 -0
  172. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts +3 -0
  173. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts.map +1 -0
  174. package/dist/detectors/registry/walk-time-distance-mismatch.js +3 -0
  175. package/dist/detectors/registry/walk-time-distance-mismatch.js.map +1 -0
  176. package/dist/detectors/rent-multiple-mismatch.d.ts +3 -0
  177. package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -0
  178. package/dist/detectors/rent-multiple-mismatch.js +39 -0
  179. package/dist/detectors/rent-multiple-mismatch.js.map +1 -0
  180. package/dist/detectors/scaled-quantity.d.ts +3 -0
  181. package/dist/detectors/scaled-quantity.d.ts.map +1 -0
  182. package/dist/detectors/scaled-quantity.js +37 -0
  183. package/dist/detectors/scaled-quantity.js.map +1 -0
  184. package/dist/detectors/signals.d.ts.map +1 -1
  185. package/dist/detectors/signals.js +3 -1
  186. package/dist/detectors/signals.js.map +1 -1
  187. package/dist/detectors/step-time-sum.d.ts +3 -0
  188. package/dist/detectors/step-time-sum.d.ts.map +1 -0
  189. package/dist/detectors/step-time-sum.js +46 -0
  190. package/dist/detectors/step-time-sum.js.map +1 -0
  191. package/dist/detectors/structure-tree.d.ts +2 -0
  192. package/dist/detectors/structure-tree.d.ts.map +1 -1
  193. package/dist/detectors/structure-tree.js +22 -5
  194. package/dist/detectors/structure-tree.js.map +1 -1
  195. package/dist/detectors/structure.js +1 -1
  196. package/dist/detectors/structure.js.map +1 -1
  197. package/dist/detectors/technical-docs.d.ts +5 -0
  198. package/dist/detectors/technical-docs.d.ts.map +1 -1
  199. package/dist/detectors/technical-docs.js +12 -0
  200. package/dist/detectors/technical-docs.js.map +1 -1
  201. package/dist/detectors/time-order.d.ts +1 -0
  202. package/dist/detectors/time-order.d.ts.map +1 -1
  203. package/dist/detectors/time-order.js +3 -1
  204. package/dist/detectors/time-order.js.map +1 -1
  205. package/dist/detectors/unit-consistency.d.ts +1 -1
  206. package/dist/detectors/unit-consistency.d.ts.map +1 -1
  207. package/dist/detectors/unit-consistency.js +46 -38
  208. package/dist/detectors/unit-consistency.js.map +1 -1
  209. package/dist/detectors/unlisted-items.d.ts +3 -0
  210. package/dist/detectors/unlisted-items.d.ts.map +1 -0
  211. package/dist/detectors/unlisted-items.js +57 -0
  212. package/dist/detectors/unlisted-items.js.map +1 -0
  213. package/dist/detectors/use-count-words.d.ts +8 -0
  214. package/dist/detectors/use-count-words.d.ts.map +1 -0
  215. package/dist/detectors/use-count-words.js +20 -0
  216. package/dist/detectors/use-count-words.js.map +1 -0
  217. package/dist/detectors/use-interval.d.ts +3 -0
  218. package/dist/detectors/use-interval.d.ts.map +1 -0
  219. package/dist/detectors/use-interval.js +39 -0
  220. package/dist/detectors/use-interval.js.map +1 -0
  221. package/dist/detectors/walk-time-distance-mismatch.d.ts +4 -0
  222. package/dist/detectors/walk-time-distance-mismatch.d.ts.map +1 -0
  223. package/dist/detectors/walk-time-distance-mismatch.js +38 -0
  224. package/dist/detectors/walk-time-distance-mismatch.js.map +1 -0
  225. package/dist/facts/approximate-tolerance.d.ts +7 -0
  226. package/dist/facts/approximate-tolerance.d.ts.map +1 -0
  227. package/dist/facts/approximate-tolerance.js +16 -0
  228. package/dist/facts/approximate-tolerance.js.map +1 -0
  229. package/dist/facts/item-amounts.d.ts +18 -0
  230. package/dist/facts/item-amounts.d.ts.map +1 -0
  231. package/dist/facts/item-amounts.js +67 -0
  232. package/dist/facts/item-amounts.js.map +1 -0
  233. package/dist/facts/labelled-facts.d.ts +4 -0
  234. package/dist/facts/labelled-facts.d.ts.map +1 -1
  235. package/dist/facts/labelled-facts.js +21 -6
  236. package/dist/facts/labelled-facts.js.map +1 -1
  237. package/dist/facts/measure-units.d.ts +5 -0
  238. package/dist/facts/measure-units.d.ts.map +1 -0
  239. package/dist/facts/measure-units.js +28 -0
  240. package/dist/facts/measure-units.js.map +1 -0
  241. package/dist/facts/measures.d.ts +23 -4
  242. package/dist/facts/measures.d.ts.map +1 -1
  243. package/dist/facts/measures.js +68 -19
  244. package/dist/facts/measures.js.map +1 -1
  245. package/dist/facts/retention-periods.d.ts +51 -0
  246. package/dist/facts/retention-periods.d.ts.map +1 -0
  247. package/dist/facts/retention-periods.js +148 -0
  248. package/dist/facts/retention-periods.js.map +1 -0
  249. package/dist/facts/unit-conflicts.d.ts +3 -2
  250. package/dist/facts/unit-conflicts.d.ts.map +1 -1
  251. package/dist/facts/unit-conflicts.js +15 -7
  252. package/dist/facts/unit-conflicts.js.map +1 -1
  253. package/dist/facts/unit-equivalents.d.ts +13 -0
  254. package/dist/facts/unit-equivalents.d.ts.map +1 -0
  255. package/dist/facts/unit-equivalents.js +56 -0
  256. package/dist/facts/unit-equivalents.js.map +1 -0
  257. package/dist/facts/unit-tolerance.d.ts +4 -0
  258. package/dist/facts/unit-tolerance.d.ts.map +1 -0
  259. package/dist/facts/unit-tolerance.js +13 -0
  260. package/dist/facts/unit-tolerance.js.map +1 -0
  261. package/dist/facts/unit-word.d.ts +8 -0
  262. package/dist/facts/unit-word.d.ts.map +1 -0
  263. package/dist/facts/unit-word.js +34 -0
  264. package/dist/facts/unit-word.js.map +1 -0
  265. package/dist/facts/written-precision.d.ts +10 -0
  266. package/dist/facts/written-precision.d.ts.map +1 -0
  267. package/dist/facts/written-precision.js +20 -0
  268. package/dist/facts/written-precision.js.map +1 -0
  269. package/dist/kana-spelling.d.ts +12 -0
  270. package/dist/kana-spelling.d.ts.map +1 -1
  271. package/dist/kana-spelling.js +23 -0
  272. package/dist/kana-spelling.js.map +1 -1
  273. package/dist/model-codes.d.ts +23 -0
  274. package/dist/model-codes.d.ts.map +1 -0
  275. package/dist/model-codes.js +81 -0
  276. package/dist/model-codes.js.map +1 -0
  277. package/dist/name-spelling-chars.d.ts +16 -0
  278. package/dist/name-spelling-chars.d.ts.map +1 -0
  279. package/dist/name-spelling-chars.js +14 -0
  280. package/dist/name-spelling-chars.js.map +1 -0
  281. package/dist/name-variants.d.ts +3 -2
  282. package/dist/name-variants.d.ts.map +1 -1
  283. package/dist/name-variants.js +4 -7
  284. package/dist/name-variants.js.map +1 -1
  285. package/dist/phone-numbers.d.ts.map +1 -1
  286. package/dist/phone-numbers.js +22 -5
  287. package/dist/phone-numbers.js.map +1 -1
  288. package/dist/place-names.d.ts +12 -5
  289. package/dist/place-names.d.ts.map +1 -1
  290. package/dist/place-names.js +34 -14
  291. package/dist/place-names.js.map +1 -1
  292. package/dist/plugin.d.ts +5 -0
  293. package/dist/plugin.d.ts.map +1 -1
  294. package/dist/preamble-paragraphs.d.ts +2 -1
  295. package/dist/preamble-paragraphs.d.ts.map +1 -1
  296. package/dist/preamble-paragraphs.js +16 -1
  297. package/dist/preamble-paragraphs.js.map +1 -1
  298. package/dist/profile/parse.d.ts.map +1 -1
  299. package/dist/profile/parse.js +11 -0
  300. package/dist/profile/parse.js.map +1 -1
  301. package/dist/spacing-code.d.ts +6 -0
  302. package/dist/spacing-code.d.ts.map +1 -0
  303. package/dist/spacing-code.js +16 -0
  304. package/dist/spacing-code.js.map +1 -0
  305. package/dist/structure/amount-range.d.ts +2 -2
  306. package/dist/structure/amount-range.d.ts.map +1 -1
  307. package/dist/structure/amount-range.js +12 -5
  308. package/dist/structure/amount-range.js.map +1 -1
  309. package/dist/structure/capacity-count.d.ts +59 -0
  310. package/dist/structure/capacity-count.d.ts.map +1 -0
  311. package/dist/structure/capacity-count.js +118 -0
  312. package/dist/structure/capacity-count.js.map +1 -0
  313. package/dist/structure/counted-amounts.d.ts +6 -0
  314. package/dist/structure/counted-amounts.d.ts.map +1 -1
  315. package/dist/structure/counted-amounts.js +4 -4
  316. package/dist/structure/counted-amounts.js.map +1 -1
  317. package/dist/structure/date-outside-period.d.ts +3 -1
  318. package/dist/structure/date-outside-period.d.ts.map +1 -1
  319. package/dist/structure/date-outside-period.js +8 -5
  320. package/dist/structure/date-outside-period.js.map +1 -1
  321. package/dist/structure/date-range.d.ts +1 -0
  322. package/dist/structure/date-range.d.ts.map +1 -1
  323. package/dist/structure/date-range.js +1 -1
  324. package/dist/structure/date-range.js.map +1 -1
  325. package/dist/structure/deadline-after-event.d.ts +23 -0
  326. package/dist/structure/deadline-after-event.d.ts.map +1 -0
  327. package/dist/structure/deadline-after-event.js +42 -0
  328. package/dist/structure/deadline-after-event.js.map +1 -0
  329. package/dist/structure/definition-use.d.ts +1 -1
  330. package/dist/structure/definition-use.d.ts.map +1 -1
  331. package/dist/structure/definition-use.js +55 -2
  332. package/dist/structure/definition-use.js.map +1 -1
  333. package/dist/structure/due-date.d.ts.map +1 -1
  334. package/dist/structure/due-date.js +5 -27
  335. package/dist/structure/due-date.js.map +1 -1
  336. package/dist/structure/labelled-date-order.d.ts +38 -0
  337. package/dist/structure/labelled-date-order.d.ts.map +1 -0
  338. package/dist/structure/labelled-date-order.js +41 -0
  339. package/dist/structure/labelled-date-order.js.map +1 -0
  340. package/dist/structure/layout-codes.d.ts +68 -0
  341. package/dist/structure/layout-codes.d.ts.map +1 -0
  342. package/dist/structure/layout-codes.js +161 -0
  343. package/dist/structure/layout-codes.js.map +1 -0
  344. package/dist/structure/measured-amounts.d.ts +17 -0
  345. package/dist/structure/measured-amounts.d.ts.map +1 -0
  346. package/dist/structure/measured-amounts.js +110 -0
  347. package/dist/structure/measured-amounts.js.map +1 -0
  348. package/dist/structure/quantity-range.d.ts +8 -0
  349. package/dist/structure/quantity-range.d.ts.map +1 -0
  350. package/dist/structure/quantity-range.js +36 -0
  351. package/dist/structure/quantity-range.js.map +1 -0
  352. package/dist/structure/range-bands.d.ts +75 -0
  353. package/dist/structure/range-bands.d.ts.map +1 -0
  354. package/dist/structure/range-bands.js +244 -0
  355. package/dist/structure/range-bands.js.map +1 -0
  356. package/dist/structure/rent-multiple.d.ts +76 -0
  357. package/dist/structure/rent-multiple.d.ts.map +1 -0
  358. package/dist/structure/rent-multiple.js +186 -0
  359. package/dist/structure/rent-multiple.js.map +1 -0
  360. package/dist/structure/scaled-quantities.d.ts +50 -0
  361. package/dist/structure/scaled-quantities.d.ts.map +1 -0
  362. package/dist/structure/scaled-quantities.js +153 -0
  363. package/dist/structure/scaled-quantities.js.map +1 -0
  364. package/dist/structure/stamp-order.d.ts +16 -0
  365. package/dist/structure/stamp-order.d.ts.map +1 -0
  366. package/dist/structure/stamp-order.js +78 -0
  367. package/dist/structure/stamp-order.js.map +1 -0
  368. package/dist/structure/step-times.d.ts +49 -0
  369. package/dist/structure/step-times.d.ts.map +1 -0
  370. package/dist/structure/step-times.js +128 -0
  371. package/dist/structure/step-times.js.map +1 -0
  372. package/dist/structure/term-deadline.d.ts +19 -0
  373. package/dist/structure/term-deadline.d.ts.map +1 -0
  374. package/dist/structure/term-deadline.js +28 -0
  375. package/dist/structure/term-deadline.js.map +1 -0
  376. package/dist/structure/time-range.d.ts +16 -0
  377. package/dist/structure/time-range.d.ts.map +1 -0
  378. package/dist/structure/time-range.js +57 -0
  379. package/dist/structure/time-range.js.map +1 -0
  380. package/dist/structure/total-line.d.ts +16 -0
  381. package/dist/structure/total-line.d.ts.map +1 -0
  382. package/dist/structure/total-line.js +44 -0
  383. package/dist/structure/total-line.js.map +1 -0
  384. package/dist/structure/total.d.ts +7 -1
  385. package/dist/structure/total.d.ts.map +1 -1
  386. package/dist/structure/total.js +28 -5
  387. package/dist/structure/total.js.map +1 -1
  388. package/dist/structure/unlisted-items.d.ts +70 -0
  389. package/dist/structure/unlisted-items.d.ts.map +1 -0
  390. package/dist/structure/unlisted-items.js +152 -0
  391. package/dist/structure/unlisted-items.js.map +1 -0
  392. package/dist/structure/walk-time.d.ts +87 -0
  393. package/dist/structure/walk-time.d.ts.map +1 -0
  394. package/dist/structure/walk-time.js +195 -0
  395. package/dist/structure/walk-time.js.map +1 -0
  396. package/package.json +3 -3
  397. package/profiles/listing.yaml +20 -0
  398. package/rules/announced-count-mismatch.yaml +2 -2
  399. package/rules/capacity-runtime-mismatch.yaml +66 -0
  400. package/rules/contact-variant.yaml +76 -0
  401. package/rules/count-over-capacity.yaml +55 -0
  402. package/rules/date-outside-period.yaml +4 -4
  403. package/rules/date-stamp-order.yaml +82 -0
  404. package/rules/deadline-after-event.yaml +67 -0
  405. package/rules/defined-name-repeated.yaml +2 -2
  406. package/rules/due-before-issue.yaml +11 -7
  407. package/rules/fact-conflict.yaml +9 -4
  408. package/rules/heading-echo.yaml +2 -2
  409. package/rules/interval-count-mismatch.yaml +54 -0
  410. package/rules/latin-spacing.yaml +2 -2
  411. package/rules/layout-code-mismatch.yaml +68 -0
  412. package/rules/name-variant.yaml +8 -2
  413. package/rules/no-em-dash.yaml +2 -2
  414. package/rules/preamble-length.yaml +2 -2
  415. package/rules/quantity-range-reversed.yaml +53 -0
  416. package/rules/range-band-mismatch.yaml +70 -0
  417. package/rules/rent-multiple-mismatch.yaml +61 -0
  418. package/rules/scaled-quantity-mismatch.yaml +69 -0
  419. package/rules/step-time-sum-mismatch.yaml +65 -0
  420. package/rules/time-range-reversed.yaml +48 -0
  421. package/rules/title-case-consistency.yaml +2 -2
  422. package/rules/total-mismatch.yaml +2 -2
  423. package/rules/unit-mismatch.yaml +12 -4
  424. package/rules/unlisted-item-used.yaml +90 -0
  425. package/rules/unused-definition.yaml +2 -2
  426. package/rules/use-before-definition.yaml +2 -2
  427. package/rules/walk-time-distance-mismatch.yaml +55 -0
  428. package/src/announced-count.ts +6 -3
  429. package/src/contacts/addresses.ts +179 -0
  430. package/src/contacts/emails.ts +63 -0
  431. package/src/contacts/variants.ts +33 -0
  432. package/src/derived/battery-runtime.ts +284 -0
  433. package/src/derived/period-counts.ts +178 -0
  434. package/src/derived/time-lengths.ts +12 -9
  435. package/src/derived/use-interval.ts +151 -0
  436. package/src/detectors/announced-count.ts +1 -0
  437. package/src/detectors/capacity-runtime.ts +128 -0
  438. package/src/detectors/contact-variant.ts +24 -0
  439. package/src/detectors/count-over-capacity.ts +37 -0
  440. package/src/detectors/date-outside-period.ts +2 -1
  441. package/src/detectors/date-stamp-order.ts +28 -0
  442. package/src/detectors/defined-term-form.ts +63 -7
  443. package/src/detectors/definition-use.ts +6 -2
  444. package/src/detectors/derived-numbers.ts +3 -3
  445. package/src/detectors/due-date.ts +10 -2
  446. package/src/detectors/en-shape.ts +11 -4
  447. package/src/detectors/event-deadline.ts +29 -0
  448. package/src/detectors/fact-consistency.ts +64 -3
  449. package/src/detectors/heading-case.ts +13 -4
  450. package/src/detectors/heading-echo.ts +17 -1
  451. package/src/detectors/layout-code.ts +174 -0
  452. package/src/detectors/measured-facts.ts +20 -0
  453. package/src/detectors/name-variant-model-codes.ts +27 -0
  454. package/src/detectors/name-variant.ts +32 -6
  455. package/src/detectors/orthographic-variant.ts +78 -9
  456. package/src/detectors/orthography.ts +16 -6
  457. package/src/detectors/quantity-range.ts +47 -0
  458. package/src/detectors/range-band.ts +125 -0
  459. package/src/detectors/registry/capacity-runtime-mismatch.ts +4 -0
  460. package/src/detectors/registry/contact-variant.ts +4 -0
  461. package/src/detectors/registry/count-over-capacity.ts +4 -0
  462. package/src/detectors/registry/date-stamp-order.ts +4 -0
  463. package/src/detectors/registry/event-deadline.ts +4 -0
  464. package/src/detectors/registry/interval-count-mismatch.ts +4 -0
  465. package/src/detectors/registry/layout-code-mismatch.ts +4 -0
  466. package/src/detectors/registry/quantity-range.ts +4 -0
  467. package/src/detectors/registry/range-band-mismatch.ts +4 -0
  468. package/src/detectors/registry/rent-multiple-mismatch.ts +4 -0
  469. package/src/detectors/registry/scaled-quantity-mismatch.ts +4 -0
  470. package/src/detectors/registry/step-time-sum-mismatch.ts +4 -0
  471. package/src/detectors/registry/time-range-reversed.ts +4 -0
  472. package/src/detectors/registry/unlisted-item-used.ts +4 -0
  473. package/src/detectors/registry/walk-time-distance-mismatch.ts +4 -0
  474. package/src/detectors/rent-multiple-mismatch.ts +56 -0
  475. package/src/detectors/scaled-quantity.ts +49 -0
  476. package/src/detectors/signals.ts +3 -1
  477. package/src/detectors/step-time-sum.ts +55 -0
  478. package/src/detectors/structure-tree.ts +30 -5
  479. package/src/detectors/structure.ts +1 -1
  480. package/src/detectors/technical-docs.ts +12 -0
  481. package/src/detectors/time-order.ts +5 -1
  482. package/src/detectors/unit-consistency.ts +50 -38
  483. package/src/detectors/unlisted-items.ts +72 -0
  484. package/src/detectors/use-count-words.ts +32 -0
  485. package/src/detectors/use-interval.ts +51 -0
  486. package/src/detectors/walk-time-distance-mismatch.ts +45 -0
  487. package/src/facts/approximate-tolerance.ts +19 -0
  488. package/src/facts/item-amounts.ts +94 -0
  489. package/src/facts/labelled-facts.ts +29 -7
  490. package/src/facts/measure-units.ts +31 -0
  491. package/src/facts/measures.ts +113 -25
  492. package/src/facts/retention-periods.ts +216 -0
  493. package/src/facts/unit-conflicts.ts +22 -9
  494. package/src/facts/unit-equivalents.ts +70 -0
  495. package/src/facts/unit-tolerance.ts +17 -0
  496. package/src/facts/unit-word.ts +35 -0
  497. package/src/facts/written-precision.ts +23 -0
  498. package/src/kana-spelling.ts +25 -0
  499. package/src/model-codes.ts +104 -0
  500. package/src/name-spelling-chars.ts +34 -0
  501. package/src/name-variants.ts +8 -7
  502. package/src/phone-numbers.ts +26 -6
  503. package/src/place-names.ts +53 -14
  504. package/src/plugin.ts +2 -0
  505. package/src/preamble-paragraphs.ts +21 -2
  506. package/src/profile/parse.ts +10 -0
  507. package/src/spacing-code.ts +26 -0
  508. package/src/structure/amount-range.ts +16 -7
  509. package/src/structure/capacity-count.ts +196 -0
  510. package/src/structure/counted-amounts.ts +5 -4
  511. package/src/structure/date-outside-period.ts +11 -6
  512. package/src/structure/date-range.ts +1 -1
  513. package/src/structure/deadline-after-event.ts +67 -0
  514. package/src/structure/definition-use.ts +59 -2
  515. package/src/structure/due-date.ts +8 -32
  516. package/src/structure/labelled-date-order.ts +84 -0
  517. package/src/structure/layout-codes.ts +210 -0
  518. package/src/structure/measured-amounts.ts +138 -0
  519. package/src/structure/quantity-range.ts +54 -0
  520. package/src/structure/range-bands.ts +316 -0
  521. package/src/structure/rent-multiple.ts +277 -0
  522. package/src/structure/scaled-quantities.ts +225 -0
  523. package/src/structure/stamp-order.ts +100 -0
  524. package/src/structure/step-times.ts +169 -0
  525. package/src/structure/term-deadline.ts +35 -0
  526. package/src/structure/time-range.ts +64 -0
  527. package/src/structure/total-line.ts +63 -0
  528. package/src/structure/total.ts +41 -6
  529. package/src/structure/unlisted-items.ts +207 -0
  530. package/src/structure/walk-time.ts +285 -0
@@ -371,7 +371,9 @@ export const concreteEvidence: Detector = (doc, options): Finding[] => {
371
371
  * ダッシュの多用。英語では正当な用法が多いが、2026 年時点で最も知られた生成文のシグナルでもある。
372
372
  * 日本語の組版ではそもそも扱いが難しい。どちらが正しいかは severity の言語別指定で分ける。spec §12.4。
373
373
  */
374
- const DASH = /[\u2014\u2015\u2013]/gu;
374
+ // An en dash between two digits (45–58, 10:00–10:15) is a range mark, the typographer's correct choice, not a dash that
375
+ // sets off words.
376
+ const DASH = /[\u2014\u2015]|(?<!\p{Nd})\u2013|\u2013(?!\p{Nd})/gu;
375
377
 
376
378
  export const dashDensity: Detector = (doc, options): Finding[] => {
377
379
  const hits = findIn(doc, DASH);
@@ -0,0 +1,55 @@
1
+ // A stated total time (所要時間:60分, Total time: 1 hr) that is not the sum of the times written in the numbered steps. The
2
+ // units, the total labels, the rough-figure marks and the marks of a time that is not a step's own come from the language packages.
3
+ import { stepTimeSlip, type StepTimeWords } from "../structure/step-times.ts";
4
+ import type { Mark } from "../structure/time-marks.ts";
5
+ import { quoteAt } from "./structure-tree.ts";
6
+ import type { Detector, Finding, ProseDocument } from "../plugin.ts";
7
+
8
+ const SECONDS_PER_HOUR = 3600;
9
+ const SECONDS_PER_MINUTE = 60;
10
+
11
+ const patternsOf = (doc: ProseDocument, id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
12
+
13
+ const marksOf = (doc: ProseDocument, id: string): Mark[] =>
14
+ (doc.lexicons[id] ?? []).map((entry) => ({ pattern: entry.pattern, position: entry.position, group: entry.group }));
15
+
16
+ const unitsWeighing = (doc: ProseDocument, seconds: number): string[] =>
17
+ (doc.lexicons["unit-time"] ?? []).filter((entry) => entry.weight === seconds).map((entry) => entry.pattern);
18
+
19
+ const RANGE_GROUP = "range";
20
+
21
+ const wordsOf = (doc: ProseDocument): StepTimeWords => {
22
+ const notOwn = marksOf(doc, "step-time-not-own");
23
+ return {
24
+ lengths: {
25
+ hourUnits: unitsWeighing(doc, SECONDS_PER_HOUR),
26
+ minuteUnits: unitsWeighing(doc, SECONDS_PER_MINUTE),
27
+ halves: patternsOf(doc, "length-half"),
28
+ numberWords: patternsOf(doc, "count-number"),
29
+ },
30
+ totals: marksOf(doc, "step-time-total"),
31
+ approximate: marksOf(doc, "approximate-marker"),
32
+ notOwn: notOwn.filter((mark) => mark.group !== RANGE_GROUP),
33
+ rangeJoiners: notOwn.filter((mark) => mark.group === RANGE_GROUP).map((mark) => mark.pattern),
34
+ };
35
+ };
36
+
37
+ export const stepTimeSumMismatch: Detector = (doc): Finding[] => {
38
+ const slip = stepTimeSlip(doc.source, wordsOf(doc));
39
+ if (slip === undefined) return [];
40
+ return [
41
+ {
42
+ rule: "step-time-sum-mismatch",
43
+ severity: "warning",
44
+ line: 0,
45
+ column: 0,
46
+ quote: quoteAt(doc.source, slip.total.start),
47
+ values: {
48
+ written: doc.source.slice(slip.total.start, slip.total.end),
49
+ sum: slip.sum,
50
+ steps: slip.steps,
51
+ offset: slip.total.start,
52
+ },
53
+ },
54
+ ];
55
+ };
@@ -6,8 +6,11 @@ import { weekdayMismatches } from "../structure/weekday.ts";
6
6
  import { documentDateOf } from "./document-date.ts";
7
7
  import { dateOrderBreaks } from "../structure/date-order.ts";
8
8
  import { totalMismatches, type Amount } from "../structure/total.ts";
9
+ import { isTotalLine } from "../structure/total-line.ts";
9
10
  import { proseTotalMismatches } from "../structure/prose-total.ts";
10
11
  import { countedAmounts, type SummedCounter } from "../structure/counted-amounts.ts";
12
+ import { measuredAmounts, type MeasureMarks, type SummedMeasure } from "../structure/measured-amounts.ts";
13
+ import { measureUnitsOf } from "../facts/measure-units.ts";
11
14
  import { rangeFrameOf, reversedRanges, type DatedSpan, type RangeWords } from "../structure/date-range.ts";
12
15
  import { afterLabel } from "../structure/stated-period.ts";
13
16
  import { percentSumMismatches, type ShareWords } from "../structure/percent-sum.ts";
@@ -184,11 +187,33 @@ const summableAmountsOf = (doc: ProseDocument, tree: NonNullable<ProseDocument["
184
187
  return [...known, ...countedAmounts(doc.source, summedCountersOf(doc), known)].toSorted((left, right) => left.offset - right.offset);
185
188
  };
186
189
 
187
- /** 合計の行と内訳の行、文の中の合計と内訳。同じ金額は一度だけ言う。 */
190
+ /** 合計の行で足す量の種類。換算の倍率は語彙表の weight。 */
191
+ const SUMMED_DIMENSIONS = ["unit-mass", "unit-volume", "unit-length"] as const;
192
+
193
+ /** 足す単位。零点のずれた単位(°F)は足せないので除く。 */
194
+ const summedMeasuresOf = (doc: ProseDocument): SummedMeasure[] =>
195
+ measureUnitsOf(doc).filter((unit) => unit.zero === 0 && SUMMED_DIMENSIONS.some((dimension) => dimension === unit.dimension));
196
+
197
+ const positionedOf = (doc: ProseDocument, lexicon: string, position: "before" | "after"): string[] =>
198
+ (doc.lexicons[lexicon] ?? []).filter((entry) => (entry.position ?? "before") === position).map((entry) => entry.pattern);
199
+
200
+ const measureMarksOf = (doc: ProseDocument): MeasureMarks => ({
201
+ roughBefore: positionedOf(doc, "approximate-marker", "before"),
202
+ roughAfter: positionedOf(doc, "approximate-marker", "after"),
203
+ connectors: patternsOf(doc, "range-connector"),
204
+ perMarks: patternsOf(doc, "measure-per-mark"),
205
+ });
206
+
207
+ /** 表と箇条書きの合計で足す数量。金額と助数詞の数に、単位の付いた量(300 mg、0.5 g)を加える。 */
208
+ const lineAmountsOf = (doc: ProseDocument, amounts: readonly Amount[]): Amount[] =>
209
+ [...amounts, ...measuredAmounts(doc.source, summedMeasuresOf(doc), measureMarksOf(doc), amounts)].toSorted((left, right) => left.offset - right.offset);
210
+
211
+ /** 合計の行と内訳の行、文の中の合計と内訳。同じ金額は一度だけ言う。単位の付いた量は、表と箇条書きの合計だけで足す。 */
188
212
  const totalIssues = (doc: ProseDocument, tree: NonNullable<ProseDocument["structure"]>): StructureIssue[] => {
189
213
  const amounts = summableAmountsOf(doc, tree);
190
- const lines = totalMismatches(doc.source, amounts, patternsOf(doc, "total-label"));
191
- const words = {
214
+ const words = { labels: patternsOf(doc, "total-label"), qualifiers: doc.lexicons["total-label-qualifier"] ?? [] };
215
+ const lines = totalMismatches(doc.source, lineAmountsOf(doc, amounts), (text) => isTotalLine(text, words));
216
+ const phrases = {
192
217
  totals: patternsOf(doc, "total-phrase"),
193
218
  breakdowns: patternsOf(doc, "breakdown-phrase"),
194
219
  discounts: patternsOf(doc, "discount-word"),
@@ -198,7 +223,7 @@ const totalIssues = (doc: ProseDocument, tree: NonNullable<ProseDocument["struct
198
223
  doc.source,
199
224
  doc.sentences.map((sentence) => sentence.span),
200
225
  amounts,
201
- words,
226
+ phrases,
202
227
  );
203
228
  return [...lines, ...prose.filter((issue) => !lines.some((line) => line.offset === issue.offset))];
204
229
  };
@@ -219,7 +244,7 @@ export const totalMismatch: Detector = (doc): Finding[] =>
219
244
  values: { ...issue.values, offset: issue.offset },
220
245
  }));
221
246
 
222
- const rangeWordsOf = (doc: ProseDocument): RangeWords => ({
247
+ export const rangeWordsOf = (doc: ProseDocument): RangeWords => ({
223
248
  connectors: (doc.lexicons["range-connector"] ?? []).map((entry) => entry.pattern),
224
249
  openers: (doc.lexicons["range-opener"] ?? []).map((entry) => entry.pattern),
225
250
  closers: (doc.lexicons["range-closer"] ?? []).map((entry) => entry.pattern),
@@ -138,7 +138,7 @@ const withoutDateStamps = (doc: ProseDocument, paragraphs: readonly Paragraph[])
138
138
  export const preambleLength: Detector = (doc, options): Finding[] => {
139
139
  const body = bodySectionOf(doc.sections);
140
140
  if (body === undefined) return [];
141
- const all = preambleParagraphs(doc.paragraphs, body.span.start);
141
+ const all = preambleParagraphs(doc.paragraphs, body.span.start, doc.source);
142
142
  if (all.length <= options.limit) return [];
143
143
  const before = withoutDateStamps(doc, all);
144
144
  if (before.length <= options.limit) return [];
@@ -191,6 +191,17 @@ const isInflected = (token: Token): boolean =>
191
191
  token.lemma !== undefined && token.lemma !== "" && token.surface.toLowerCase() !== token.lemma && token.features?.["VerbForm"] !== "Part";
192
192
 
193
193
  const NOMINAL = new Set(["NOUN", "PROPN"]);
194
+ /** What an adjective at the head of a subject goes on to: the noun it describes, or another word describing it. */
195
+ const DESCRIBED = new Set(["NOUN", "PROPN", "ADJ"]);
196
+
197
+ /**
198
+ * An adjective opening a subject is followed by what it describes (Each user, Large files). One followed by a preposition
199
+ * or an article is an imperative the tagger read as an adjective (Bake in the oven, Open the file).
200
+ */
201
+ export const opensWithMisreadImperative = (tokens: readonly Token[]): boolean => {
202
+ const [head, next] = tokens.filter((token) => !SKIPPED.has(token.pos));
203
+ return head?.pos === "ADJ" && !DESCRIBED.has(next?.pos ?? "");
204
+ };
194
205
 
195
206
  /** A determiner right after a noun opens a clause inside the phrase (a buffer that is big): the verb after it is the clause's, not a subject's. */
196
207
  const opensClause = (words: readonly Token[]): boolean => words.some((token, at) => at > 0 && token.pos === "DET" && NOMINAL.has(words[at - 1]?.pos ?? ""));
@@ -204,6 +215,7 @@ export const isStatement = (tokens: readonly Token[]): boolean => {
204
215
  const words = tokens.filter((token) => !SKIPPED.has(token.pos));
205
216
  const [head, ...rest] = words;
206
217
  if (head === undefined || words.length < MIN_STEP_TOKENS || !SUBJECT_HEAD.has(head.pos)) return false;
218
+ if (opensWithMisreadImperative(words)) return false;
207
219
  const verbAt = rest.findIndex((token) => PREDICATE.has(token.pos));
208
220
  const verb = rest[verbAt];
209
221
  const subject = [head, ...rest.slice(0, verbAt)];
@@ -7,7 +7,8 @@ import { scheduleDays, timeOrderBreaks } from "../structure/time-order.ts";
7
7
  import { arrivalOffsets, arrivalsBeforeDeparture } from "../structure/leg-times.ts";
8
8
  import { shortConnections, type TravelLength, type TravelWords } from "../structure/connection-times.ts";
9
9
  import { timeLengths } from "../derived/time-lengths.ts";
10
- import { quoteAt } from "./structure-tree.ts";
10
+ import { reversedTimeRanges } from "../structure/time-range.ts";
11
+ import { quoteAt, rangeWordsOf } from "./structure-tree.ts";
11
12
 
12
13
  // 一日の予定の時刻の順番(time-order)、発より前の着(arrival-before-departure)、移動の時間より短い乗り継ぎ(connection-time-short)。時刻は compare/clock-time.ts が読み、
13
14
  // 発着・翌日・時間帯の語は言語パッケージの語彙表(leg-mark、leg-column、day-shift-mark、time-zone、local-time-mark)から取る。
@@ -49,6 +50,9 @@ const detectorOf =
49
50
 
50
51
  export const timeOrder: Detector = detectorOf("time-order", timeOrderBreaks);
51
52
  export const arrivalBeforeDeparture: Detector = detectorOf("arrival-before-departure", arrivalsBeforeDeparture);
53
+ export const timeRangeReversed: Detector = detectorOf("time-range-reversed", (source, times, _dates, words, doc) =>
54
+ reversedTimeRanges(source, times, rangeWordsOf(doc), words),
55
+ );
52
56
 
53
57
  const SECONDS_PER_HOUR = 3600;
54
58
  const SECONDS_PER_MINUTE = 60;
@@ -1,55 +1,67 @@
1
1
  import type { Detector, Finding, ProseDocument, StructureNode } from "../plugin.ts";
2
- import { nameSpans } from "../compare/proper-nouns.ts";
3
- import { overlapsAny, spanIndex } from "../compare/spans.ts";
4
- import { factValues, type FactValue } from "../facts/fact-values.ts";
5
2
  import { labelledFacts } from "../facts/labelled-facts.ts";
6
3
  import { tableFacts } from "../facts/table-facts.ts";
7
4
  import { scopedFacts } from "../facts/fact-scope.ts";
8
- import { measuredValues, type Measured, type MeasureUnit } from "../facts/measures.ts";
5
+ import type { Measured } from "../facts/measures.ts";
9
6
  import { unitConflicts } from "../facts/unit-conflicts.ts";
7
+ import { unitPairs } from "../facts/unit-equivalents.ts";
8
+ import { itemConflicts } from "../facts/item-amounts.ts";
10
9
  import { factWordsOf } from "./fact-consistency.ts";
10
+ import { measuredOf, valuesWith } from "./measured-facts.ts";
11
11
  import { quoteAt } from "./structure-tree.ts";
12
12
 
13
- /** 量の種類ごとの語彙表。weight が基準の単位への倍率で、同じ単位を二度書けば倍率が二つ(GB の 10^9 と 2^30)。 */
14
- const DIMENSIONS = ["unit-length", "unit-mass", "unit-time", "unit-volume", "unit-data"] as const;
13
+ const patternsOf = (doc: ProseDocument, id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
15
14
 
16
- const unitsOf = (doc: ProseDocument): MeasureUnit[] =>
17
- DIMENSIONS.flatMap((dimension) => {
18
- const factors = new Map<string, number[]>();
19
- (doc.lexicons[dimension] ?? []).forEach((entry) => {
20
- if (entry.weight !== undefined) factors.set(entry.pattern, [...(factors.get(entry.pattern) ?? []), entry.weight]);
21
- });
22
- return [...factors.entries()].map(([pattern, weights]) => ({ pattern, dimension, factors: weights }));
23
- });
15
+ type Mismatch = { readonly label: string; readonly measured: Measured; readonly other: Measured; readonly variant?: string };
24
16
 
25
- /** fact-conflict と同じ値に、単位の付いた量を足す。量と重なる値(量の数だけを読んだもの)は量に置き換える。 */
26
- const valuesWith = (tree: StructureNode, doc: ProseDocument, measured: readonly Measured[]): FactValue[] => {
27
- const taken = spanIndex(measured);
28
- const others = factValues(tree, doc.source, nameSpans(doc)).filter((value) => !overlapsAny(taken, value));
29
- return [...others, ...measured].toSorted((left, right) => left.start - right.start);
17
+ /** 同じ範囲の同じ名前の量(「距離:5 km」と「距離:3000 m」)。名前と量のあいだの目安の語(「重さは約1.2kg」)は外して読む。 */
18
+ const labelledMismatches = (doc: ProseDocument, tree: StructureNode, measured: readonly Measured[]): Mismatch[] => {
19
+ const byStart = new Map(measured.map((value) => [value.start, value]));
20
+ const values = valuesWith(tree, doc, measured);
21
+ const words = { ...factWordsOf(doc), valueLeads: patternsOf(doc, "measure-value-lead") };
22
+ const facts = [...labelledFacts(doc.source, values, words), ...tableFacts(doc.source, values)];
23
+ const scoped = scopedFacts(facts, tree, doc.source, []);
24
+ return unitConflicts(scoped, (fact) => byStart.get(fact.value.start), doc.source).map((conflict) => ({
25
+ label: conflict.fact.label,
26
+ measured: conflict.measured,
27
+ other: conflict.other,
28
+ }));
30
29
  };
31
30
 
32
- /** 同じ名前の量を、違う単位で書いて、換算すると合わない。 */
31
+ const mismatchesOf = (doc: ProseDocument, tree: StructureNode, measured: readonly Measured[]): Mismatch[] => [
32
+ ...labelledMismatches(doc, tree, measured),
33
+ ...unitPairs(doc.source, measured, patternsOf(doc, "unit-equivalent-hedge")).map((pair) => ({ label: "", ...pair, variant: "bracket" })),
34
+ ...itemConflicts(doc.source, measured, { links: patternsOf(doc, "unit-item-link"), determiners: patternsOf(doc, "fact-label-drop") }).map((conflict) => ({
35
+ label: conflict.name,
36
+ measured: conflict.measured,
37
+ other: conflict.other,
38
+ variant: "item",
39
+ })),
40
+ ];
41
+
42
+ const findingOf = (doc: ProseDocument, mismatch: Mismatch): Finding => ({
43
+ rule: "unit-mismatch",
44
+ severity: "warning",
45
+ line: 0,
46
+ column: 0,
47
+ quote: quoteAt(doc.source, mismatch.measured.start),
48
+ values: {
49
+ label: mismatch.label,
50
+ value: doc.source.slice(mismatch.measured.start, mismatch.measured.end),
51
+ other: doc.source.slice(mismatch.other.start, mismatch.other.end),
52
+ offset: mismatch.measured.start,
53
+ },
54
+ ...(mismatch.variant === undefined ? {} : { variant: mismatch.variant }),
55
+ });
56
+
57
+ /** 同じ量を、違う単位で書いて、換算すると合わない。一つの量は一度だけ言う。 */
33
58
  export const unitMismatch: Detector = (doc): Finding[] => {
34
59
  const tree = doc.structure;
35
60
  if (tree === undefined) return [];
36
- const measured = measuredValues(doc.source, unitsOf(doc));
61
+ const measured = measuredOf(doc);
37
62
  if (measured.length === 0) return [];
38
- const byStart = new Map(measured.map((value) => [value.start, value]));
39
- const values = valuesWith(tree, doc, measured);
40
- const facts = [...labelledFacts(doc.source, values, factWordsOf(doc)), ...tableFacts(doc.source, values)];
41
- const scoped = scopedFacts(facts, tree, doc.source, []);
42
- return unitConflicts(scoped, (fact) => byStart.get(fact.value.start)).map((conflict) => ({
43
- rule: "unit-mismatch",
44
- severity: "warning",
45
- line: 0,
46
- column: 0,
47
- quote: quoteAt(doc.source, conflict.measured.start),
48
- values: {
49
- label: conflict.fact.label,
50
- value: doc.source.slice(conflict.measured.start, conflict.measured.end),
51
- other: doc.source.slice(conflict.other.start, conflict.other.end),
52
- offset: conflict.measured.start,
53
- },
54
- }));
63
+ const mismatches = mismatchesOf(doc, tree, measured);
64
+ return mismatches
65
+ .filter((mismatch, index) => mismatches.findIndex((other) => other.measured.start === mismatch.measured.start) === index)
66
+ .map((mismatch) => findingOf(doc, mismatch));
55
67
  };
@@ -0,0 +1,72 @@
1
+ // unlisted-item-used: a numbered step uses a thing the document's list of what is needed does not name
2
+ // (structure/unlisted-items.ts). The words are the language package's item-list-heading, item-use, item-amount,
3
+ // item-at-hand and enumeration-joiner.
4
+ import type { Detector, Finding, ProseDocument, Token } from "../plugin.ts";
5
+ import { itemLists, unlistedItems, type ItemStep, type ItemToken, type ItemWords, type UseGroup, type UseMarker } from "../structure/unlisted-items.ts";
6
+ import { quoteAround } from "./quote-around.ts";
7
+
8
+ const USE_GROUPS: readonly UseGroup[] = ["object", "place", "instrument"];
9
+
10
+ const patternsOf = (doc: ProseDocument, lexicon: string, group?: string): string[] =>
11
+ (doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group).map((entry) => entry.pattern);
12
+
13
+ const markersOf = (doc: ProseDocument): UseMarker[] =>
14
+ (doc.lexicons["item-use"] ?? []).flatMap((entry) => {
15
+ const group = USE_GROUPS.find((use) => use === entry.group);
16
+ return group === undefined || entry.position === undefined ? [] : [{ word: entry.pattern, group, position: entry.position }];
17
+ });
18
+
19
+ const wordsOf = (doc: ProseDocument): ItemWords => ({
20
+ ingredientHeadings: patternsOf(doc, "item-list-heading", "ingredient"),
21
+ toolHeadings: patternsOf(doc, "item-list-heading", "tool"),
22
+ markers: markersOf(doc),
23
+ verbs: Object.fromEntries(USE_GROUPS.map((group) => [group, patternsOf(doc, "item-use", `${group}-verb`)])),
24
+ negations: patternsOf(doc, "item-use", "negation"),
25
+ actions: patternsOf(doc, "item-use", "action"),
26
+ joiners: patternsOf(doc, "enumeration-joiner"),
27
+ partitives: patternsOf(doc, "item-use", "partitive"),
28
+ amounts: patternsOf(doc, "item-amount"),
29
+ atHand: patternsOf(doc, "item-at-hand"),
30
+ });
31
+
32
+ const NUMBERED_LINE = /^[ \t]*\d+[.).)][ \t]+/gmu;
33
+
34
+ const itemToken = (token: Token): ItemToken => ({
35
+ surface: token.surface,
36
+ pos: token.pos,
37
+ lemma: token.lemma ?? token.surface,
38
+ start: token.span.start,
39
+ end: token.span.end,
40
+ });
41
+
42
+ /** Each numbered line, with the words of the sentences that start on it. */
43
+ const stepsOf = (doc: ProseDocument): ItemStep[] =>
44
+ [...doc.source.matchAll(NUMBERED_LINE)].map((match) => {
45
+ const lineEnd = doc.source.indexOf("\n", match.index);
46
+ const end = lineEnd === -1 ? doc.source.length : lineEnd;
47
+ const sentences = doc.sentences.filter((sentence) => sentence.span.start >= match.index && sentence.span.start < end);
48
+ return { start: match.index, tokens: sentences.flatMap((sentence) => (sentence.tokens ?? []).map(itemToken)) };
49
+ });
50
+
51
+ const headingsOf = (doc: ProseDocument): string[] => doc.sections.filter((section) => section.depth > 0).map((section) => section.heading);
52
+
53
+ /** Every word the tagger read, in the text and in the table cells (a list of ingredients is often a table). */
54
+ const wordsWritten = (doc: ProseDocument): ItemToken[] => [
55
+ ...doc.sentences.flatMap((sentence) => (sentence.tokens ?? []).map(itemToken)),
56
+ ...(doc.tableCells?.() ?? []).flatMap((cell) => (cell.tokens ?? []).map(itemToken)),
57
+ ];
58
+
59
+ export const unlistedItemUsed: Detector = (doc): Finding[] => {
60
+ const words = wordsOf(doc);
61
+ const headings = headingsOf(doc);
62
+ // Reading every table cell is not free; a document without a list heading needs none of it.
63
+ if (itemLists(headings, words).length === 0) return [];
64
+ return unlistedItems({ source: doc.source, words: wordsWritten(doc) }, headings, stepsOf(doc), words).map((item) => ({
65
+ rule: "unlisted-item-used",
66
+ severity: "warning",
67
+ line: 0,
68
+ column: 0,
69
+ quote: quoteAround(doc.source, item.offset, item.offset + item.item.length),
70
+ values: { item: item.item, list: item.list, offset: item.offset },
71
+ }));
72
+ };
@@ -0,0 +1,32 @@
1
+ import type { ProseDocument } from "../plugin.ts";
2
+ import type { Mark } from "../structure/time-marks.ts";
3
+ import type { CountWord, LimitWords, PeriodCountWords, PeriodWord } from "../derived/period-counts.ts";
4
+
5
+ // 期間あたりの回数の語(use-count-word)と、語で書いた数(count-number)。product-exceeds-maximum と interval-count-mismatch が読む。
6
+
7
+ const entriesOf = (doc: ProseDocument, lexicon: string, group?: string) =>
8
+ (doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group);
9
+
10
+ export const marksOf = (doc: ProseDocument, lexicon: string, group?: string): Mark[] =>
11
+ entriesOf(doc, lexicon, group).map((entry) => ({ pattern: entry.pattern, position: entry.position, group: entry.group }));
12
+
13
+ const periodsOf = (doc: ProseDocument): PeriodWord[] =>
14
+ entriesOf(doc, "use-count-word", "period").map((entry) => ({ pattern: entry.pattern, position: entry.position, hours: entry.weight ?? 0 }));
15
+
16
+ /** one, two, …: the position in count-number is the value. once, twice: the weight in use-count-word. */
17
+ const numberWordsOf = (doc: ProseDocument): CountWord[] => [
18
+ ...entriesOf(doc, "count-number").map((entry, index) => ({ pattern: entry.pattern, value: index + 1, alone: false })),
19
+ ...entriesOf(doc, "use-count-word", "alone").map((entry) => ({ pattern: entry.pattern, value: entry.weight ?? 0, alone: true })),
20
+ ];
21
+
22
+ export const periodCountWordsOf = (doc: ProseDocument): PeriodCountWords => ({
23
+ periods: periodsOf(doc),
24
+ counters: entriesOf(doc, "use-count-word", "counter").map((entry) => entry.pattern),
25
+ numberWords: numberWordsOf(doc),
26
+ });
27
+
28
+ /** The limit words, the ones that need a negation before them ("more than"), and the negations. */
29
+ export const limitWordsOf = (doc: ProseDocument): LimitWords => ({
30
+ limits: [...marksOf(doc, "use-count-word", "limit"), ...marksOf(doc, "use-count-word", "limit-negated")],
31
+ negations: marksOf(doc, "use-count-word", "negation").map((mark) => mark.pattern),
32
+ });
@@ -0,0 +1,51 @@
1
+ import type { Detector, Finding, ProseDocument, Span } from "../plugin.ts";
2
+ import { intervalCountMismatches, type IntervalMismatch, type IntervalWords } from "../derived/use-interval.ts";
3
+ import { paragraphsOf } from "../derived/period-counts.ts";
4
+ import { timeLengths, type TimeLength } from "../derived/time-lengths.ts";
5
+ import { limitWordsOf, marksOf, periodCountWordsOf } from "./use-count-words.ts";
6
+ import { quoteAt } from "./structure-tree.ts";
7
+
8
+ // 使う間隔の下限と期間あたりの回数の上限が両立しない、または表の回数と合わない(interval-count-mismatch)。
9
+ // 語は use-count-word と use-interval-word、長さの単位は unit-time から取る。
10
+
11
+ const SECONDS_PER_HOUR = 3600;
12
+ const SECONDS_PER_MINUTE = 60;
13
+
14
+ const patternsOf = (doc: ProseDocument, lexicon: string, weight?: number): string[] =>
15
+ (doc.lexicons[lexicon] ?? []).filter((entry) => weight === undefined || entry.weight === weight).map((entry) => entry.pattern);
16
+
17
+ const lengthsOf = (doc: ProseDocument): TimeLength[] =>
18
+ timeLengths(doc.source, {
19
+ hourUnits: patternsOf(doc, "unit-time", SECONDS_PER_HOUR),
20
+ minuteUnits: patternsOf(doc, "unit-time", SECONDS_PER_MINUTE),
21
+ halves: patternsOf(doc, "length-half"),
22
+ numberWords: patternsOf(doc, "count-number"),
23
+ });
24
+
25
+ const wordsOf = (doc: ProseDocument): IntervalWords => ({
26
+ ...periodCountWordsOf(doc),
27
+ ...limitWordsOf(doc),
28
+ minimum: marksOf(doc, "use-interval-word", "minimum"),
29
+ cues: marksOf(doc, "use-interval-word", "cue").map((mark) => mark.pattern),
30
+ columns: (doc.lexicons["use-interval-word"] ?? [])
31
+ .filter((entry) => entry.group === "column")
32
+ .map((entry) => ({ pattern: entry.pattern, hours: entry.weight ?? 0 })),
33
+ });
34
+
35
+ const written = (doc: ProseDocument, span: Span): string => doc.source.slice(span.start, span.end);
36
+
37
+ const valuesOf = (doc: ProseDocument, mismatch: IntervalMismatch): Record<string, string | number> =>
38
+ mismatch.kind === "interval"
39
+ ? { count: written(doc, mismatch.count), interval: written(doc, mismatch.interval), needed: mismatch.neededHours, hours: mismatch.count.hours }
40
+ : { count: written(doc, mismatch.count), column: mismatch.column, table: mismatch.tableCount };
41
+
42
+ export const intervalCountMismatch: Detector = (doc): Finding[] =>
43
+ intervalCountMismatches(doc.source, paragraphsOf(doc.source), lengthsOf(doc), wordsOf(doc)).map((mismatch) => ({
44
+ rule: "interval-count-mismatch",
45
+ severity: "warning",
46
+ line: 0,
47
+ column: 0,
48
+ quote: quoteAt(doc.source, mismatch.count.start),
49
+ values: { ...valuesOf(doc, mismatch), offset: mismatch.count.start },
50
+ ...(mismatch.kind === "table" ? { variant: "table" } : {}),
51
+ }));
@@ -0,0 +1,45 @@
1
+ // A walking time that does not match the stated distance at the document's walking speed (walk-time-distance-mismatch).
2
+ // The words come from the language package (walk-word, walk-rate, unit-length, unit-time, unit-equivalent-hedge); the speed
3
+ // used when the document states none comes from its profile (profiles/listing.yaml).
4
+ import { walkTimeMismatches, type WalkMark, type WalkRate, type WalkWords } from "../structure/walk-time.ts";
5
+ import { proseAndTablesOf } from "../table-text.ts";
6
+ import { quoteAt } from "./structure-tree.ts";
7
+ import type { Detector, Finding, ProseDocument } from "../plugin.ts";
8
+
9
+ const marksOf = (doc: ProseDocument, lexicon: string, group?: string): WalkMark[] =>
10
+ (doc.lexicons[lexicon] ?? [])
11
+ .filter((entry) => group === undefined || entry.group === group)
12
+ .map((entry) => ({ pattern: entry.pattern, position: entry.position, weight: entry.weight }));
13
+
14
+ const patternsOf = (doc: ProseDocument, lexicon: string, group?: string): string[] => marksOf(doc, lexicon, group).map((mark) => mark.pattern);
15
+
16
+ const walkWords = (doc: ProseDocument): WalkWords => ({
17
+ walks: marksOf(doc, "walk-word", "walk"),
18
+ gaps: patternsOf(doc, "walk-word", "gap"),
19
+ bounds: marksOf(doc, "walk-word", "bound"),
20
+ ranges: patternsOf(doc, "walk-word", "range"),
21
+ fillers: patternsOf(doc, "walk-word", "filler"),
22
+ lengths: marksOf(doc, "unit-length"),
23
+ times: marksOf(doc, "unit-time"),
24
+ hedges: patternsOf(doc, "unit-equivalent-hedge"),
25
+ perTimes: marksOf(doc, "walk-rate", "per"),
26
+ roundUps: patternsOf(doc, "walk-rate", "round-up"),
27
+ notRoundUps: patternsOf(doc, "walk-rate", "not-round-up"),
28
+ });
29
+
30
+ /** The speed the document's profile walks at when the document states none (a real-estate listing: 80 m a minute, rounded up). */
31
+ const profileRate = (doc: ProseDocument): WalkRate | undefined => {
32
+ const rate = doc.profile?.walkRate;
33
+ return rate === undefined ? undefined : { metresPerMinute: rate.metresPerMinute, rounding: rate.roundUp ? "up" : "any" };
34
+ };
35
+
36
+ /** The prose with its tables put back, so code is not read; offsets are the source's. */
37
+ export const walkTimeDistanceMismatch: Detector = (doc): Finding[] =>
38
+ walkTimeMismatches(proseAndTablesOf(doc), walkWords(doc), profileRate(doc)).map((issue) => ({
39
+ rule: "walk-time-distance-mismatch",
40
+ severity: "warning",
41
+ line: 0,
42
+ column: 0,
43
+ quote: quoteAt(doc.source, issue.offset),
44
+ values: { ...issue.values, offset: issue.offset },
45
+ }));
@@ -0,0 +1,19 @@
1
+ import type { Measured, Tolerance } from "./measures.ts";
2
+
3
+ const HALF = 0.5;
4
+ const DECIMAL_BASE = 10;
5
+
6
+ /** 目安の量(約1.2kg)が言える幅を、基準の単位で。書いた最後の桁の半分(約1.2kg は 1.15〜1.25 kg)。 */
7
+ const writtenHalfStep = (value: Measured): number => {
8
+ const step = HALF * DECIMAL_BASE ** -value.decimals;
9
+ return step * Math.max(...value.factors);
10
+ };
11
+
12
+ /**
13
+ * 目安と書いた量を含む二つを比べるときの、合うとみなす差。目安の量は書いた桁までしか言っていないので、その桁の半分までの差は合う。
14
+ * 量の種類ごとの差(tolerance)のほうが広ければ、そちら。
15
+ */
16
+ export const approximateTolerance = (tolerance: Tolerance, approximate: readonly Measured[]): Tolerance => ({
17
+ relative: tolerance.relative,
18
+ absolute: Math.max(tolerance.absolute, ...approximate.map(writtenHalfStep)),
19
+ });
@@ -0,0 +1,94 @@
1
+ import { linesOf, type Line } from "../structure/lines.ts";
2
+ import { sameMeasure, sameUnit, type Measured } from "./measures.ts";
3
+ import { cellsOf, tablesOf, type Cell } from "./table-facts.ts";
4
+ import { withoutEdgeMarks } from "./trim-marks.ts";
5
+ import { toleranceOf } from "./unit-tolerance.ts";
6
+
7
+ /**
8
+ * 表に量を書いた品物(材料の表の「しょうゆ|大さじ1」)を、本文で違う単位の量と一緒に書いた所(しょうゆ30ml、30 ml of soy sauce)。
9
+ * 表の量は、行の最初の量(人数ごとの列があれば、手順が従う最初の列)。本文の量が表の量より多いときだけ言う。本文の名前は、量のすぐ前(空白一つまで)か、つなぐ語(の、of)と
10
+ * 冠詞(the)を挟んで量の後ろ。名前は語の切れ目で始まり終わる(「濃口しょうゆ」の しょうゆ は切れ目で始まるが、「強力粉」の 力粉 は始まらない)。
11
+ */
12
+ export type ItemConflict = { readonly name: string; readonly measured: Measured; readonly other: Measured };
13
+
14
+ export type ItemWords = { readonly links: readonly string[]; readonly determiners: readonly string[] };
15
+
16
+ type ItemAmount = { readonly name: string; readonly key: string; readonly measured: Measured };
17
+
18
+ const LETTER = /\p{L}/u;
19
+ const SCRIPTS = [/\p{Script=Hiragana}/u, /[\p{Script=Katakana}ー]/u, /\p{Script=Han}/u, /\p{Script=Latin}/u];
20
+ const NAME_WINDOW = 80;
21
+
22
+ const keyOf = (text: string): string => text.normalize("NFKC").toLowerCase();
23
+
24
+ const scriptOf = (char: string): number => SCRIPTS.findIndex((script) => script.test(char));
25
+
26
+ /** 二つの字の間が語の切れ目か。字でない側があるか、字の種類(仮名、漢字、英字)が変わる所。英字どうしは続いている。 */
27
+ const isBoundary = (left: string, right: string): boolean =>
28
+ left === "" || right === "" || !LETTER.test(left) || !LETTER.test(right) || scriptOf(left) !== scriptOf(right);
29
+
30
+ const cellValue = (cell: Cell, source: string, measured: readonly Measured[]): Measured | undefined =>
31
+ measured.find((value) => value.start >= cell.start && value.end <= cell.end && source.slice(value.start, value.end) === withoutEdgeMarks(cell.text));
32
+
33
+ /** 表の行の見出しと、その行の最初の量。同じ名前の行が二つあれば最初の行。 */
34
+ const tableAmounts = (source: string, rows: readonly Line[], measured: readonly Measured[]): ItemAmount[] => {
35
+ const amounts = rows.flatMap((row): ItemAmount[] => {
36
+ const [first, ...rest] = cellsOf(row);
37
+ const name = first === undefined ? "" : withoutEdgeMarks(first.text);
38
+ const value = rest.map((cell) => cellValue(cell, source, measured)).find((found) => found !== undefined);
39
+ if (first === undefined || value === undefined || !LETTER.test(name) || cellValue(first, source, measured) !== undefined) return [];
40
+ return [{ name, key: keyOf(name), measured: value }];
41
+ });
42
+ return amounts.filter((amount, index) => amounts.findIndex((other) => other.key === amount.key) === index);
43
+ };
44
+
45
+ /** 量のすぐ前に名前があるか(しょうゆ大さじ1、soy sauce 30 ml)。 */
46
+ const namedBefore = (source: string, value: Measured, key: string): boolean => {
47
+ const end = source.charAt(value.start - 1) === " " ? value.start - 1 : value.start;
48
+ const head = keyOf(source.slice(Math.max(0, end - NAME_WINDOW), end));
49
+ return head.endsWith(key) && isBoundary(head.charAt(head.length - key.length - 1), key.charAt(0));
50
+ };
51
+
52
+ const skipSpace = (text: string): string => (text.startsWith(" ") ? text.slice(1) : text);
53
+
54
+ /** 頭の語を一つ落とす。英字の語は、後ろに空白が要る(of は offer の頭ではない)。 */
55
+ const dropWord = (text: string, words: readonly string[]): string | undefined => {
56
+ const word = words.map(keyOf).find((candidate) => text.startsWith(candidate) && isBoundary(candidate.slice(-1), text.charAt(candidate.length)));
57
+ return word === undefined ? undefined : skipSpace(text.slice(word.length));
58
+ };
59
+
60
+ /** つなぐ語を挟んで、量の後ろに名前があるか(30mlのしょうゆ、30 ml of the soy sauce)。 */
61
+ const namedAfter = (source: string, value: Measured, key: string, words: ItemWords): boolean => {
62
+ const linked = dropWord(skipSpace(keyOf(source.slice(value.end, value.end + NAME_WINDOW))), words.links);
63
+ if (linked === undefined) return false;
64
+ const rest = dropWord(linked, words.determiners) ?? linked;
65
+ return rest.startsWith(key) && isBoundary(key.slice(-1), rest.charAt(key.length));
66
+ };
67
+
68
+ /** 本文の量が、表の量をどう換算しても超えるか。表より少ない量は、その一部を使う所(牛乳の半分を先に)かもしれない。 */
69
+ const exceeds = (value: Measured, listed: Measured): boolean =>
70
+ value.factors.every((a) => listed.factors.every((b) => (value.amount - value.zero) * a > (listed.amount - listed.zero) * b));
71
+
72
+ const inTable = (value: Measured, rows: readonly Line[]): boolean => rows.some((row) => value.start >= row.start && value.end <= row.start + row.text.length);
73
+
74
+ /** 本文の量のうち、表の同じ品物の量と単位が違い、換算すると合わないもの。 */
75
+ export const itemConflicts = (source: string, measured: readonly Measured[], words: ItemWords): ItemConflict[] => {
76
+ const rows = tablesOf(linesOf(source)).flatMap((table) => [table.header, ...table.rows]);
77
+ const amounts = tableAmounts(source, rows, measured);
78
+ if (amounts.length === 0) return [];
79
+ return measured
80
+ .filter((value) => !inTable(value, rows))
81
+ .flatMap((value): ItemConflict[] => {
82
+ const item = amounts.find(
83
+ ({ key, measured: listed }) => listed.dimension === value.dimension && (namedBefore(source, value, key) || namedAfter(source, value, key, words)),
84
+ );
85
+ if (
86
+ item === undefined ||
87
+ sameUnit(item.measured, value) ||
88
+ sameMeasure(item.measured, value, toleranceOf(value.dimension)) ||
89
+ !exceeds(value, item.measured)
90
+ )
91
+ return [];
92
+ return [{ name: item.name, measured: value, other: item.measured }];
93
+ });
94
+ };