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
@@ -0,0 +1,210 @@
1
+ // layout-code-mismatch: a layout code (2LDK, "2 bedrooms") against the rooms a listing names. Pure: the words come in as
2
+ // LayoutWords (the layout-code and layout-room lexicons), the text as strings.
3
+
4
+ export type LayoutPart = "living" | "dining" | "kitchen";
5
+ export type LetterGroup = LayoutPart | "storage" | "one-room";
6
+ export type RoomGroup = LayoutPart | "room" | "storage" | "unclear";
7
+
8
+ export type LayoutWords = {
9
+ /** One-letter marks written right after the number (L, D, K, S, R). */
10
+ readonly letters: ReadonlyMap<string, LetterGroup>;
11
+ /** Marks that add a room not counted (2LDK+S). */
12
+ readonly joiners: readonly string[];
13
+ /** Words after the number that say what it counts ("bedrooms", "BR"). */
14
+ readonly counted: readonly string[];
15
+ readonly rooms: readonly { readonly pattern: string; readonly group: RoomGroup }[];
16
+ /** Words after a number that make it a count of rooms (洋室2室): an entry with one is not read as one room. */
17
+ readonly counters: readonly string[];
18
+ };
19
+
20
+ export type LayoutCode = {
21
+ readonly text: string;
22
+ /** Where the code starts and ends in the text it was read from. */
23
+ readonly start: number;
24
+ readonly end: number;
25
+ readonly rooms: number;
26
+ /** The L, D and K a lettered code has. Undefined for a code that says nothing about them ("2 bedrooms", 1R). */
27
+ readonly parts: ReadonlySet<LayoutPart> | undefined;
28
+ };
29
+
30
+ export type Breakdown = {
31
+ readonly rooms: number;
32
+ /** Each L, D or K part the breakdown names, with the entry that names it. */
33
+ readonly parts: ReadonlyMap<LayoutPart, string>;
34
+ /** An entry that is a room in some listings and not in others, or names two kinds at once. */
35
+ readonly unclear: boolean;
36
+ };
37
+
38
+ export type LayoutSlip =
39
+ { readonly kind: "count"; readonly code: LayoutCode; readonly listed: number } | { readonly kind: "part"; readonly code: LayoutCode; readonly entry: string };
40
+
41
+ const FULLWIDTH_OFFSET = 0xfee0;
42
+ const FULLWIDTH_FIRST = 0xff01;
43
+ const FULLWIDTH_LAST = 0xff5e;
44
+
45
+ /** Full-width ASCII (2LDK) as ASCII, one character for one, so offsets stay those of the source. */
46
+ export const foldWidth = (text: string): string =>
47
+ text.replace(/[!-~]/gu, (char) => {
48
+ const code = char.charCodeAt(0);
49
+ return code >= FULLWIDTH_FIRST && code <= FULLWIDTH_LAST ? String.fromCharCode(code - FULLWIDTH_OFFSET) : char;
50
+ });
51
+
52
+ const DIGITS = /\d+/uy;
53
+ const LATIN = /[a-z]/iu;
54
+ const WORD_CHAR = /[\p{L}\p{N}]/u;
55
+
56
+ /** Whether text has pattern at index, and does not run on into a longer word when the pattern is Latin (2BR is a word after a number). */
57
+ const wordAt = (text: string, index: number, pattern: string): boolean => {
58
+ if (text.slice(index, index + pattern.length).toLowerCase() !== pattern.toLowerCase()) return false;
59
+ if (!LATIN.test(pattern)) return true;
60
+ return !WORD_CHAR.test(text.charAt(index + pattern.length)) && !/\p{L}/u.test(text.charAt(index - 1));
61
+ };
62
+
63
+ /** The longest code letters run (3SLDK). A longer run is a word, not a code. */
64
+ const MAX_CODE_LETTERS = 5;
65
+ /** The most "+S" a code adds (2LDK+S+納戸). */
66
+ const MAX_JOINS = 3;
67
+
68
+ /** The run of code letters at index (LDK, SLDK), with what they say; empty when the run is longer than a code. */
69
+ const lettersAt = (text: string, index: number, words: LayoutWords): LetterGroup[] => {
70
+ const head = Array.from(text.slice(index, index + MAX_CODE_LETTERS + 1));
71
+ const stop = head.findIndex((char) => !words.letters.has(char));
72
+ const length = stop === -1 ? head.length : stop;
73
+ if (length > MAX_CODE_LETTERS) return [];
74
+ return head.slice(0, length).flatMap((char) => words.letters.get(char) ?? []);
75
+ };
76
+
77
+ /** After a lettered code, each "+S" or "+納戸" that adds a room not counted: the index where they end. */
78
+ const joinedEnd = (text: string, index: number, words: LayoutWords, joins = 0): number => {
79
+ const joiner = words.joiners.find((mark) => text.startsWith(mark, index));
80
+ if (joiner === undefined || joins === MAX_JOINS) return index;
81
+ const after = index + joiner.length;
82
+ const letter = words.letters.get(text.charAt(after)) === "storage" ? 1 : 0;
83
+ const word = words.rooms.find((room) => room.group === "storage" && wordAt(text, after, room.pattern))?.pattern.length ?? letter;
84
+ return word === 0 ? index : joinedEnd(text, after + word, words, joins + 1);
85
+ };
86
+
87
+ const letteredCode = (text: string, start: number, digitsEnd: number, words: LayoutWords): LayoutCode | undefined => {
88
+ const groups = lettersAt(text, digitsEnd, words);
89
+ const lettersEnd = digitsEnd + groups.length;
90
+ if (!groups.includes("kitchen") && !groups.includes("one-room")) return undefined;
91
+ if (LATIN.test(text.charAt(lettersEnd))) return undefined;
92
+ const end = joinedEnd(text, lettersEnd, words);
93
+ const parts = groups.includes("one-room") ? undefined : new Set(groups.filter((group): group is LayoutPart => group !== "storage"));
94
+ return { text: text.slice(start, end), start, end, rooms: Number(text.slice(start, digitsEnd)), parts };
95
+ };
96
+
97
+ const SPACER = /[ \-‐–]?/uy;
98
+
99
+ const countedCode = (text: string, start: number, digitsEnd: number, words: LayoutWords): LayoutCode | undefined => {
100
+ SPACER.lastIndex = digitsEnd;
101
+ const after = digitsEnd + (SPACER.exec(text)?.[0].length ?? 0);
102
+ const word = words.counted.find((pattern) => wordAt(text, after, pattern));
103
+ if (word === undefined) return undefined;
104
+ const end = after + word.length;
105
+ return { text: text.slice(start, end), start, end, rooms: Number(text.slice(start, digitsEnd)), parts: undefined };
106
+ };
107
+
108
+ const codeAt = (text: string, start: number, words: LayoutWords): LayoutCode | undefined => {
109
+ if (/[\p{L}\p{N}.,]/u.test(text.charAt(start - 1))) return undefined;
110
+ DIGITS.lastIndex = start;
111
+ const digits = DIGITS.exec(text)?.[0];
112
+ if (digits === undefined) return undefined;
113
+ const digitsEnd = start + digits.length;
114
+ return letteredCode(text, start, digitsEnd, words) ?? countedCode(text, start, digitsEnd, words);
115
+ };
116
+
117
+ /**
118
+ * The one layout code a field value gives (2LDK, 3SLDK, 2LDK+S, "2 bedrooms", 2-bed, 2BR). A value with two codes of
119
+ * different counts ("2LDK or 3DK") gives none: which one the breakdown is for cannot be told.
120
+ */
121
+ export const parseLayoutCode = (value: string, words: LayoutWords): LayoutCode | undefined => {
122
+ const text = foldWidth(value);
123
+ const codes = [...text.matchAll(/\d+/gu)].flatMap((match) => codeAt(text, match.index, words) ?? []);
124
+ const first = codes[0];
125
+ if (first === undefined || codes.some((code) => code.rooms !== first.rooms)) return undefined;
126
+ return { ...first, text: value.slice(first.start, first.end) };
127
+ };
128
+
129
+ const ENTRY_SEPARATOR = /[、,,・//++;;\n]/u;
130
+ const MULTIPLIER = /(?:[×✕]|(?<=[\s\d])[xX])\s*(\d+)\s*$/u;
131
+
132
+ /** The kinds of room an entry names, by the room words in it; a lone run of code letters (LDK 12帖, S 4帖) by its letters. */
133
+ const groupsOfEntry = (entry: string, words: LayoutWords): RoomGroup[] => {
134
+ const named = words.rooms.filter((room) => positions(entry).some((index) => wordAt(entry, index, room.pattern))).map((room) => room.group);
135
+ if (named.length > 0) return [...new Set(named)];
136
+ const letters = /^[A-Z]+(?![A-Za-z])/u.exec(entry)?.[0] ?? "";
137
+ const groups = [...letters].map((letter) => words.letters.get(letter));
138
+ if (letters === "" || groups.some((group) => group === undefined || group === "one-room")) return [];
139
+ return [...new Set(groups.flatMap((group): RoomGroup[] => (group === undefined || group === "one-room" ? [] : [group])))];
140
+ };
141
+
142
+ const positions = (entry: string): number[] => Array.from({ length: entry.length }, (_, index) => index);
143
+
144
+ /** How many rooms an entry names in words (Bedroom 1 and Bedroom 2 names two). */
145
+ const roomMentions = (entry: string, words: LayoutWords): number => {
146
+ const rooms = words.rooms.filter((room) => room.group === "room");
147
+ return positions(entry).filter((index) => rooms.some((room) => wordAt(entry, index, room.pattern))).length;
148
+ };
149
+
150
+ /** Whether the entry counts rooms with a number (洋室2室, 和室 2間). */
151
+ const countsRooms = (entry: string, words: LayoutWords): boolean =>
152
+ [...entry.matchAll(/\d+\s*/gu)].some((match) => words.counters.some((counter) => entry.startsWith(counter, match.index + match[0].length)));
153
+
154
+ const isPart = (group: RoomGroup): group is LayoutPart => group === "living" || group === "dining" || group === "kitchen";
155
+
156
+ type Tally = { rooms: number; parts: Map<LayoutPart, string>; unclear: boolean };
157
+
158
+ const tallyEntry = (tally: Tally, entry: string, words: LayoutWords): Tally => {
159
+ const groups = groupsOfEntry(entry, words);
160
+ const rooms = groups.includes("room");
161
+ const several = rooms && (groups.length > 1 || roomMentions(entry, words) > 1 || countsRooms(entry, words));
162
+ if (groups.includes("unclear") || several) return { ...tally, unclear: true };
163
+ const times = Number(MULTIPLIER.exec(entry)?.[1] ?? 1);
164
+ const parts = new Map(tally.parts);
165
+ groups.filter(isPart).forEach((part) => parts.set(part, parts.get(part) ?? entry));
166
+ return { rooms: tally.rooms + (rooms ? times : 0), parts, unclear: tally.unclear };
167
+ };
168
+
169
+ /** The rooms a breakdown names ("LDK 11.5帖、洋室 6帖、洋室 5帖", "Bedroom 1, Bedroom 2, Bathroom"). Entries it cannot name are left out. */
170
+ export const parseBreakdown = (entries: readonly string[], words: LayoutWords): Breakdown =>
171
+ entries
172
+ .flatMap((entry) => foldWidth(entry).split(ENTRY_SEPARATOR))
173
+ .map((entry) => entry.trim())
174
+ .filter((entry) => entry !== "")
175
+ .reduce<Tally>((tally, entry) => tallyEntry(tally, entry, words), { rooms: 0, parts: new Map(), unclear: false });
176
+
177
+ /**
178
+ * Where a code and the breakdown of the same listing disagree. Silent when the breakdown names no room (it may list only
179
+ * the LDK), or names one that is counted in some listings and not in others. A lettered code is also checked for an L, D or
180
+ * K part the breakdown names and the code lacks; a one-room code (1R) and a counted code ("2 bedrooms") are not.
181
+ */
182
+ export const layoutSlip = (code: LayoutCode, breakdown: Breakdown): LayoutSlip | undefined => {
183
+ if (breakdown.unclear || breakdown.rooms === 0) return undefined;
184
+ if (breakdown.rooms !== code.rooms) return { kind: "count", code, listed: breakdown.rooms };
185
+ const parts = code.parts;
186
+ if (parts === undefined) return undefined;
187
+ const extra = [...breakdown.parts].find(([part]) => !parts.has(part));
188
+ return extra === undefined ? undefined : { kind: "part", code, entry: extra[1] };
189
+ };
190
+
191
+ /**
192
+ * The codes and breakdowns of one page, paired listing by listing: within one scope (a section), the first code with the
193
+ * first breakdown and so on, but only when the scope has as many of each. Any other count is several listings that cannot
194
+ * be told apart, and none is paired.
195
+ */
196
+ export const pairByScope = <C extends { readonly scope: number }, B extends { readonly scope: number }>(
197
+ codes: readonly C[],
198
+ breakdowns: readonly B[],
199
+ ): [C, B][] => {
200
+ const scopes = [...new Set(codes.map((code) => code.scope))];
201
+ return scopes.flatMap((scope) => {
202
+ const inCodes = codes.filter((code) => code.scope === scope);
203
+ const inBreakdowns = breakdowns.filter((breakdown) => breakdown.scope === scope);
204
+ if (inCodes.length !== inBreakdowns.length) return [];
205
+ return inCodes.flatMap((code, index): [C, B][] => {
206
+ const breakdown = inBreakdowns[index];
207
+ return breakdown === undefined ? [] : [[code, breakdown]];
208
+ });
209
+ });
210
+ };
@@ -0,0 +1,138 @@
1
+ import type { Amount } from "./total.ts";
2
+ import type { MeasureUnit } from "../facts/measures.ts";
3
+ import { CONTINUES_WORD, NUMBER, overlapsAny, valueOf } from "./counted-amounts.ts";
4
+
5
+ /**
6
+ * 足してよい、単位の付いた量(300 mg、0.5 g、2 L、3 cm)。単位は量の種類(dimension)と、基準の単位への倍率(factors)を持つ。
7
+ * 同じ種類の量は、その種類で一番小さい単位に直して足す(500 mg と 0.5 g は 1,000 mg)。倍率が二つある単位(cup)は換算が決まらない。
8
+ */
9
+ export type SummedMeasure = Pick<MeasureUnit, "pattern" | "dimension" | "factors" | "before" | "context">;
10
+
11
+ /** 足せない量の印。目安の印(約、about、程度)、範囲の印(〜)、何あたりかの印(数の前の $3 / 200 g、単位の後ろの 300 mg/錠、per tablet)。 */
12
+ export type MeasureMarks = {
13
+ readonly roughBefore: readonly string[];
14
+ readonly roughAfter: readonly string[];
15
+ readonly connectors: readonly string[];
16
+ readonly perMarks: readonly string[];
17
+ };
18
+
19
+ /**
20
+ * 足せない量の単位に付ける印。量は読むが、単位に位置も入れて、ほかのどの量とも同じ単位にならないようにする。その量がある列の和は取らない。
21
+ * 読まずに捨てると、その行が空の升に見えて、残りの行の和を合計と比べてしまう。
22
+ */
23
+ const UNSUMMABLE = "?";
24
+
25
+ const LOOKAHEAD = 32;
26
+ const LATIN = /\p{Script=Latin}/u;
27
+ const DIGIT_AT_END = /\p{N}$/u;
28
+
29
+ const lineStartOf = (source: string, offset: number): number => source.lastIndexOf("\n", offset - 1) + 1;
30
+
31
+ /** text が word で終わり、英字の語なら語の途中(awesome の some)でない。 */
32
+ const endsWithWord = (text: string, word: string): boolean => {
33
+ if (!text.toLowerCase().endsWith(word.toLowerCase())) return false;
34
+ return !LATIN.test(word.charAt(0)) || !LATIN.test(text.charAt(text.length - word.length - 1));
35
+ };
36
+
37
+ /** text が word で始まり、英字の語なら語の途中(personal の per)でない。 */
38
+ const startsWithWord = (text: string, word: string): boolean => {
39
+ if (!text.toLowerCase().startsWith(word.toLowerCase())) return false;
40
+ return !LATIN.test(word.at(-1) ?? "") || !LATIN.test(text.charAt(word.length));
41
+ };
42
+
43
+ /**
44
+ * 数の前の目安の印(約300mg、about 300 mg)、何あたりかの印($3 / 200 g、$3 per 200 g)、
45
+ * 範囲の後ろの端(300〜400mg、300 - 400 mg)。
46
+ */
47
+ const markedBefore = (source: string, start: number, marks: MeasureMarks): boolean => {
48
+ const before = source.slice(lineStartOf(source, start), start);
49
+ const trimmed = before.trimEnd();
50
+ if ([...marks.roughBefore, ...marks.perMarks].some((word) => endsWithWord(trimmed, word))) return true;
51
+ return marks.connectors.some(
52
+ (connector) => before.endsWith(connector) || (trimmed.endsWith(connector) && DIGIT_AT_END.test(trimmed.slice(0, -connector.length).trimEnd())),
53
+ );
54
+ };
55
+
56
+ /** 印の前に置かれうる、開き括弧と空白(5 mg (per tablet))。 */
57
+ const OPENING = /^[\s(([[]+/u;
58
+
59
+ /** 単位の後ろの、目安の印(300mg程度)か何あたりかの印(300 mg/tablet、300 mg per tablet、5 mg (per tablet))。 */
60
+ const markedAfter = (source: string, end: number, marks: MeasureMarks): boolean => {
61
+ const after = source.slice(end, end + LOOKAHEAD).replace(OPENING, "");
62
+ return [...marks.roughAfter, ...marks.perMarks].some((word) => startsWithWord(after, word));
63
+ };
64
+
65
+ type Found = { readonly measure: SummedMeasure; readonly end: number };
66
+
67
+ const lineAt = (source: string, offset: number): string => {
68
+ const end = source.indexOf("\n", offset);
69
+ return source.slice(lineStartOf(source, offset), end === -1 ? source.length : end);
70
+ };
71
+
72
+ /** 文脈の語が要る単位(unit-context)は、同じ行にその語があるときだけ単位。 */
73
+ const inContext = (measure: SummedMeasure, source: string, offset: number): boolean => {
74
+ if (measure.context.length === 0) return true;
75
+ const line = lineAt(source, offset).toLowerCase();
76
+ return measure.context.some((word) => line.includes(word.toLowerCase()));
77
+ };
78
+
79
+ /** 数のすぐ後ろ(空白一つまで)の単位。長い単位から試す(mg を m で切らない)。大文字と小文字は区別する(mL と ML は別)。 */
80
+ const measureAfter = (source: string, end: number, measures: readonly SummedMeasure[], marks: MeasureMarks): Found | undefined => {
81
+ const gap = source.charAt(end) === " " ? 1 : 0;
82
+ const rest = source.slice(end + gap, end + gap + LOOKAHEAD);
83
+ const measure = measures.find((candidate) => {
84
+ if (candidate.before || !rest.startsWith(candidate.pattern) || !inContext(candidate, source, end)) return false;
85
+ const unitEnd = end + gap + candidate.pattern.length;
86
+ return markedAfter(source, unitEnd, marks) || !CONTINUES_WORD.test(rest.slice(candidate.pattern.length));
87
+ });
88
+ return measure === undefined ? undefined : { measure, end: end + gap + measure.pattern.length };
89
+ };
90
+
91
+ /** 量の種類ごとの、一番小さい倍率。倍率が一つの単位だけから取る。 */
92
+ const baseFactors = (measures: readonly SummedMeasure[]): Map<string, number> =>
93
+ measures.reduce((bases, measure) => {
94
+ const [factor] = measure.factors;
95
+ if (measure.factors.length !== 1 || factor === undefined || factor <= 0) return bases;
96
+ return bases.set(measure.dimension, Math.min(bases.get(measure.dimension) ?? factor, factor));
97
+ }, new Map<string, number>());
98
+
99
+ const unsummable = (offset: number, end: number, value: number, measure: SummedMeasure): Amount => ({
100
+ offset,
101
+ end,
102
+ value,
103
+ unit: `${measure.dimension}${UNSUMMABLE}${String(offset)}`,
104
+ });
105
+
106
+ const amountOf = (source: string, start: number, written: string, found: Found, bases: Map<string, number>, marks: MeasureMarks): Amount => {
107
+ const value = valueOf(written);
108
+ const { measure } = found;
109
+ const [factor] = measure.factors;
110
+ const base = bases.get(measure.dimension);
111
+ const sure = measure.factors.length === 1 && factor !== undefined && base !== undefined;
112
+ if (!sure || markedBefore(source, start, marks) || markedAfter(source, found.end, marks)) return unsummable(start, found.end, value, measure);
113
+ const scale = factor / base;
114
+ return { offset: start, end: found.end, value: value * scale, unit: measure.dimension, scale };
115
+ };
116
+
117
+ /**
118
+ * 数のすぐ前(空白一つまで)の単位(大さじ1)は、足さない量として読む。読まずに捨てると、その行が空の升に見えてしまう。
119
+ */
120
+ const measureBefore = (source: string, start: number, end: number, measures: readonly SummedMeasure[]): Amount | undefined => {
121
+ const gap = source.charAt(start - 1) === " " ? 1 : 0;
122
+ const head = source.slice(Math.max(0, start - gap - LOOKAHEAD), start - gap);
123
+ const measure = measures.find((candidate) => candidate.before && head.endsWith(candidate.pattern) && inContext(candidate, source, start));
124
+ return measure === undefined ? undefined : unsummable(start - gap - measure.pattern.length, end, valueOf(source.slice(start, end)), measure);
125
+ };
126
+
127
+ /** 文書の中の、単位の付いた量。値は量の種類で一番小さい単位に直す。木がすでに数量として読んだもの(known)とは重ねない。 */
128
+ export const measuredAmounts = (source: string, measures: readonly SummedMeasure[], marks: MeasureMarks, known: readonly Amount[]): Amount[] => {
129
+ const longestFirst = measures.toSorted((left, right) => right.pattern.length - left.pattern.length);
130
+ const bases = baseFactors(measures);
131
+ return [...source.matchAll(NUMBER)].flatMap((match): Amount[] => {
132
+ const end = match.index + match[0].length;
133
+ const found = measureAfter(source, end, longestFirst, marks);
134
+ if (found !== undefined) return overlapsAny(match.index, found.end, known) ? [] : [amountOf(source, match.index, match[0], found, bases, marks)];
135
+ const before = measureBefore(source, match.index, end, longestFirst);
136
+ return before === undefined || overlapsAny(before.offset, before.end, known) ? [] : [before];
137
+ });
138
+ };
@@ -0,0 +1,54 @@
1
+ // quantity-range-reversed: which measured quantities may be one end of a range, read as the ends structure/amount-range.ts
2
+ // compares. Pure.
3
+ import type { Measured } from "../facts/measures.ts";
4
+ import { escapeRegExp } from "../orthography.ts";
5
+ import type { StructureIssue } from "./issues.ts";
6
+ import type { RangeAmount } from "./amount-range.ts";
7
+
8
+ const LATIN_EDGE = /[A-Za-z]$/u;
9
+ const LATIN_START = /^[A-Za-z]/u;
10
+
11
+ /** A Latin unit running into a word is part of that word: the t of "40 to" is not a tonne. */
12
+ const standsAlone = (text: string, measured: Measured): boolean =>
13
+ !(LATIN_EDGE.test(measured.unit) && LATIN_START.test(text.slice(measured.end, measured.end + 1)));
14
+
15
+ /** 10 and a minus sign right before the number is a power of ten written flat ("10-8 cm" for 10⁻⁸ cm), not a range. */
16
+ const POWER_OF_TEN = /(?<![0-90-9.,,.])[11][00][-−-]$/u;
17
+
18
+ /** "10-" and the character before it, which must not be a digit (110-8 cm is a range). */
19
+ const POWER_OF_TEN_LOOKBACK = 4;
20
+
21
+ const isExponent = (text: string, measured: Measured): boolean =>
22
+ POWER_OF_TEN.test(text.slice(Math.max(0, measured.start - POWER_OF_TEN_LOOKBACK), measured.start));
23
+
24
+ /** A quantity as one end of a range: two ends compare only in one unit, however it is spelled (℃ with °C, m with meters). */
25
+ const rangeEndOf = (text: string, measured: Measured): RangeAmount => ({
26
+ offset: measured.start,
27
+ end: measured.end,
28
+ currency: `${measured.dimension}:${measured.factors.join(",")}`,
29
+ value: measured.amount,
30
+ scale: undefined,
31
+ position: text.startsWith(measured.unit, measured.start) ? "before" : "after",
32
+ });
33
+
34
+ /** The measured quantities that can end a range, as range ends. */
35
+ export const quantityRangeEnds = (text: string, measured: readonly Measured[]): RangeAmount[] =>
36
+ measured.filter((value) => standsAlone(text, value) && !isExponent(text, value)).map((value) => rangeEndOf(text, value));
37
+
38
+ const LATIN_WORD = /^[A-Za-z]/u;
39
+
40
+ /** Whether a lead word stands right before the range on its line ("went from 9 minutes to 2 minutes" tells of a change). */
41
+ const ledBy = (text: string, offset: number, leads: readonly string[]): boolean => {
42
+ const before = text
43
+ .slice(text.lastIndexOf("\n", offset - 1) + 1, offset)
44
+ .trimEnd()
45
+ .toLowerCase();
46
+ return leads.some((lead) => {
47
+ const edge = LATIN_WORD.test(lead) ? "(?<![a-z])" : "";
48
+ return new RegExp(`${edge}${escapeRegExp(lead.toLowerCase())}$`, "u").test(before);
49
+ });
50
+ };
51
+
52
+ /** The reversed ranges that no lead word frames as a change. */
53
+ export const unledRanges = (text: string, issues: readonly StructureIssue[], leads: readonly string[]): StructureIssue[] =>
54
+ issues.filter((issue) => !ledBy(text, issue.offset, leads));