chaffjs 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (681) hide show
  1. package/dist/announced-count.d.ts +2 -0
  2. package/dist/announced-count.d.ts.map +1 -1
  3. package/dist/announced-count.js +4 -3
  4. package/dist/announced-count.js.map +1 -1
  5. package/dist/contacts/addresses.d.ts +21 -0
  6. package/dist/contacts/addresses.d.ts.map +1 -0
  7. package/dist/contacts/addresses.js +144 -0
  8. package/dist/contacts/addresses.js.map +1 -0
  9. package/dist/contacts/emails.d.ts +11 -0
  10. package/dist/contacts/emails.d.ts.map +1 -0
  11. package/dist/contacts/emails.js +51 -0
  12. package/dist/contacts/emails.js.map +1 -0
  13. package/dist/contacts/variants.d.ts +14 -0
  14. package/dist/contacts/variants.d.ts.map +1 -0
  15. package/dist/contacts/variants.js +25 -0
  16. package/dist/contacts/variants.js.map +1 -0
  17. package/dist/derived/battery-runtime.d.ts +85 -0
  18. package/dist/derived/battery-runtime.d.ts.map +1 -0
  19. package/dist/derived/battery-runtime.js +217 -0
  20. package/dist/derived/battery-runtime.js.map +1 -0
  21. package/dist/derived/period-counts.d.ts +48 -0
  22. package/dist/derived/period-counts.d.ts.map +1 -0
  23. package/dist/derived/period-counts.js +128 -0
  24. package/dist/derived/period-counts.js.map +1 -0
  25. package/dist/derived/session-hours.d.ts +26 -0
  26. package/dist/derived/session-hours.d.ts.map +1 -0
  27. package/dist/derived/session-hours.js +29 -0
  28. package/dist/derived/session-hours.js.map +1 -0
  29. package/dist/derived/time-lengths.d.ts +37 -0
  30. package/dist/derived/time-lengths.d.ts.map +1 -0
  31. package/dist/derived/time-lengths.js +171 -0
  32. package/dist/derived/time-lengths.js.map +1 -0
  33. package/dist/derived/unit-counts.d.ts +7 -0
  34. package/dist/derived/unit-counts.d.ts.map +1 -0
  35. package/dist/derived/unit-counts.js +13 -0
  36. package/dist/derived/unit-counts.js.map +1 -0
  37. package/dist/derived/use-interval.d.ts +33 -0
  38. package/dist/derived/use-interval.d.ts.map +1 -0
  39. package/dist/derived/use-interval.js +93 -0
  40. package/dist/derived/use-interval.js.map +1 -0
  41. package/dist/derived/working-hours.d.ts +36 -0
  42. package/dist/derived/working-hours.d.ts.map +1 -0
  43. package/dist/derived/working-hours.js +55 -0
  44. package/dist/derived/working-hours.js.map +1 -0
  45. package/dist/detectors/acronym-expansion.d.ts +1 -1
  46. package/dist/detectors/acronym-expansion.d.ts.map +1 -1
  47. package/dist/detectors/acronym-expansion.js +46 -2
  48. package/dist/detectors/acronym-expansion.js.map +1 -1
  49. package/dist/detectors/announced-count.d.ts.map +1 -1
  50. package/dist/detectors/announced-count.js +1 -0
  51. package/dist/detectors/announced-count.js.map +1 -1
  52. package/dist/detectors/capacity-runtime.d.ts +7 -0
  53. package/dist/detectors/capacity-runtime.d.ts.map +1 -0
  54. package/dist/detectors/capacity-runtime.js +98 -0
  55. package/dist/detectors/capacity-runtime.js.map +1 -0
  56. package/dist/detectors/capitalised-term.d.ts +44 -0
  57. package/dist/detectors/capitalised-term.d.ts.map +1 -0
  58. package/dist/detectors/capitalised-term.js +101 -0
  59. package/dist/detectors/capitalised-term.js.map +1 -0
  60. package/dist/detectors/contact-variant.d.ts +3 -0
  61. package/dist/detectors/contact-variant.d.ts.map +1 -0
  62. package/dist/detectors/contact-variant.js +21 -0
  63. package/dist/detectors/contact-variant.js.map +1 -0
  64. package/dist/detectors/count-over-capacity.d.ts +4 -0
  65. package/dist/detectors/count-over-capacity.d.ts.map +1 -0
  66. package/dist/detectors/count-over-capacity.js +26 -0
  67. package/dist/detectors/count-over-capacity.js.map +1 -0
  68. package/dist/detectors/date-outside-period.d.ts.map +1 -1
  69. package/dist/detectors/date-outside-period.js +5 -1
  70. package/dist/detectors/date-outside-period.js.map +1 -1
  71. package/dist/detectors/date-stamp-order.d.ts +3 -0
  72. package/dist/detectors/date-stamp-order.d.ts.map +1 -0
  73. package/dist/detectors/date-stamp-order.js +24 -0
  74. package/dist/detectors/date-stamp-order.js.map +1 -0
  75. package/dist/detectors/defined-term-form.d.ts.map +1 -1
  76. package/dist/detectors/defined-term-form.js +57 -7
  77. package/dist/detectors/defined-term-form.js.map +1 -1
  78. package/dist/detectors/definition-use.d.ts +1 -1
  79. package/dist/detectors/definition-use.d.ts.map +1 -1
  80. package/dist/detectors/definition-use.js +4 -2
  81. package/dist/detectors/definition-use.js.map +1 -1
  82. package/dist/detectors/derived-numbers.d.ts +11 -1
  83. package/dist/detectors/derived-numbers.d.ts.map +1 -1
  84. package/dist/detectors/derived-numbers.js +74 -20
  85. package/dist/detectors/derived-numbers.js.map +1 -1
  86. package/dist/detectors/due-date.d.ts.map +1 -1
  87. package/dist/detectors/due-date.js +6 -0
  88. package/dist/detectors/due-date.js.map +1 -1
  89. package/dist/detectors/en-shape.d.ts.map +1 -1
  90. package/dist/detectors/en-shape.js +7 -4
  91. package/dist/detectors/en-shape.js.map +1 -1
  92. package/dist/detectors/event-deadline.d.ts +3 -0
  93. package/dist/detectors/event-deadline.d.ts.map +1 -0
  94. package/dist/detectors/event-deadline.js +24 -0
  95. package/dist/detectors/event-deadline.js.map +1 -0
  96. package/dist/detectors/fact-consistency.d.ts +1 -1
  97. package/dist/detectors/fact-consistency.d.ts.map +1 -1
  98. package/dist/detectors/fact-consistency.js +52 -3
  99. package/dist/detectors/fact-consistency.js.map +1 -1
  100. package/dist/detectors/heading-case.d.ts +5 -2
  101. package/dist/detectors/heading-case.d.ts.map +1 -1
  102. package/dist/detectors/heading-case.js +12 -4
  103. package/dist/detectors/heading-case.js.map +1 -1
  104. package/dist/detectors/heading-echo.d.ts +5 -0
  105. package/dist/detectors/heading-echo.d.ts.map +1 -1
  106. package/dist/detectors/heading-echo.js +14 -1
  107. package/dist/detectors/heading-echo.js.map +1 -1
  108. package/dist/detectors/layout-code.d.ts +5 -0
  109. package/dist/detectors/layout-code.d.ts.map +1 -0
  110. package/dist/detectors/layout-code.js +124 -0
  111. package/dist/detectors/layout-code.js.map +1 -0
  112. package/dist/detectors/measured-facts.d.ts +8 -0
  113. package/dist/detectors/measured-facts.d.ts.map +1 -0
  114. package/dist/detectors/measured-facts.js +18 -0
  115. package/dist/detectors/measured-facts.js.map +1 -0
  116. package/dist/detectors/name-variant-model-codes.d.ts +11 -0
  117. package/dist/detectors/name-variant-model-codes.d.ts.map +1 -0
  118. package/dist/detectors/name-variant-model-codes.js +21 -0
  119. package/dist/detectors/name-variant-model-codes.js.map +1 -0
  120. package/dist/detectors/name-variant.d.ts.map +1 -1
  121. package/dist/detectors/name-variant.js +78 -10
  122. package/dist/detectors/name-variant.js.map +1 -1
  123. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  124. package/dist/detectors/orthographic-variant.js +62 -7
  125. package/dist/detectors/orthographic-variant.js.map +1 -1
  126. package/dist/detectors/orthography.d.ts.map +1 -1
  127. package/dist/detectors/orthography.js +12 -6
  128. package/dist/detectors/orthography.js.map +1 -1
  129. package/dist/detectors/party-order.d.ts +3 -0
  130. package/dist/detectors/party-order.d.ts.map +1 -0
  131. package/dist/detectors/party-order.js +46 -0
  132. package/dist/detectors/party-order.js.map +1 -0
  133. package/dist/detectors/quantity-range.d.ts +3 -0
  134. package/dist/detectors/quantity-range.d.ts.map +1 -0
  135. package/dist/detectors/quantity-range.js +35 -0
  136. package/dist/detectors/quantity-range.js.map +1 -0
  137. package/dist/detectors/range-band.d.ts +5 -0
  138. package/dist/detectors/range-band.d.ts.map +1 -0
  139. package/dist/detectors/range-band.js +80 -0
  140. package/dist/detectors/range-band.js.map +1 -0
  141. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts +3 -0
  142. package/dist/detectors/registry/capacity-runtime-mismatch.d.ts.map +1 -0
  143. package/dist/detectors/registry/capacity-runtime-mismatch.js +3 -0
  144. package/dist/detectors/registry/capacity-runtime-mismatch.js.map +1 -0
  145. package/dist/detectors/registry/connection-time-short.d.ts +3 -0
  146. package/dist/detectors/registry/connection-time-short.d.ts.map +1 -0
  147. package/dist/detectors/registry/connection-time-short.js +3 -0
  148. package/dist/detectors/registry/connection-time-short.js.map +1 -0
  149. package/dist/detectors/registry/contact-variant.d.ts +3 -0
  150. package/dist/detectors/registry/contact-variant.d.ts.map +1 -0
  151. package/dist/detectors/registry/contact-variant.js +3 -0
  152. package/dist/detectors/registry/contact-variant.js.map +1 -0
  153. package/dist/detectors/registry/count-over-capacity.d.ts +3 -0
  154. package/dist/detectors/registry/count-over-capacity.d.ts.map +1 -0
  155. package/dist/detectors/registry/count-over-capacity.js +3 -0
  156. package/dist/detectors/registry/count-over-capacity.js.map +1 -0
  157. package/dist/detectors/registry/date-stamp-order.d.ts +3 -0
  158. package/dist/detectors/registry/date-stamp-order.d.ts.map +1 -0
  159. package/dist/detectors/registry/date-stamp-order.js +3 -0
  160. package/dist/detectors/registry/date-stamp-order.js.map +1 -0
  161. package/dist/detectors/registry/duration-product-mismatch.d.ts +3 -0
  162. package/dist/detectors/registry/duration-product-mismatch.d.ts.map +1 -0
  163. package/dist/detectors/registry/duration-product-mismatch.js +3 -0
  164. package/dist/detectors/registry/duration-product-mismatch.js.map +1 -0
  165. package/dist/detectors/registry/event-deadline.d.ts +3 -0
  166. package/dist/detectors/registry/event-deadline.d.ts.map +1 -0
  167. package/dist/detectors/registry/event-deadline.js +3 -0
  168. package/dist/detectors/registry/event-deadline.js.map +1 -0
  169. package/dist/detectors/registry/interval-count-mismatch.d.ts +3 -0
  170. package/dist/detectors/registry/interval-count-mismatch.d.ts.map +1 -0
  171. package/dist/detectors/registry/interval-count-mismatch.js +3 -0
  172. package/dist/detectors/registry/interval-count-mismatch.js.map +1 -0
  173. package/dist/detectors/registry/layout-code-mismatch.d.ts +3 -0
  174. package/dist/detectors/registry/layout-code-mismatch.d.ts.map +1 -0
  175. package/dist/detectors/registry/layout-code-mismatch.js +3 -0
  176. package/dist/detectors/registry/layout-code-mismatch.js.map +1 -0
  177. package/dist/detectors/registry/party-order.d.ts +3 -0
  178. package/dist/detectors/registry/party-order.d.ts.map +1 -0
  179. package/dist/detectors/registry/party-order.js +3 -0
  180. package/dist/detectors/registry/party-order.js.map +1 -0
  181. package/dist/detectors/registry/quantity-range.d.ts +3 -0
  182. package/dist/detectors/registry/quantity-range.d.ts.map +1 -0
  183. package/dist/detectors/registry/quantity-range.js +3 -0
  184. package/dist/detectors/registry/quantity-range.js.map +1 -0
  185. package/dist/detectors/registry/range-band-mismatch.d.ts +3 -0
  186. package/dist/detectors/registry/range-band-mismatch.d.ts.map +1 -0
  187. package/dist/detectors/registry/range-band-mismatch.js +3 -0
  188. package/dist/detectors/registry/range-band-mismatch.js.map +1 -0
  189. package/dist/detectors/registry/rent-multiple-mismatch.d.ts +3 -0
  190. package/dist/detectors/registry/rent-multiple-mismatch.d.ts.map +1 -0
  191. package/dist/detectors/registry/rent-multiple-mismatch.js +3 -0
  192. package/dist/detectors/registry/rent-multiple-mismatch.js.map +1 -0
  193. package/dist/detectors/registry/requirement-lists.d.ts +3 -0
  194. package/dist/detectors/registry/requirement-lists.d.ts.map +1 -0
  195. package/dist/detectors/registry/requirement-lists.js +3 -0
  196. package/dist/detectors/registry/requirement-lists.js.map +1 -0
  197. package/dist/detectors/registry/return-type.d.ts +3 -0
  198. package/dist/detectors/registry/return-type.d.ts.map +1 -0
  199. package/dist/detectors/registry/return-type.js +3 -0
  200. package/dist/detectors/registry/return-type.js.map +1 -0
  201. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts +3 -0
  202. package/dist/detectors/registry/scaled-quantity-mismatch.d.ts.map +1 -0
  203. package/dist/detectors/registry/scaled-quantity-mismatch.js +3 -0
  204. package/dist/detectors/registry/scaled-quantity-mismatch.js.map +1 -0
  205. package/dist/detectors/registry/step-time-sum-mismatch.d.ts +3 -0
  206. package/dist/detectors/registry/step-time-sum-mismatch.d.ts.map +1 -0
  207. package/dist/detectors/registry/step-time-sum-mismatch.js +3 -0
  208. package/dist/detectors/registry/step-time-sum-mismatch.js.map +1 -0
  209. package/dist/detectors/registry/time-range-reversed.d.ts +3 -0
  210. package/dist/detectors/registry/time-range-reversed.d.ts.map +1 -0
  211. package/dist/detectors/registry/time-range-reversed.js +3 -0
  212. package/dist/detectors/registry/time-range-reversed.js.map +1 -0
  213. package/dist/detectors/registry/unlisted-item-used.d.ts +3 -0
  214. package/dist/detectors/registry/unlisted-item-used.d.ts.map +1 -0
  215. package/dist/detectors/registry/unlisted-item-used.js +3 -0
  216. package/dist/detectors/registry/unlisted-item-used.js.map +1 -0
  217. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts +3 -0
  218. package/dist/detectors/registry/walk-time-distance-mismatch.d.ts.map +1 -0
  219. package/dist/detectors/registry/walk-time-distance-mismatch.js +3 -0
  220. package/dist/detectors/registry/walk-time-distance-mismatch.js.map +1 -0
  221. package/dist/detectors/rent-multiple-mismatch.d.ts +3 -0
  222. package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -0
  223. package/dist/detectors/rent-multiple-mismatch.js +39 -0
  224. package/dist/detectors/rent-multiple-mismatch.js.map +1 -0
  225. package/dist/detectors/requirement-lists.d.ts +3 -0
  226. package/dist/detectors/requirement-lists.d.ts.map +1 -0
  227. package/dist/detectors/requirement-lists.js +46 -0
  228. package/dist/detectors/requirement-lists.js.map +1 -0
  229. package/dist/detectors/return-type.d.ts +3 -0
  230. package/dist/detectors/return-type.d.ts.map +1 -0
  231. package/dist/detectors/return-type.js +24 -0
  232. package/dist/detectors/return-type.js.map +1 -0
  233. package/dist/detectors/scaled-quantity.d.ts +3 -0
  234. package/dist/detectors/scaled-quantity.d.ts.map +1 -0
  235. package/dist/detectors/scaled-quantity.js +37 -0
  236. package/dist/detectors/scaled-quantity.js.map +1 -0
  237. package/dist/detectors/signals.d.ts.map +1 -1
  238. package/dist/detectors/signals.js +3 -1
  239. package/dist/detectors/signals.js.map +1 -1
  240. package/dist/detectors/step-time-sum.d.ts +3 -0
  241. package/dist/detectors/step-time-sum.d.ts.map +1 -0
  242. package/dist/detectors/step-time-sum.js +46 -0
  243. package/dist/detectors/step-time-sum.js.map +1 -0
  244. package/dist/detectors/structure-tree.d.ts +2 -0
  245. package/dist/detectors/structure-tree.d.ts.map +1 -1
  246. package/dist/detectors/structure-tree.js +31 -6
  247. package/dist/detectors/structure-tree.js.map +1 -1
  248. package/dist/detectors/structure.js +1 -1
  249. package/dist/detectors/structure.js.map +1 -1
  250. package/dist/detectors/technical-docs.d.ts +5 -0
  251. package/dist/detectors/technical-docs.d.ts.map +1 -1
  252. package/dist/detectors/technical-docs.js +12 -0
  253. package/dist/detectors/technical-docs.js.map +1 -1
  254. package/dist/detectors/time-order.d.ts +2 -0
  255. package/dist/detectors/time-order.d.ts.map +1 -1
  256. package/dist/detectors/time-order.js +33 -5
  257. package/dist/detectors/time-order.js.map +1 -1
  258. package/dist/detectors/undefined-term.d.ts.map +1 -1
  259. package/dist/detectors/undefined-term.js +19 -3
  260. package/dist/detectors/undefined-term.js.map +1 -1
  261. package/dist/detectors/unit-consistency.d.ts +1 -1
  262. package/dist/detectors/unit-consistency.d.ts.map +1 -1
  263. package/dist/detectors/unit-consistency.js +46 -38
  264. package/dist/detectors/unit-consistency.js.map +1 -1
  265. package/dist/detectors/unlisted-items.d.ts +3 -0
  266. package/dist/detectors/unlisted-items.d.ts.map +1 -0
  267. package/dist/detectors/unlisted-items.js +57 -0
  268. package/dist/detectors/unlisted-items.js.map +1 -0
  269. package/dist/detectors/use-count-words.d.ts +8 -0
  270. package/dist/detectors/use-count-words.d.ts.map +1 -0
  271. package/dist/detectors/use-count-words.js +20 -0
  272. package/dist/detectors/use-count-words.js.map +1 -0
  273. package/dist/detectors/use-interval.d.ts +3 -0
  274. package/dist/detectors/use-interval.d.ts.map +1 -0
  275. package/dist/detectors/use-interval.js +39 -0
  276. package/dist/detectors/use-interval.js.map +1 -0
  277. package/dist/detectors/walk-time-distance-mismatch.d.ts +4 -0
  278. package/dist/detectors/walk-time-distance-mismatch.d.ts.map +1 -0
  279. package/dist/detectors/walk-time-distance-mismatch.js +38 -0
  280. package/dist/detectors/walk-time-distance-mismatch.js.map +1 -0
  281. package/dist/document.d.ts.map +1 -1
  282. package/dist/document.js +4 -1
  283. package/dist/document.js.map +1 -1
  284. package/dist/facts/approximate-tolerance.d.ts +7 -0
  285. package/dist/facts/approximate-tolerance.d.ts.map +1 -0
  286. package/dist/facts/approximate-tolerance.js +16 -0
  287. package/dist/facts/approximate-tolerance.js.map +1 -0
  288. package/dist/facts/item-amounts.d.ts +18 -0
  289. package/dist/facts/item-amounts.d.ts.map +1 -0
  290. package/dist/facts/item-amounts.js +67 -0
  291. package/dist/facts/item-amounts.js.map +1 -0
  292. package/dist/facts/labelled-facts.d.ts +4 -0
  293. package/dist/facts/labelled-facts.d.ts.map +1 -1
  294. package/dist/facts/labelled-facts.js +21 -6
  295. package/dist/facts/labelled-facts.js.map +1 -1
  296. package/dist/facts/measure-units.d.ts +5 -0
  297. package/dist/facts/measure-units.d.ts.map +1 -0
  298. package/dist/facts/measure-units.js +28 -0
  299. package/dist/facts/measure-units.js.map +1 -0
  300. package/dist/facts/measures.d.ts +23 -4
  301. package/dist/facts/measures.d.ts.map +1 -1
  302. package/dist/facts/measures.js +68 -19
  303. package/dist/facts/measures.js.map +1 -1
  304. package/dist/facts/retention-periods.d.ts +51 -0
  305. package/dist/facts/retention-periods.d.ts.map +1 -0
  306. package/dist/facts/retention-periods.js +148 -0
  307. package/dist/facts/retention-periods.js.map +1 -0
  308. package/dist/facts/table-facts.d.ts +2 -0
  309. package/dist/facts/table-facts.d.ts.map +1 -1
  310. package/dist/facts/table-facts.js +2 -0
  311. package/dist/facts/table-facts.js.map +1 -1
  312. package/dist/facts/unit-conflicts.d.ts +3 -2
  313. package/dist/facts/unit-conflicts.d.ts.map +1 -1
  314. package/dist/facts/unit-conflicts.js +15 -7
  315. package/dist/facts/unit-conflicts.js.map +1 -1
  316. package/dist/facts/unit-equivalents.d.ts +13 -0
  317. package/dist/facts/unit-equivalents.d.ts.map +1 -0
  318. package/dist/facts/unit-equivalents.js +56 -0
  319. package/dist/facts/unit-equivalents.js.map +1 -0
  320. package/dist/facts/unit-tolerance.d.ts +4 -0
  321. package/dist/facts/unit-tolerance.d.ts.map +1 -0
  322. package/dist/facts/unit-tolerance.js +13 -0
  323. package/dist/facts/unit-tolerance.js.map +1 -0
  324. package/dist/facts/unit-word.d.ts +8 -0
  325. package/dist/facts/unit-word.d.ts.map +1 -0
  326. package/dist/facts/unit-word.js +34 -0
  327. package/dist/facts/unit-word.js.map +1 -0
  328. package/dist/facts/written-precision.d.ts +10 -0
  329. package/dist/facts/written-precision.d.ts.map +1 -0
  330. package/dist/facts/written-precision.js +20 -0
  331. package/dist/facts/written-precision.js.map +1 -0
  332. package/dist/kana-spelling.d.ts +12 -0
  333. package/dist/kana-spelling.d.ts.map +1 -1
  334. package/dist/kana-spelling.js +23 -0
  335. package/dist/kana-spelling.js.map +1 -1
  336. package/dist/model-codes.d.ts +23 -0
  337. package/dist/model-codes.d.ts.map +1 -0
  338. package/dist/model-codes.js +81 -0
  339. package/dist/model-codes.js.map +1 -0
  340. package/dist/name-spelling-chars.d.ts +16 -0
  341. package/dist/name-spelling-chars.d.ts.map +1 -0
  342. package/dist/name-spelling-chars.js +14 -0
  343. package/dist/name-spelling-chars.js.map +1 -0
  344. package/dist/name-variants.d.ts +8 -2
  345. package/dist/name-variants.d.ts.map +1 -1
  346. package/dist/name-variants.js +72 -7
  347. package/dist/name-variants.js.map +1 -1
  348. package/dist/name-word-order.d.ts +49 -0
  349. package/dist/name-word-order.d.ts.map +1 -0
  350. package/dist/name-word-order.js +114 -0
  351. package/dist/name-word-order.js.map +1 -0
  352. package/dist/phone-numbers.d.ts.map +1 -1
  353. package/dist/phone-numbers.js +22 -5
  354. package/dist/phone-numbers.js.map +1 -1
  355. package/dist/place-names.d.ts +12 -5
  356. package/dist/place-names.d.ts.map +1 -1
  357. package/dist/place-names.js +34 -14
  358. package/dist/place-names.js.map +1 -1
  359. package/dist/plugin.d.ts +12 -0
  360. package/dist/plugin.d.ts.map +1 -1
  361. package/dist/preamble-paragraphs.d.ts +2 -1
  362. package/dist/preamble-paragraphs.d.ts.map +1 -1
  363. package/dist/preamble-paragraphs.js +16 -1
  364. package/dist/preamble-paragraphs.js.map +1 -1
  365. package/dist/profile/parse.d.ts.map +1 -1
  366. package/dist/profile/parse.js +11 -0
  367. package/dist/profile/parse.js.map +1 -1
  368. package/dist/return-type.d.ts +33 -0
  369. package/dist/return-type.d.ts.map +1 -0
  370. package/dist/return-type.js +302 -0
  371. package/dist/return-type.js.map +1 -0
  372. package/dist/spacing-code.d.ts +6 -0
  373. package/dist/spacing-code.d.ts.map +1 -0
  374. package/dist/spacing-code.js +16 -0
  375. package/dist/spacing-code.js.map +1 -0
  376. package/dist/structure/amount-range.d.ts +2 -2
  377. package/dist/structure/amount-range.d.ts.map +1 -1
  378. package/dist/structure/amount-range.js +12 -5
  379. package/dist/structure/amount-range.js.map +1 -1
  380. package/dist/structure/capacity-count.d.ts +59 -0
  381. package/dist/structure/capacity-count.d.ts.map +1 -0
  382. package/dist/structure/capacity-count.js +118 -0
  383. package/dist/structure/capacity-count.js.map +1 -0
  384. package/dist/structure/connection-times.d.ts +56 -0
  385. package/dist/structure/connection-times.d.ts.map +1 -0
  386. package/dist/structure/connection-times.js +165 -0
  387. package/dist/structure/connection-times.js.map +1 -0
  388. package/dist/structure/counted-amounts.d.ts +6 -0
  389. package/dist/structure/counted-amounts.d.ts.map +1 -1
  390. package/dist/structure/counted-amounts.js +4 -4
  391. package/dist/structure/counted-amounts.js.map +1 -1
  392. package/dist/structure/date-order-tie.d.ts +13 -0
  393. package/dist/structure/date-order-tie.d.ts.map +1 -0
  394. package/dist/structure/date-order-tie.js +61 -0
  395. package/dist/structure/date-order-tie.js.map +1 -0
  396. package/dist/structure/date-order.d.ts +2 -2
  397. package/dist/structure/date-order.d.ts.map +1 -1
  398. package/dist/structure/date-order.js +26 -9
  399. package/dist/structure/date-order.js.map +1 -1
  400. package/dist/structure/date-outside-period.d.ts +8 -0
  401. package/dist/structure/date-outside-period.d.ts.map +1 -1
  402. package/dist/structure/date-outside-period.js +52 -3
  403. package/dist/structure/date-outside-period.js.map +1 -1
  404. package/dist/structure/date-range.d.ts +6 -2
  405. package/dist/structure/date-range.d.ts.map +1 -1
  406. package/dist/structure/date-range.js +37 -9
  407. package/dist/structure/date-range.js.map +1 -1
  408. package/dist/structure/deadline-after-event.d.ts +23 -0
  409. package/dist/structure/deadline-after-event.d.ts.map +1 -0
  410. package/dist/structure/deadline-after-event.js +42 -0
  411. package/dist/structure/deadline-after-event.js.map +1 -0
  412. package/dist/structure/definition-use.d.ts +1 -1
  413. package/dist/structure/definition-use.d.ts.map +1 -1
  414. package/dist/structure/definition-use.js +55 -2
  415. package/dist/structure/definition-use.js.map +1 -1
  416. package/dist/structure/due-date.d.ts.map +1 -1
  417. package/dist/structure/due-date.js +5 -27
  418. package/dist/structure/due-date.js.map +1 -1
  419. package/dist/structure/labelled-date-order.d.ts +38 -0
  420. package/dist/structure/labelled-date-order.d.ts.map +1 -0
  421. package/dist/structure/labelled-date-order.js +41 -0
  422. package/dist/structure/labelled-date-order.js.map +1 -0
  423. package/dist/structure/layout-codes.d.ts +68 -0
  424. package/dist/structure/layout-codes.d.ts.map +1 -0
  425. package/dist/structure/layout-codes.js +161 -0
  426. package/dist/structure/layout-codes.js.map +1 -0
  427. package/dist/structure/leg-times.d.ts +2 -0
  428. package/dist/structure/leg-times.d.ts.map +1 -1
  429. package/dist/structure/leg-times.js +13 -0
  430. package/dist/structure/leg-times.js.map +1 -1
  431. package/dist/structure/measured-amounts.d.ts +17 -0
  432. package/dist/structure/measured-amounts.d.ts.map +1 -0
  433. package/dist/structure/measured-amounts.js +110 -0
  434. package/dist/structure/measured-amounts.js.map +1 -0
  435. package/dist/structure/party-order.d.ts +33 -0
  436. package/dist/structure/party-order.d.ts.map +1 -0
  437. package/dist/structure/party-order.js +79 -0
  438. package/dist/structure/party-order.js.map +1 -0
  439. package/dist/structure/quantity-range.d.ts +8 -0
  440. package/dist/structure/quantity-range.d.ts.map +1 -0
  441. package/dist/structure/quantity-range.js +36 -0
  442. package/dist/structure/quantity-range.js.map +1 -0
  443. package/dist/structure/range-bands.d.ts +75 -0
  444. package/dist/structure/range-bands.d.ts.map +1 -0
  445. package/dist/structure/range-bands.js +244 -0
  446. package/dist/structure/range-bands.js.map +1 -0
  447. package/dist/structure/rent-multiple.d.ts +76 -0
  448. package/dist/structure/rent-multiple.d.ts.map +1 -0
  449. package/dist/structure/rent-multiple.js +186 -0
  450. package/dist/structure/rent-multiple.js.map +1 -0
  451. package/dist/structure/requirement-lists.d.ts +37 -0
  452. package/dist/structure/requirement-lists.d.ts.map +1 -0
  453. package/dist/structure/requirement-lists.js +89 -0
  454. package/dist/structure/requirement-lists.js.map +1 -0
  455. package/dist/structure/scaled-quantities.d.ts +50 -0
  456. package/dist/structure/scaled-quantities.d.ts.map +1 -0
  457. package/dist/structure/scaled-quantities.js +153 -0
  458. package/dist/structure/scaled-quantities.js.map +1 -0
  459. package/dist/structure/stamp-order.d.ts +16 -0
  460. package/dist/structure/stamp-order.d.ts.map +1 -0
  461. package/dist/structure/stamp-order.js +78 -0
  462. package/dist/structure/stamp-order.js.map +1 -0
  463. package/dist/structure/step-times.d.ts +49 -0
  464. package/dist/structure/step-times.d.ts.map +1 -0
  465. package/dist/structure/step-times.js +128 -0
  466. package/dist/structure/step-times.js.map +1 -0
  467. package/dist/structure/term-deadline.d.ts +19 -0
  468. package/dist/structure/term-deadline.d.ts.map +1 -0
  469. package/dist/structure/term-deadline.js +28 -0
  470. package/dist/structure/term-deadline.js.map +1 -0
  471. package/dist/structure/time-order.d.ts +21 -0
  472. package/dist/structure/time-order.d.ts.map +1 -1
  473. package/dist/structure/time-order.js +5 -6
  474. package/dist/structure/time-order.js.map +1 -1
  475. package/dist/structure/time-range.d.ts +16 -0
  476. package/dist/structure/time-range.d.ts.map +1 -0
  477. package/dist/structure/time-range.js +57 -0
  478. package/dist/structure/time-range.js.map +1 -0
  479. package/dist/structure/total-line.d.ts +16 -0
  480. package/dist/structure/total-line.d.ts.map +1 -0
  481. package/dist/structure/total-line.js +44 -0
  482. package/dist/structure/total-line.js.map +1 -0
  483. package/dist/structure/total.d.ts +7 -1
  484. package/dist/structure/total.d.ts.map +1 -1
  485. package/dist/structure/total.js +28 -5
  486. package/dist/structure/total.js.map +1 -1
  487. package/dist/structure/unlisted-items.d.ts +70 -0
  488. package/dist/structure/unlisted-items.d.ts.map +1 -0
  489. package/dist/structure/unlisted-items.js +152 -0
  490. package/dist/structure/unlisted-items.js.map +1 -0
  491. package/dist/structure/version-order.d.ts +2 -0
  492. package/dist/structure/version-order.d.ts.map +1 -1
  493. package/dist/structure/version-order.js +10 -0
  494. package/dist/structure/version-order.js.map +1 -1
  495. package/dist/structure/walk-time.d.ts +87 -0
  496. package/dist/structure/walk-time.d.ts.map +1 -0
  497. package/dist/structure/walk-time.js +195 -0
  498. package/dist/structure/walk-time.js.map +1 -0
  499. package/dist/surname-near.d.ts +6 -0
  500. package/dist/surname-near.d.ts.map +1 -0
  501. package/dist/surname-near.js +31 -0
  502. package/dist/surname-near.js.map +1 -0
  503. package/dist/table-cells.d.ts +13 -0
  504. package/dist/table-cells.d.ts.map +1 -0
  505. package/dist/table-cells.js +36 -0
  506. package/dist/table-cells.js.map +1 -0
  507. package/dist/table-names.d.ts +42 -0
  508. package/dist/table-names.d.ts.map +1 -0
  509. package/dist/table-names.js +94 -0
  510. package/dist/table-names.js.map +1 -0
  511. package/package.json +3 -3
  512. package/profiles/listing.yaml +20 -0
  513. package/rules/announced-count-mismatch.yaml +2 -2
  514. package/rules/capacity-runtime-mismatch.yaml +66 -0
  515. package/rules/connection-time-short.yaml +52 -0
  516. package/rules/contact-variant.yaml +76 -0
  517. package/rules/count-over-capacity.yaml +55 -0
  518. package/rules/date-outside-period.yaml +4 -4
  519. package/rules/date-stamp-order.yaml +82 -0
  520. package/rules/deadline-after-event.yaml +67 -0
  521. package/rules/defined-name-repeated.yaml +2 -2
  522. package/rules/due-before-issue.yaml +11 -7
  523. package/rules/duration-mismatch.yaml +7 -4
  524. package/rules/duration-product-mismatch.yaml +73 -0
  525. package/rules/fact-conflict.yaml +9 -4
  526. package/rules/heading-echo.yaml +2 -2
  527. package/rules/interval-count-mismatch.yaml +54 -0
  528. package/rules/latin-spacing.yaml +2 -2
  529. package/rules/layout-code-mismatch.yaml +68 -0
  530. package/rules/name-variant.yaml +14 -2
  531. package/rules/no-em-dash.yaml +2 -2
  532. package/rules/party-order.yaml +84 -0
  533. package/rules/preamble-length.yaml +2 -2
  534. package/rules/quantity-range-reversed.yaml +53 -0
  535. package/rules/range-band-mismatch.yaml +70 -0
  536. package/rules/rent-multiple-mismatch.yaml +61 -0
  537. package/rules/required-listed-as-preferred.yaml +102 -0
  538. package/rules/return-type-mismatch.yaml +79 -0
  539. package/rules/scaled-quantity-mismatch.yaml +69 -0
  540. package/rules/step-time-sum-mismatch.yaml +65 -0
  541. package/rules/time-range-reversed.yaml +48 -0
  542. package/rules/title-case-consistency.yaml +2 -2
  543. package/rules/total-mismatch.yaml +2 -2
  544. package/rules/undefined-term.yaml +21 -7
  545. package/rules/unit-mismatch.yaml +12 -4
  546. package/rules/unlisted-item-used.yaml +90 -0
  547. package/rules/unused-definition.yaml +2 -2
  548. package/rules/use-before-definition.yaml +2 -2
  549. package/rules/walk-time-distance-mismatch.yaml +55 -0
  550. package/src/announced-count.ts +6 -3
  551. package/src/contacts/addresses.ts +179 -0
  552. package/src/contacts/emails.ts +63 -0
  553. package/src/contacts/variants.ts +33 -0
  554. package/src/derived/battery-runtime.ts +284 -0
  555. package/src/derived/period-counts.ts +178 -0
  556. package/src/derived/session-hours.ts +62 -0
  557. package/src/derived/time-lengths.ts +229 -0
  558. package/src/derived/unit-counts.ts +18 -0
  559. package/src/derived/use-interval.ts +151 -0
  560. package/src/derived/working-hours.ts +97 -0
  561. package/src/detectors/acronym-expansion.ts +57 -2
  562. package/src/detectors/announced-count.ts +1 -0
  563. package/src/detectors/capacity-runtime.ts +128 -0
  564. package/src/detectors/capitalised-term.ts +147 -0
  565. package/src/detectors/contact-variant.ts +24 -0
  566. package/src/detectors/count-over-capacity.ts +37 -0
  567. package/src/detectors/date-outside-period.ts +5 -1
  568. package/src/detectors/date-stamp-order.ts +28 -0
  569. package/src/detectors/defined-term-form.ts +63 -7
  570. package/src/detectors/definition-use.ts +6 -2
  571. package/src/detectors/derived-numbers.ts +97 -24
  572. package/src/detectors/due-date.ts +10 -2
  573. package/src/detectors/en-shape.ts +11 -4
  574. package/src/detectors/event-deadline.ts +29 -0
  575. package/src/detectors/fact-consistency.ts +64 -3
  576. package/src/detectors/heading-case.ts +13 -4
  577. package/src/detectors/heading-echo.ts +17 -1
  578. package/src/detectors/layout-code.ts +174 -0
  579. package/src/detectors/measured-facts.ts +20 -0
  580. package/src/detectors/name-variant-model-codes.ts +27 -0
  581. package/src/detectors/name-variant.ts +99 -15
  582. package/src/detectors/orthographic-variant.ts +78 -9
  583. package/src/detectors/orthography.ts +16 -6
  584. package/src/detectors/party-order.ts +50 -0
  585. package/src/detectors/quantity-range.ts +47 -0
  586. package/src/detectors/range-band.ts +125 -0
  587. package/src/detectors/registry/capacity-runtime-mismatch.ts +4 -0
  588. package/src/detectors/registry/connection-time-short.ts +4 -0
  589. package/src/detectors/registry/contact-variant.ts +4 -0
  590. package/src/detectors/registry/count-over-capacity.ts +4 -0
  591. package/src/detectors/registry/date-stamp-order.ts +4 -0
  592. package/src/detectors/registry/duration-product-mismatch.ts +4 -0
  593. package/src/detectors/registry/event-deadline.ts +4 -0
  594. package/src/detectors/registry/interval-count-mismatch.ts +4 -0
  595. package/src/detectors/registry/layout-code-mismatch.ts +4 -0
  596. package/src/detectors/registry/party-order.ts +4 -0
  597. package/src/detectors/registry/quantity-range.ts +4 -0
  598. package/src/detectors/registry/range-band-mismatch.ts +4 -0
  599. package/src/detectors/registry/rent-multiple-mismatch.ts +4 -0
  600. package/src/detectors/registry/requirement-lists.ts +4 -0
  601. package/src/detectors/registry/return-type.ts +4 -0
  602. package/src/detectors/registry/scaled-quantity-mismatch.ts +4 -0
  603. package/src/detectors/registry/step-time-sum-mismatch.ts +4 -0
  604. package/src/detectors/registry/time-range-reversed.ts +4 -0
  605. package/src/detectors/registry/unlisted-item-used.ts +4 -0
  606. package/src/detectors/registry/walk-time-distance-mismatch.ts +4 -0
  607. package/src/detectors/rent-multiple-mismatch.ts +56 -0
  608. package/src/detectors/requirement-lists.ts +65 -0
  609. package/src/detectors/return-type.ts +30 -0
  610. package/src/detectors/scaled-quantity.ts +49 -0
  611. package/src/detectors/signals.ts +3 -1
  612. package/src/detectors/step-time-sum.ts +55 -0
  613. package/src/detectors/structure-tree.ts +42 -6
  614. package/src/detectors/structure.ts +1 -1
  615. package/src/detectors/technical-docs.ts +12 -0
  616. package/src/detectors/time-order.ts +45 -6
  617. package/src/detectors/undefined-term.ts +20 -3
  618. package/src/detectors/unit-consistency.ts +50 -38
  619. package/src/detectors/unlisted-items.ts +72 -0
  620. package/src/detectors/use-count-words.ts +32 -0
  621. package/src/detectors/use-interval.ts +51 -0
  622. package/src/detectors/walk-time-distance-mismatch.ts +45 -0
  623. package/src/document.ts +4 -1
  624. package/src/facts/approximate-tolerance.ts +19 -0
  625. package/src/facts/item-amounts.ts +94 -0
  626. package/src/facts/labelled-facts.ts +29 -7
  627. package/src/facts/measure-units.ts +31 -0
  628. package/src/facts/measures.ts +113 -25
  629. package/src/facts/retention-periods.ts +216 -0
  630. package/src/facts/table-facts.ts +3 -0
  631. package/src/facts/unit-conflicts.ts +22 -9
  632. package/src/facts/unit-equivalents.ts +70 -0
  633. package/src/facts/unit-tolerance.ts +17 -0
  634. package/src/facts/unit-word.ts +35 -0
  635. package/src/facts/written-precision.ts +23 -0
  636. package/src/kana-spelling.ts +25 -0
  637. package/src/model-codes.ts +104 -0
  638. package/src/name-spelling-chars.ts +34 -0
  639. package/src/name-variants.ts +83 -7
  640. package/src/name-word-order.ts +149 -0
  641. package/src/phone-numbers.ts +26 -6
  642. package/src/place-names.ts +53 -14
  643. package/src/plugin.ts +7 -0
  644. package/src/preamble-paragraphs.ts +21 -2
  645. package/src/profile/parse.ts +10 -0
  646. package/src/return-type.ts +361 -0
  647. package/src/spacing-code.ts +26 -0
  648. package/src/structure/amount-range.ts +16 -7
  649. package/src/structure/capacity-count.ts +196 -0
  650. package/src/structure/connection-times.ts +226 -0
  651. package/src/structure/counted-amounts.ts +5 -4
  652. package/src/structure/date-order-tie.ts +86 -0
  653. package/src/structure/date-order.ts +41 -14
  654. package/src/structure/date-outside-period.ts +77 -7
  655. package/src/structure/date-range.ts +45 -9
  656. package/src/structure/deadline-after-event.ts +67 -0
  657. package/src/structure/definition-use.ts +59 -2
  658. package/src/structure/due-date.ts +8 -32
  659. package/src/structure/labelled-date-order.ts +84 -0
  660. package/src/structure/layout-codes.ts +210 -0
  661. package/src/structure/leg-times.ts +15 -0
  662. package/src/structure/measured-amounts.ts +138 -0
  663. package/src/structure/party-order.ts +115 -0
  664. package/src/structure/quantity-range.ts +54 -0
  665. package/src/structure/range-bands.ts +316 -0
  666. package/src/structure/rent-multiple.ts +277 -0
  667. package/src/structure/requirement-lists.ts +145 -0
  668. package/src/structure/scaled-quantities.ts +225 -0
  669. package/src/structure/stamp-order.ts +100 -0
  670. package/src/structure/step-times.ts +169 -0
  671. package/src/structure/term-deadline.ts +35 -0
  672. package/src/structure/time-order.ts +8 -7
  673. package/src/structure/time-range.ts +64 -0
  674. package/src/structure/total-line.ts +63 -0
  675. package/src/structure/total.ts +41 -6
  676. package/src/structure/unlisted-items.ts +207 -0
  677. package/src/structure/version-order.ts +10 -0
  678. package/src/structure/walk-time.ts +285 -0
  679. package/src/surname-near.ts +31 -0
  680. package/src/table-cells.ts +41 -0
  681. package/src/table-names.ts +113 -0
@@ -0,0 +1,361 @@
1
+ // One function's return type stated two ways on one page: the signature in a code block against a "Returns:" line, a
2
+ // table's return column, or a sentence ("measure returns a string", 「measure は文字列を返します」). Pure; reads the
3
+ // Markdown source. The words (Returns, 戻り値, returns, 返します, は, and the type words string, 文字列) come from the
4
+ // language's lexicons. A statement is compared only with the signatures of the function it names, and only when both
5
+ // sides are readable. The signature reader is small and TypeScript/Python-shaped; it may later be shared with
6
+ // parameter-table.ts.
7
+
8
+ /** The words of one language that state a return type. `kinds` maps a lower-cased type word to a kind (string, array). */
9
+ export type ReturnWords = {
10
+ readonly labels: readonly string[];
11
+ readonly verbs: readonly string[];
12
+ /** What may follow the function's name before the verb (は, が); none for a language that puts the verb first. */
13
+ readonly subjects: readonly string[];
14
+ readonly kinds: ReadonlyMap<string, string>;
15
+ /** Words opening a clause about when (when, とき): a type word on the far side of one names the input, not the result. */
16
+ readonly clauses: readonly string[];
17
+ /** Words making the type word before them one item of the result (per, ごと). */
18
+ readonly items: readonly string[];
19
+ };
20
+
21
+ /** A statement that disagrees with every signature of its function: what it says, what a signature says, and where. */
22
+ export type ReturnTypeClash = { readonly fn: string; readonly stated: string; readonly signature: string; readonly offset: number };
23
+
24
+ /** A return type as written: a type in code (`string[]`), or the kind a type word names (a string → string). */
25
+ type Stated = { readonly code: string; readonly written: string } | { readonly kind: string; readonly written: string };
26
+
27
+ type Statement = { readonly fn: string; readonly stated: Stated; readonly offset: number };
28
+ type Signature = { readonly fn: string; readonly params: string; readonly returns: string; readonly offset: number };
29
+ type Line = { readonly text: string; readonly start: number; readonly fenced: boolean; readonly section: number };
30
+
31
+ const FENCE = /^ {0,3}(`{3,}|~{3,})/u;
32
+ const HEADING = /^ {0,3}#{1,6}[ \t]/u;
33
+ const TABLE_ROW = /^[ \t]{0,3}\|/u;
34
+ const DELIMITER_ROW = /^[ \t]{0,3}\|?[ \t]*:?-{3,}/u;
35
+ const IDENTIFIER = /^[A-Za-z_$][\w$]*/u;
36
+ const DECLARATION_HEAD = /^(?:(?:export|default|declare|async|public|static)\s+)*(?:(?:function\*?|def|fn|func)\s+)?/u;
37
+ const ARROW_HEAD = /^(?:(?:export|declare)\s+)*(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s*)?/u;
38
+ const RETURN_MARK = /^\s*(?::|->)\s*/u;
39
+ /** What may close a declaration line after its return type: an arrow's `=>` or a body's `{` with what follows, `;`, Python's `:`. */
40
+ const withoutDeclarationTail = (text: string): string => {
41
+ const cut = [text.indexOf("=>"), text.indexOf("{")].filter((at) => at !== -1);
42
+ const head = (cut.length === 0 ? text : text.slice(0, Math.min(...cut))).trim();
43
+ return head.endsWith(";") || head.endsWith(":") ? head.slice(0, -1).trim() : head;
44
+ };
45
+ const GENERICS = /^<[^<>()]*>/u;
46
+ const TYPE_NAME = String.raw`[A-Za-z_$][\w$.]*(?:<[\w$.,\s<>[\]|]*>|\[[\w$.,\s[\]|]*\])?(?:\[\])*`;
47
+ /** A type as code writes it: names, generic arguments, array brackets, and unions of those. */
48
+ const TYPE_TEXT = new RegExp(String.raw`^${TYPE_NAME}(?:\s*\|\s*${TYPE_NAME})*$`, "u");
49
+ /** Where a sentence ends, so a type word of the next sentence is not read as this one's. */
50
+ const SENTENCE_END = /[。!?!?;]|\.(?=\s|$)/u;
51
+ const NOTHING_TYPES: ReadonlySet<string> = new Set(["null", "undefined", "void", "None"]);
52
+ /** The kind of a type name in code; a name not listed (a class, a generic parameter, Promise<…>) has none. */
53
+ const CODE_KINDS: ReadonlyMap<string, string> = new Map([
54
+ ["string", "string"],
55
+ ["str", "string"],
56
+ ["number", "number"],
57
+ ["int", "number"],
58
+ ["float", "number"],
59
+ ["bigint", "number"],
60
+ ["boolean", "boolean"],
61
+ ["bool", "boolean"],
62
+ ["list", "array"],
63
+ ["tuple", "array"],
64
+ ]);
65
+
66
+ const linesOf = (source: string): Line[] => {
67
+ const state = { start: 0, fence: "", section: 0 };
68
+ return source.split("\n").map((text) => {
69
+ const start = state.start;
70
+ state.start += text.length + 1;
71
+ const mark = FENCE.exec(text)?.[1];
72
+ const closes = mark !== undefined && state.fence !== "" && mark[0] === state.fence[0] && mark.length >= state.fence.length;
73
+ if (mark !== undefined && (state.fence === "" || closes)) {
74
+ state.fence = closes ? "" : mark;
75
+ return { text, start, fenced: true, section: state.section };
76
+ }
77
+ if (state.fence === "" && HEADING.test(text)) state.section += 1;
78
+ return { text, start, fenced: state.fence !== "", section: state.section };
79
+ });
80
+ };
81
+
82
+ /** The index of the ) that closes the ( at `open`, or -1. */
83
+ const closingParen = (text: string, open: number): number => {
84
+ const state = { depth: 0, at: -1 };
85
+ Array.from(text.slice(open)).some((char, index) => {
86
+ if (char === "(") state.depth += 1;
87
+ if (char === ")") state.depth -= 1;
88
+ if (state.depth === 0) state.at = open + index;
89
+ return state.depth === 0;
90
+ });
91
+ return state.at;
92
+ };
93
+
94
+ const squeeze = (text: string): string => text.replace(/\s+/gu, "");
95
+
96
+ /** `Array<string>` is `string[]`: the inner generic first, so Array<Array<string>> becomes string[][]. */
97
+ const withoutArrayGenerics = (type: string): string => {
98
+ const next = type.replace(/(?<![\w$.])Array<([^<>]*)>/u, (_whole, inner: string) => (inner.includes("|") ? `(${inner})[]` : `${inner}[]`));
99
+ return next === type ? type : withoutArrayGenerics(next);
100
+ };
101
+
102
+ /** The members of a union at its top level, outside brackets. */
103
+ const unionMembers = (type: string): string[] => {
104
+ const state = { depth: 0, from: 0 };
105
+ const members: string[] = [];
106
+ Array.from(type).forEach((char, index) => {
107
+ if ("(<[{".includes(char)) state.depth += 1;
108
+ if (")>]}".includes(char)) state.depth -= 1;
109
+ if (char !== "|" || state.depth !== 0) return;
110
+ members.push(type.slice(state.from, index));
111
+ state.from = index + 1;
112
+ });
113
+ return [...members, type.slice(state.from)].filter((member) => member !== "");
114
+ };
115
+
116
+ /** A code type's members in one spelling: no spaces, Array<T> as T[], sorted. */
117
+ export const typeMembers = (type: string): string[] =>
118
+ unionMembers(withoutArrayGenerics(squeeze(type))).toSorted((left, right) => left.localeCompare(right, "en"));
119
+
120
+ /** The kind of one member, or undefined when its name says nothing a type word could (WrapResult, T, Promise<…>). */
121
+ const kindOfMember = (member: string): string | undefined => {
122
+ if (member.endsWith("[]") || /^(?:list|List|tuple|Tuple)\[/u.test(member)) return "array";
123
+ return CODE_KINDS.get(member);
124
+ };
125
+
126
+ /**
127
+ * A type in a sentence or a cell is read only when it can be a type and not a parameter's name: each member has a kind, is
128
+ * null or the like, or is a capitalised name (WrapResult). `text` is a name, and `null` alone says when, not what.
129
+ */
130
+ const isReadableCode = (type: string): boolean => {
131
+ const members = typeMembers(type);
132
+ const readable = members.every((member) => kindOfMember(member) !== undefined || NOTHING_TYPES.has(member) || /^[A-Z]/u.test(member));
133
+ return TYPE_TEXT.test(type.trim()) && readable && members.some((member) => !NOTHING_TYPES.has(member));
134
+ };
135
+
136
+ const declaredAt = (line: string): { readonly fn: string; readonly afterName: number } | undefined => {
137
+ const arrow = ARROW_HEAD.exec(line);
138
+ if (arrow?.[1] !== undefined) return { fn: arrow[1], afterName: arrow[0].length };
139
+ const headLength = DECLARATION_HEAD.exec(line)?.[0].length ?? 0;
140
+ const fn = IDENTIFIER.exec(line.slice(headLength))?.[0];
141
+ return fn === undefined ? undefined : { fn, afterName: headLength + fn.length };
142
+ };
143
+
144
+ /** A declaration with a return type (`wrap(text: string): string[]`, `def f(x) -> str:`); a call has none. */
145
+ export const signatureOf = (line: string, offset = 0): Signature | undefined => {
146
+ const declared = declaredAt(line);
147
+ if (declared === undefined) return undefined;
148
+ const open = declared.afterName + (GENERICS.exec(line.slice(declared.afterName))?.[0].length ?? 0);
149
+ const close = line[open] === "(" ? closingParen(line, open) : -1;
150
+ const mark = close === -1 ? null : RETURN_MARK.exec(line.slice(close + 1));
151
+ if (close === -1 || mark === null) return undefined;
152
+ const returns = withoutDeclarationTail(line.slice(close + 1 + mark[0].length));
153
+ return TYPE_TEXT.test(returns) ? { fn: declared.fn, params: squeeze(line.slice(open + 1, close)), returns, offset } : undefined;
154
+ };
155
+
156
+ const signaturesOf = (lines: readonly Line[]): Signature[] =>
157
+ lines.flatMap((line) => {
158
+ const lead = line.text.length - line.text.trimStart().length;
159
+ const signature = line.fenced ? signatureOf(line.text.trim(), line.start + lead) : undefined;
160
+ return signature === undefined ? [] : [signature];
161
+ });
162
+
163
+ const escaped = (text: string): string => text.replace(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`);
164
+
165
+ const isSpaced = (word: string): boolean => /^[A-Za-z]/u.test(word);
166
+
167
+ /** A word's occurrences: whole words where words are spaced, any occurrence where they are not. */
168
+ const wordPattern = (word: string): RegExp => (isSpaced(word) ? new RegExp(String.raw`\b${escaped(word)}\b`, "giu") : new RegExp(escaped(word), "gu"));
169
+
170
+ const startsWithWord = (text: string, word: string): boolean =>
171
+ isSpaced(word) ? new RegExp(String.raw`^${escaped(word)}\b`, "iu").test(text) : text.startsWith(word);
172
+
173
+ /** "returns true when …" is a boolean: a literal says its kind in any language, in code or not. */
174
+ const LITERAL_KINDS: ReadonlyMap<string, string> = new Map([
175
+ ["true", "boolean"],
176
+ ["false", "boolean"],
177
+ ]);
178
+
179
+ /** Whether `word` stands in `text` as a whole value, not followed by a word making it one item (string per line). */
180
+ const namesWhole = (text: string, word: string, items: readonly string[]): boolean =>
181
+ [...text.matchAll(wordPattern(word))].some((match) => {
182
+ const next = text.slice(match.index + match[0].length).trimStart();
183
+ return !items.some((item) => startsWithWord(next, item));
184
+ });
185
+
186
+ /** The type words in a stretch of text, outside code spans other than a literal (`false`). */
187
+ const typeWordsIn = (text: string, words: ReturnWords): [string, string][] => {
188
+ const bare = text.replace(/`([^`]*)`/gu, (_whole, inner: string) => (LITERAL_KINDS.has(inner.toLowerCase()) ? ` ${inner} ` : " "));
189
+ return [...words.kinds, ...LITERAL_KINDS].filter(([word]) => namesWhole(bare, word, words.items));
190
+ };
191
+
192
+ /** A language with no subject words (en) puts the verb right after the name, and a clause about when after the result. */
193
+ const isVerbFirst = (words: ReturnWords): boolean => words.subjects.length === 0;
194
+
195
+ /**
196
+ * The part of a statement that can name what is returned, without a clause about when (when …, …とき): before the first
197
+ * clause word where the verb comes first, after the last where the verb comes last.
198
+ */
199
+ const returnedPart = (text: string, words: ReturnWords): string => {
200
+ const cuts = words.clauses.flatMap((word) =>
201
+ [...text.matchAll(wordPattern(word))].map((match) => ({ from: match.index, to: match.index + match[0].length })),
202
+ );
203
+ if (cuts.length === 0) return text;
204
+ return isVerbFirst(words) ? text.slice(0, Math.min(...cuts.map((cut) => cut.from))) : text.slice(Math.max(...cuts.map((cut) => cut.to)));
205
+ };
206
+
207
+ /** The one kind a stretch's type words name. */
208
+ const kindIn = (text: string, returnWords: ReturnWords): Stated | undefined => {
209
+ const words = typeWordsIn(text, returnWords);
210
+ const named = new Set(words.map(([, kind]) => kind));
211
+ return named.size === 1 && words[0] !== undefined ? { kind: words[0][1], written: words[0][0] } : undefined;
212
+ };
213
+
214
+ /** What a stretch of text says is returned: one readable code span, else the one kind its type words name. */
215
+ const statedIn = (text: string, words: ReturnWords): Stated | undefined => {
216
+ const trimmed = returnedPart(text, words).trim();
217
+ if (TYPE_TEXT.test(trimmed) && isReadableCode(trimmed)) return { code: trimmed, written: trimmed };
218
+ const spans = new Set([...trimmed.matchAll(/`([^`]+)`/gu)].map((match) => match[1] ?? "").filter(isReadableCode));
219
+ if (spans.size === 1) return { code: [...spans][0] ?? "", written: [...spans][0] ?? "" };
220
+ return spans.size === 0 ? kindIn(trimmed, words) : undefined;
221
+ };
222
+
223
+ const firstSentence = (text: string): string => text.split(SENTENCE_END)[0] ?? "";
224
+
225
+ const lineOf = (lines: readonly Line[], offset: number): Line | undefined =>
226
+ lines.find((line) => line.start <= offset && offset <= line.start + line.text.length);
227
+
228
+ /** The function each section's "Returns:" line is about: the one function its code blocks declare. */
229
+ const sectionFunctions = (lines: readonly Line[], signatures: readonly Signature[]): Map<number, string> => {
230
+ const bySection = new Map<number, Set<string>>();
231
+ signatures.forEach((signature) => {
232
+ const section = lineOf(lines, signature.offset)?.section ?? -1;
233
+ bySection.set(section, (bySection.get(section) ?? new Set<string>()).add(signature.fn));
234
+ });
235
+ return new Map([...bySection].flatMap(([section, names]): [number, string][] => (names.size === 1 ? [[section, [...names].join("")]] : [])));
236
+ };
237
+
238
+ const labelPattern = (labels: readonly string[]): RegExp =>
239
+ new RegExp(String.raw`^[ \t]*(?:[-*+][ \t]+)?(?:\*\*|__)?(?:${labels.map(escaped).join("|")})(?:\*\*|__)?[ \t]*[::](?:\*\*|__)?`, "iu");
240
+
241
+ /** A "Returns:" line under a heading whose code blocks declare one function. */
242
+ const labelStatements = (lines: readonly Line[], functions: ReadonlyMap<number, string>, words: ReturnWords): Statement[] => {
243
+ if (words.labels.length === 0) return [];
244
+ const label = labelPattern(words.labels);
245
+ return lines.flatMap((line) => {
246
+ const head = line.fenced ? null : label.exec(line.text);
247
+ const fn = functions.get(line.section);
248
+ if (head === null || fn === undefined) return [];
249
+ const stated = statedIn(firstSentence(line.text.slice(head[0].length)), words);
250
+ return stated === undefined ? [] : [{ fn, stated, offset: line.start }];
251
+ });
252
+ };
253
+
254
+ const cellsOf = (row: string): string[] => row.trim().replace(/^\|/u, "").replace(/\|$/u, "").split("|");
255
+
256
+ const cellText = (cell: string): string => cell.trim().replace(/^(?:\*\*|__)(.*)(?:\*\*|__)$/u, "$1");
257
+
258
+ /** The function a table row names in its first cell: `wrap`, wrap(), `measure(text)`. */
259
+ const rowFunction = (cell: string): string => {
260
+ const name = cellText(cell).replace(/`/gu, "").trim();
261
+ const open = name.indexOf("(");
262
+ return name.endsWith(")") && open > 0 ? name.slice(0, open).trim() : name;
263
+ };
264
+
265
+ /** A table with a return column (Returns, 戻り値) whose rows name functions in their first cell. */
266
+ const tableStatements = (lines: readonly Line[], known: ReadonlySet<string>, words: ReturnWords): Statement[] => {
267
+ const labels = new Set(words.labels.map((label) => label.toLowerCase()));
268
+ const state = { column: -1 };
269
+ return lines.flatMap((line, index) => {
270
+ if (line.fenced || !TABLE_ROW.test(line.text)) {
271
+ state.column = -1;
272
+ return [];
273
+ }
274
+ if (DELIMITER_ROW.test(line.text)) {
275
+ state.column = cellsOf(lines[index - 1]?.text ?? "").findIndex((cell) => labels.has(cellText(cell).toLowerCase()));
276
+ return [];
277
+ }
278
+ const cells = cellsOf(line.text);
279
+ const fn = rowFunction(cells[0] ?? "");
280
+ const stated = state.column > 0 && known.has(fn) ? statedIn(cells[state.column] ?? "", words) : undefined;
281
+ return stated === undefined ? [] : [{ fn, stated, offset: line.start }];
282
+ });
283
+ };
284
+
285
+ /** `measure returns …` / 「measure は … 返します」: the function's name, an optional subject word, then the verb. */
286
+ const sentencePattern = (fn: string, words: ReturnWords): RegExp => {
287
+ const subject = words.subjects.length === 0 ? "" : String.raw`(?:(?:${words.subjects.map(escaped).join("|")})[、,]?)?`;
288
+ const verbs = words.verbs.map(escaped).join("|");
289
+ return new RegExp(String.raw`(?<![\w$.])\x60?${escaped(fn)}(?:\(\))?\x60?\s*${subject}([^。!?!?;]*?)(?:${verbs})(?![\p{L}\p{N}])`, "gu");
290
+ };
291
+
292
+ /** A sentence whose subject is a function's name and whose verb says it returns something. */
293
+ const sentenceStatements = (lines: readonly Line[], known: ReadonlySet<string>, words: ReturnWords): Statement[] => {
294
+ if (words.verbs.length === 0) return [];
295
+ const prose = lines.filter((line) => !line.fenced && !HEADING.test(line.text) && !TABLE_ROW.test(line.text));
296
+ return [...known].flatMap((fn) =>
297
+ prose.flatMap((line) =>
298
+ [...line.text.matchAll(sentencePattern(fn, words))].flatMap((match) => {
299
+ const between = match[1] ?? "";
300
+ // Where the verb comes first, nothing stands between the name and the verb.
301
+ const verbFirst = isVerbFirst(words);
302
+ if (verbFirst && between.trim() !== "") return [];
303
+ const after = firstSentence(line.text.slice(match.index + match[0].length));
304
+ const stated = statedIn(verbFirst ? after : between, words);
305
+ return stated === undefined ? [] : [{ fn, stated, offset: line.start + match.index }];
306
+ }),
307
+ ),
308
+ );
309
+ };
310
+
311
+ const isSubset = (small: readonly string[], large: readonly string[]): boolean => small.every((member) => large.includes(member));
312
+
313
+ /** Whether a statement can be the signature's return type; true when either side is too vague to say otherwise. */
314
+ const agrees = (stated: Stated, returns: string): boolean => {
315
+ const members = typeMembers(returns);
316
+ if ("code" in stated) {
317
+ const said = typeMembers(stated.code);
318
+ return isSubset(said, members);
319
+ }
320
+ const kinds = members.filter((member) => !NOTHING_TYPES.has(member)).map(kindOfMember);
321
+ return kinds.includes(undefined) || kinds.includes(stated.kind);
322
+ };
323
+
324
+ const clashOf = (fn: string, stated: string, signature: Signature, offset: number): ReturnTypeClash => ({
325
+ fn,
326
+ stated,
327
+ signature: signature.returns,
328
+ offset,
329
+ });
330
+
331
+ /** Two declarations with the same parameters and different return types; overloads (other parameters) are left alone. */
332
+ const signatureClashes = (signatures: readonly Signature[]): ReturnTypeClash[] =>
333
+ signatures.flatMap((later, index) => {
334
+ const earlier = signatures
335
+ .slice(0, index)
336
+ .find(
337
+ (signature) =>
338
+ signature.fn === later.fn && signature.params === later.params && typeMembers(signature.returns).join("|") !== typeMembers(later.returns).join("|"),
339
+ );
340
+ return earlier === undefined ? [] : [clashOf(later.fn, later.returns, earlier, later.offset)];
341
+ });
342
+
343
+ /** Every place on the page that states a function's return type differently from all of the function's signatures. */
344
+ export const returnTypeClashes = (source: string, words: ReturnWords): ReturnTypeClash[] => {
345
+ const lines = linesOf(source);
346
+ const signatures = signaturesOf(lines);
347
+ const known = new Set(signatures.map((signature) => signature.fn));
348
+ const statements = [
349
+ ...labelStatements(lines, sectionFunctions(lines, signatures), words),
350
+ ...tableStatements(lines, known, words),
351
+ ...sentenceStatements(lines, known, words),
352
+ ];
353
+ const statementClashes = statements.flatMap((statement) => {
354
+ const own = signatures.filter((signature) => signature.fn === statement.fn);
355
+ const first = own[0];
356
+ return first === undefined || own.some((signature) => agrees(statement.stated, signature.returns))
357
+ ? []
358
+ : [clashOf(statement.fn, statement.stated.written, first, statement.offset)];
359
+ });
360
+ return [...signatureClashes(signatures), ...statementClashes].toSorted((left, right) => left.offset - right.offset);
361
+ };
@@ -0,0 +1,26 @@
1
+ import type { Span } from "./plugin.ts";
2
+
3
+ // Text beside which a space is not a spacing choice: a clock time (13:30), which like 3時 names a time rather than a
4
+ // quantity, and an e-mail address or a URL, whose end a reader and an auto-linker find by the space after it. Pure.
5
+
6
+ /** A clock time: hours, a colon, minutes, and seconds optionally (9:05, 13:30, 08:00:15). */
7
+ const CLOCK_TIME = /(?<![\d::.])\d{1,2}[::][0-5]\d(?:[::][0-5]\d)?(?![\d::])/gu;
8
+
9
+ /** A run of printable ASCII without brackets: an address ends where Japanese text, a space or a bracket starts (…/faq で). */
10
+ const ASCII_RUN = /[!-'*-~]+/gu;
11
+
12
+ /** An e-mail address (a local part, @, and a domain with a dot) or a URL with its scheme or www. */
13
+ const EMAIL = /^[\w.+-]+@[\w-]+\.[\w.-]+$/u;
14
+ const URL_START = /^(?:https?:\/\/|www\.)/u;
15
+
16
+ const isAddress = (word: string): boolean => EMAIL.test(word) || URL_START.test(word);
17
+
18
+ const spansOf = (text: string, pattern: RegExp): Span[] =>
19
+ [...text.matchAll(pattern)].map((match) => ({ start: match.index, end: match.index + match[0].length }));
20
+
21
+ export const clockTimeSpans = (text: string): Span[] => spansOf(text, CLOCK_TIME);
22
+
23
+ export const addressSpans = (text: string): Span[] => spansOf(text, ASCII_RUN).filter((span) => isAddress(text.slice(span.start, span.end)));
24
+
25
+ /** Whether the character at `at` is inside one of the spans. */
26
+ export const isInsideSpan = (spans: readonly Span[], at: number): boolean => spans.some((span) => span.start <= at && at < span.end);
@@ -1,11 +1,11 @@
1
- // amount-range-reversed: a range of amounts whose upper end is below its lower end (月給30万円〜25万円, $90,000–$70,000,
2
- // 下限30万円・上限25万円). The amounts come read (currency, value, the word of scale they carry); the words that join a range,
3
- // change a price, and label a bound come from the lexicons. Pure.
1
+ // amount-range-reversed and quantity-range-reversed: a range whose upper end is below its lower end (月給30万円〜25万円,
2
+ // $90,000–$70,000, 下限30万円・上限25万円, 5–2 kg, -10〜-40℃). The ends come read (currency or unit, value, the word of scale
3
+ // they carry); the words that join a range, change a value, and label a bound come from the lexicons. Pure.
4
4
  import { escapeRegExp } from "../orthography.ts";
5
5
  import { amountValue, type ScaleWord } from "./amount-scale.ts";
6
6
  import type { StructureIssue } from "./issues.ts";
7
7
 
8
- /** One amount written with its currency. scale is the value of the word of scale written in it (万, million), if any. */
8
+ /** One amount written with its currency (or a quantity with its unit, in currency). scale is the value of the word of scale written in it (万, million), if any. */
9
9
  export type RangeAmount = {
10
10
  readonly offset: number;
11
11
  readonly end: number;
@@ -27,7 +27,7 @@ export type AmountRangeWords = {
27
27
  readonly lowers: readonly string[];
28
28
  readonly uppers: readonly string[];
29
29
  readonly links: readonly string[];
30
- /** The words of scale (万, million, k) and the digits of a number (the currency reader's pattern). */
30
+ /** The words of scale (万, million, k) and the digits of a number (the reader's pattern; a quantity's may open with a minus sign). */
31
31
  readonly scales: readonly ScaleWord[];
32
32
  readonly number: string;
33
33
  };
@@ -94,11 +94,20 @@ const alternation = (patterns: readonly string[]): string => patterns.toSorted((
94
94
 
95
95
  const scalePart = (words: AmountRangeWords): string => {
96
96
  const scales = alternation(words.scales.map((scale) => escapeRegExp(scale.word)));
97
- return scales === "" ? "" : `(?:\\s?(${scales}))?`;
97
+ // With no words of scale the group still stands, never matching, so the joint keeps its group number.
98
+ return scales === "" ? "((?!))?" : `(?:\\s?(${scales}))?`;
98
99
  };
99
100
 
100
101
  const jointPart = (words: AmountRangeWords): string => `(\\s?(?:${alternation([...words.connectors, ...words.openers].map(wordPattern))})\\s?)`;
101
102
 
103
+ const MINUS = /^[-−-]/u;
104
+
105
+ /** The value of a bare number, negative when it opens with a minus sign (-10〜-40℃). */
106
+ const signedValue = (digits: string): number | undefined => {
107
+ const value = amountValue(digits.replace(MINUS, ""), []);
108
+ return value === undefined || !MINUS.test(digits) ? value : -value;
109
+ };
110
+
102
111
  /** A bare number (no currency) and its word of scale, if any. */
103
112
  const bareReading = (
104
113
  span: { readonly offset: number; readonly end: number },
@@ -106,7 +115,7 @@ const bareReading = (
106
115
  scaleWord: string | undefined,
107
116
  words: AmountRangeWords,
108
117
  ): Reading | undefined => {
109
- const value = amountValue(digits, []);
118
+ const value = signedValue(digits);
110
119
  if (value === undefined) return undefined;
111
120
  const scale = scaleOf(scaleWord, words);
112
121
  return { ...span, value: scale === undefined ? value : value * scale, scale };
@@ -0,0 +1,196 @@
1
+ import type { Span } from "../plugin.ts";
2
+ import type { StructureIssue } from "./issues.ts";
3
+ import { escapeRegExp } from "../orthography.ts";
4
+
5
+ /**
6
+ * 書いた定員より多い申込・招待・参加の人数(定員:90名 と 95名の方にお申し込み、Capacity: 90 seats と 95 members have registered)。
7
+ * 定員の語(定員、先着、capacity、limited to)と人数の語(申込、招待者、have registered)を付けた数だけを読み、語の無い数は読まない。
8
+ * 比べるのは、文書に定員が一つ(一つの値)だけ書いてあるときだけ。二つ以上あれば、どの人数がどの定員のものか決められない。
9
+ * 数える物の組が違う数(30組と45名)は比べない。キャンセル待ちや抽選の語が文書にあれば、定員を超える申込は書いたとおりのことなので
10
+ * 言わない。一回あたりの数(各回定員20名、20 per session)は、定員でも人数でも比べない。全体の人数と一回の定員は比べられない。
11
+ * 去年や前回の人数(昨年は120名が参加)は、この催しの人数ではないので読まない。語は言語パッケージの語彙表から受け取る。
12
+ */
13
+
14
+ /** 語彙表の一語。position は数のどちら側に書くか。group は語の種類(数える物の組、place の付いた定員の語)。 */
15
+ export type CountMark = { readonly pattern: string; readonly position?: "before" | "after" | undefined; readonly group?: string | undefined };
16
+
17
+ export type CapacityWords = {
18
+ /** 定員の語。group が place のものは、席の数(seats、places)にだけ付く(there are 25 places)。 */
19
+ readonly capacities: readonly CountMark[];
20
+ /** 申込・招待・参加の人数の語。 */
21
+ readonly counts: readonly CountMark[];
22
+ /** 数える物の語(名、人、席、people、seats)。group は組(head は人、place は席で人と比べる、party は組や家族)。 */
23
+ readonly units: readonly CountMark[];
24
+ /** 文書にあれば定員を超える申込を言わない語(キャンセル待ち、抽選、waitlist)。 */
25
+ readonly overflows: readonly string[];
26
+ /** 同じ行にあれば一回・一日あたりの数だと言う語(各回、per session)。 */
27
+ readonly perSession: readonly string[];
28
+ /** 人数と同じ行にあれば別の時の人数だと言う語(昨年、前回、last year)。 */
29
+ readonly otherOccasions: readonly string[];
30
+ /** 人数の後ろの句にあれば、申し込んでいない人の数だと言う語(していません、not)。 */
31
+ readonly negations: readonly string[];
32
+ };
33
+
34
+ /** 句の切れ目。人数の後ろの打ち消しは、ここまでで探す。 */
35
+ const CLAUSE_BREAK = /[。..!?!?;;、,,\n]/u;
36
+
37
+ const clauseAfter = (source: string, end: number): string => {
38
+ const rest = source.slice(end);
39
+ const cut = CLAUSE_BREAK.exec(rest)?.index ?? rest.length;
40
+ return rest.slice(0, cut);
41
+ };
42
+
43
+ /** 語の付いた数。unit は数える物の組で、単位を書かない数(capacity 30)は undefined。line はその数の行。 */
44
+ export type LabelledCount = {
45
+ readonly start: number;
46
+ readonly end: number;
47
+ readonly value: number;
48
+ readonly unit: string | undefined;
49
+ readonly line: string;
50
+ };
51
+
52
+ const DIGIT = "[0-90-9]";
53
+ /** 整数(1,200 は千二百)。小数と、桁区切りの続きの途中からは読まない。 */
54
+ const NUMBER = new RegExp(`(?<!${DIGIT}|[.,,.])${DIGIT}+(?:[,,]${DIGIT}{3})*(?!${DIGIT}|[.,,.]${DIGIT})`, "gu");
55
+
56
+ /** 記号と空白を除いて、語と数の間に挟んでよいもの(定員:90名、定員は25名、capacity of 30、registrations: 45)。 */
57
+ const BEFORE_GAP_WORDS: ReadonlySet<string> = new Set(["", "は", "が", "of", "is", "was", "at"]);
58
+ const isBeforeGap = (gap: string): boolean => BEFORE_GAP_WORDS.has(lower(gap.replaceAll(/[\s::=]/gu, "")));
59
+ /** 数(と単位)と後ろの和語の間に挟んでよいもの(95名の方にお申し込み、28名のお申し込み)。 */
60
+ const AFTER_GAP_CJK = /^(?:の方|の皆様|の皆さま)?(?:[にがの]|から)?[おご]?$/u;
61
+ /** 数(と単位)と後ろの英語の間の語は三つまで(95 members have registered、28 children had already booked)。 */
62
+ const AFTER_GAP_LATIN = /^(?:\s+[A-Za-z]+){0,3}\s+$/u;
63
+ /** 英語の単位は数との間に空白を一つ置ける(90 seats)。 */
64
+ const LATIN_UNIT_GAP = /^\s?/u;
65
+ const WORD_NEXT = /^\s?[A-Za-z]/u;
66
+
67
+ const LETTER = /[A-Za-z]/u;
68
+
69
+ const lower = (text: string): string => text.toLowerCase();
70
+
71
+ const isLatin = (word: string): boolean => LETTER.test(word.charAt(0));
72
+
73
+ const numberOf = (digits: string): number => Number(digits.normalize("NFKC").replaceAll(/[,,]/gu, ""));
74
+
75
+ const startsWithWord = (text: string, word: string): boolean =>
76
+ lower(text).startsWith(lower(word)) && !(LETTER.test(word.slice(-1)) && LETTER.test(text.charAt(word.length)));
77
+
78
+ const endsWithWord = (text: string, word: string): boolean =>
79
+ lower(text).endsWith(lower(word)) && !(LETTER.test(word.charAt(0)) && LETTER.test(text.charAt(text.length - word.length - 1)));
80
+
81
+ /** 英字の語は語の切れ目で照らす(past が pastry に当たらない)。 */
82
+ const contains = (text: string, words: readonly string[]): boolean =>
83
+ words.some((word) =>
84
+ [...lower(text).matchAll(new RegExp(escapeRegExp(lower(word)), "gu"))].some(
85
+ (match) => startsWithWord(text.slice(match.index), word) && endsWithWord(text.slice(0, match.index + word.length), word),
86
+ ),
87
+ );
88
+
89
+ type Unit = { readonly mark: CountMark; readonly end: number };
90
+
91
+ /** 数のすぐ後ろの単位と、単位の終わりの位置。長い語から照らす。 */
92
+ const unitAfter = (line: string, end: number, units: readonly CountMark[]): Unit | undefined =>
93
+ units
94
+ .toSorted((left, right) => right.pattern.length - left.pattern.length)
95
+ .flatMap((mark) => {
96
+ const gap = isLatin(mark.pattern) ? (LATIN_UNIT_GAP.exec(line.slice(end))?.[0].length ?? 0) : 0;
97
+ return startsWithWord(line.slice(end + gap), mark.pattern) ? [{ mark, end: end + gap + mark.pattern.length }] : [];
98
+ })[0];
99
+
100
+ const labelBefore = (head: string, marks: readonly CountMark[]): CountMark | undefined =>
101
+ marks
102
+ .filter((mark) => mark.position !== "after")
103
+ .find((mark) => {
104
+ const at = lower(head).lastIndexOf(lower(mark.pattern));
105
+ const end = at + mark.pattern.length;
106
+ return at !== -1 && endsWithWord(head.slice(0, end), mark.pattern) && isBeforeGap(head.slice(end));
107
+ });
108
+
109
+ const labelAfter = (tail: string, marks: readonly CountMark[]): CountMark | undefined =>
110
+ marks
111
+ .filter((mark) => mark.position === "after")
112
+ .find((mark) => {
113
+ const at = lower(tail).indexOf(lower(mark.pattern));
114
+ if (at === -1 || !startsWithWord(tail.slice(at), mark.pattern)) return false;
115
+ return (isLatin(mark.pattern) ? AFTER_GAP_LATIN : AFTER_GAP_CJK).test(tail.slice(0, at));
116
+ });
117
+
118
+ /**
119
+ * 語の付いた数か。和語の語には単位が要る(定員90 は読まない)。英語の数は単位を書かなくてよいが、後ろに単位でない語が続けば
120
+ * 別の物の数(capacity 30 cars)。place の付いた語は席の数にだけ付く。
121
+ */
122
+ const isLabelled = (label: CountMark | undefined, unit: Unit | undefined, after: string, afterLabel: CountMark | undefined): label is CountMark => {
123
+ if (label === undefined || (label.group === "place" && unit?.mark.group !== "place")) return false;
124
+ if (unit !== undefined) return true;
125
+ return isLatin(label.pattern) && (afterLabel !== undefined || !WORD_NEXT.test(after));
126
+ };
127
+
128
+ /** 一行の中の、語の付いた数。 */
129
+ const labelledIn = (line: string, lineStart: number, marks: readonly CountMark[], units: readonly CountMark[]): LabelledCount[] =>
130
+ [...line.matchAll(NUMBER)].flatMap((match) => {
131
+ const unit = unitAfter(line, match.index + match[0].length, units);
132
+ const end = unit?.end ?? match.index + match[0].length;
133
+ const after = line.slice(end);
134
+ const afterLabel = labelAfter(after, marks);
135
+ if (!isLabelled(labelBefore(line.slice(0, match.index), marks) ?? afterLabel, unit, after, afterLabel)) return [];
136
+ return [{ start: lineStart + match.index, end: lineStart + end, value: numberOf(match[0]), unit: unit?.mark.group, line }];
137
+ });
138
+
139
+ const linesOf = (source: string): { readonly text: string; readonly start: number }[] => {
140
+ const starts = [0, ...[...source.matchAll(/\n/gu)].map((match) => match.index + 1)];
141
+ return starts.map((start, index) => ({ text: source.slice(start, (starts[index + 1] ?? source.length + 1) - 1), start }));
142
+ };
143
+
144
+ const readAll = (source: string, marks: readonly CountMark[], units: readonly CountMark[]): LabelledCount[] =>
145
+ linesOf(source).flatMap((line) => labelledIn(line.text, line.start, marks, units));
146
+
147
+ /** 席(place)は人(head)と同じ組として比べる。単位を書かない数は人の数。 */
148
+ export const comparableGroup = (unit: string | undefined): string => (unit === undefined || unit === "place" ? "head" : unit);
149
+
150
+ /** 文書の中の、定員の語の付いた数。一回あたりの定員は除く。 */
151
+ export const capacityStatements = (source: string, words: CapacityWords): LabelledCount[] =>
152
+ readAll(source, words.capacities, words.units).filter((reading) => !contains(reading.line, words.perSession));
153
+
154
+ /** 文書の中の、人数の語の付いた数。一回あたりの人数と、別の時の人数は除く。 */
155
+ export const countStatements = (source: string, words: CapacityWords): LabelledCount[] =>
156
+ readAll(source, words.counts, words.units).filter(
157
+ (reading) =>
158
+ !contains(reading.line, words.perSession) &&
159
+ !contains(reading.line, words.otherOccasions) &&
160
+ !contains(clauseAfter(source, reading.end), words.negations),
161
+ );
162
+
163
+ /** 定員を超える人数のうち一番多いもの。定員が一つの値でなければ、比べない。 */
164
+ export const overCapacity = (
165
+ capacities: readonly LabelledCount[],
166
+ counts: readonly LabelledCount[],
167
+ ): { readonly capacity: LabelledCount; readonly over: LabelledCount } | undefined => {
168
+ const capacity = capacities[0];
169
+ if (capacity === undefined || capacities.some((other) => other.value !== capacity.value)) return undefined;
170
+ const over = counts
171
+ .filter((count) => comparableGroup(count.unit) === comparableGroup(capacity.unit) && count.value > capacity.value)
172
+ .toSorted((left, right) => right.value - left.value)[0];
173
+ return over === undefined ? undefined : { capacity, over };
174
+ };
175
+
176
+ /** その位置を含む一番小さい節の原文。節が無ければ文書の全体。 */
177
+ export const sectionAround = (source: string, offset: number, sections: readonly Span[]): string => {
178
+ const around = sections
179
+ .filter((section) => section.start <= offset && offset < section.end)
180
+ .toSorted((left, right) => left.end - left.start - (right.end - right.start))[0];
181
+ return around === undefined ? source : source.slice(around.start, around.end);
182
+ };
183
+
184
+ /**
185
+ * 定員より多い人数。キャンセル待ちや抽選の語のある節の定員と人数は読まない(定員を超える申込が書いたとおりのこと)。
186
+ * 節に限るのは、別の節の「抽選会」(催しの一つ)で比べるのをやめないため。定員の位置を指す。sections は節の範囲。
187
+ */
188
+ export const countsOverCapacity = (source: string, words: CapacityWords, sections: readonly Span[] = []): StructureIssue[] => {
189
+ const settled = (reading: LabelledCount): boolean => !contains(sectionAround(source, reading.start, sections), words.overflows);
190
+ const capacities = capacityStatements(source, words);
191
+ if (!capacities.every(settled)) return [];
192
+ const found = overCapacity(capacities, countStatements(source, words).filter(settled));
193
+ if (found === undefined) return [];
194
+ const { capacity, over } = found;
195
+ return [{ offset: capacity.start, values: { capacity: source.slice(capacity.start, capacity.end), count: source.slice(over.start, over.end) } }];
196
+ };