chaffjs 0.35.0 → 0.36.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 (700) hide show
  1. package/README.ja.md +12 -0
  2. package/README.md +15 -0
  3. package/dist/announced-count.d.ts +2 -0
  4. package/dist/announced-count.d.ts.map +1 -1
  5. package/dist/announced-count.js +29 -7
  6. package/dist/announced-count.js.map +1 -1
  7. package/dist/chronology-rows.d.ts +9 -0
  8. package/dist/chronology-rows.d.ts.map +1 -0
  9. package/dist/chronology-rows.js +79 -0
  10. package/dist/chronology-rows.js.map +1 -0
  11. package/dist/company-name-run.d.ts +8 -0
  12. package/dist/company-name-run.d.ts.map +1 -0
  13. package/dist/company-name-run.js +30 -0
  14. package/dist/company-name-run.js.map +1 -0
  15. package/dist/compound-head.d.ts +7 -0
  16. package/dist/compound-head.d.ts.map +1 -0
  17. package/dist/compound-head.js +19 -0
  18. package/dist/compound-head.js.map +1 -0
  19. package/dist/date-format.d.ts +8 -3
  20. package/dist/date-format.d.ts.map +1 -1
  21. package/dist/date-format.js +36 -3
  22. package/dist/date-format.js.map +1 -1
  23. package/dist/derived/count-per-period.d.ts +23 -0
  24. package/dist/derived/count-per-period.d.ts.map +1 -0
  25. package/dist/derived/count-per-period.js +14 -0
  26. package/dist/derived/count-per-period.js.map +1 -0
  27. package/dist/detectors/acronym-context.d.ts +4 -0
  28. package/dist/detectors/acronym-context.d.ts.map +1 -1
  29. package/dist/detectors/acronym-context.js +29 -1
  30. package/dist/detectors/acronym-context.js.map +1 -1
  31. package/dist/detectors/acronym-expansion.d.ts +5 -1
  32. package/dist/detectors/acronym-expansion.d.ts.map +1 -1
  33. package/dist/detectors/acronym-expansion.js +35 -3
  34. package/dist/detectors/acronym-expansion.js.map +1 -1
  35. package/dist/detectors/acronym-known.d.ts +19 -0
  36. package/dist/detectors/acronym-known.d.ts.map +1 -0
  37. package/dist/detectors/acronym-known.js +32 -0
  38. package/dist/detectors/acronym-known.js.map +1 -0
  39. package/dist/detectors/acronym-link.d.ts +14 -0
  40. package/dist/detectors/acronym-link.d.ts.map +1 -0
  41. package/dist/detectors/acronym-link.js +32 -0
  42. package/dist/detectors/acronym-link.js.map +1 -0
  43. package/dist/detectors/acronym-name.d.ts +8 -1
  44. package/dist/detectors/acronym-name.d.ts.map +1 -1
  45. package/dist/detectors/acronym-name.js +23 -4
  46. package/dist/detectors/acronym-name.js.map +1 -1
  47. package/dist/detectors/addressee-edge.d.ts +13 -0
  48. package/dist/detectors/addressee-edge.d.ts.map +1 -0
  49. package/dist/detectors/addressee-edge.js +13 -0
  50. package/dist/detectors/addressee-edge.js.map +1 -0
  51. package/dist/detectors/announced-count.d.ts.map +1 -1
  52. package/dist/detectors/announced-count.js +1 -0
  53. package/dist/detectors/announced-count.js.map +1 -1
  54. package/dist/detectors/bare-comparative.d.ts +4 -0
  55. package/dist/detectors/bare-comparative.d.ts.map +1 -1
  56. package/dist/detectors/bare-comparative.js +28 -3
  57. package/dist/detectors/bare-comparative.js.map +1 -1
  58. package/dist/detectors/blank-structure.d.ts +7 -1
  59. package/dist/detectors/blank-structure.d.ts.map +1 -1
  60. package/dist/detectors/blank-structure.js +13 -8
  61. package/dist/detectors/blank-structure.js.map +1 -1
  62. package/dist/detectors/bracket-specified.d.ts +6 -0
  63. package/dist/detectors/bracket-specified.d.ts.map +1 -0
  64. package/dist/detectors/bracket-specified.js +23 -0
  65. package/dist/detectors/bracket-specified.js.map +1 -0
  66. package/dist/detectors/broken-link.d.ts +6 -0
  67. package/dist/detectors/broken-link.d.ts.map +1 -1
  68. package/dist/detectors/broken-link.js +23 -2
  69. package/dist/detectors/broken-link.js.map +1 -1
  70. package/dist/detectors/caps-caption.d.ts +7 -6
  71. package/dist/detectors/caps-caption.d.ts.map +1 -1
  72. package/dist/detectors/caps-caption.js +38 -0
  73. package/dist/detectors/caps-caption.js.map +1 -1
  74. package/dist/detectors/caps-phrase.d.ts +6 -0
  75. package/dist/detectors/caps-phrase.d.ts.map +1 -0
  76. package/dist/detectors/caps-phrase.js +16 -0
  77. package/dist/detectors/caps-phrase.js.map +1 -0
  78. package/dist/detectors/char-shape.d.ts +1 -0
  79. package/dist/detectors/char-shape.d.ts.map +1 -1
  80. package/dist/detectors/char-shape.js +29 -16
  81. package/dist/detectors/char-shape.js.map +1 -1
  82. package/dist/detectors/cited-title.d.ts +3 -2
  83. package/dist/detectors/cited-title.d.ts.map +1 -1
  84. package/dist/detectors/cited-title.js +14 -3
  85. package/dist/detectors/cited-title.js.map +1 -1
  86. package/dist/detectors/counter-edge.d.ts +2 -1
  87. package/dist/detectors/counter-edge.d.ts.map +1 -1
  88. package/dist/detectors/counter-edge.js +17 -2
  89. package/dist/detectors/counter-edge.js.map +1 -1
  90. package/dist/detectors/date-any-year.d.ts +7 -3
  91. package/dist/detectors/date-any-year.d.ts.map +1 -1
  92. package/dist/detectors/date-any-year.js +25 -5
  93. package/dist/detectors/date-any-year.js.map +1 -1
  94. package/dist/detectors/date-format.d.ts.map +1 -1
  95. package/dist/detectors/date-format.js +2 -1
  96. package/dist/detectors/date-format.js.map +1 -1
  97. package/dist/detectors/date-year.d.ts +9 -3
  98. package/dist/detectors/date-year.d.ts.map +1 -1
  99. package/dist/detectors/date-year.js +45 -9
  100. package/dist/detectors/date-year.js.map +1 -1
  101. package/dist/detectors/deadline-unit.d.ts +20 -0
  102. package/dist/detectors/deadline-unit.d.ts.map +1 -0
  103. package/dist/detectors/deadline-unit.js +19 -0
  104. package/dist/detectors/deadline-unit.js.map +1 -0
  105. package/dist/detectors/defined-term-form.d.ts.map +1 -1
  106. package/dist/detectors/defined-term-form.js +33 -7
  107. package/dist/detectors/defined-term-form.js.map +1 -1
  108. package/dist/detectors/definition-use.d.ts.map +1 -1
  109. package/dist/detectors/definition-use.js +6 -3
  110. package/dist/detectors/definition-use.js.map +1 -1
  111. package/dist/detectors/derived-numbers.d.ts.map +1 -1
  112. package/dist/detectors/derived-numbers.js +17 -4
  113. package/dist/detectors/derived-numbers.js.map +1 -1
  114. package/dist/detectors/doubled-across-aside.d.ts +9 -0
  115. package/dist/detectors/doubled-across-aside.d.ts.map +1 -0
  116. package/dist/detectors/doubled-across-aside.js +27 -0
  117. package/dist/detectors/doubled-across-aside.js.map +1 -0
  118. package/dist/detectors/doubled-punctuation.d.ts +3 -3
  119. package/dist/detectors/doubled-punctuation.d.ts.map +1 -1
  120. package/dist/detectors/doubled-punctuation.js +23 -4
  121. package/dist/detectors/doubled-punctuation.js.map +1 -1
  122. package/dist/detectors/doubled-word.d.ts.map +1 -1
  123. package/dist/detectors/doubled-word.js +2 -1
  124. package/dist/detectors/doubled-word.js.map +1 -1
  125. package/dist/detectors/ellipsis.d.ts.map +1 -1
  126. package/dist/detectors/ellipsis.js +13 -1
  127. package/dist/detectors/ellipsis.js.map +1 -1
  128. package/dist/detectors/email-letter.d.ts +31 -1
  129. package/dist/detectors/email-letter.d.ts.map +1 -1
  130. package/dist/detectors/email-letter.js +76 -14
  131. package/dist/detectors/email-letter.js.map +1 -1
  132. package/dist/detectors/field-label.d.ts +7 -0
  133. package/dist/detectors/field-label.d.ts.map +1 -0
  134. package/dist/detectors/field-label.js +23 -0
  135. package/dist/detectors/field-label.js.map +1 -0
  136. package/dist/detectors/footnote-mismatch.d.ts +6 -1
  137. package/dist/detectors/footnote-mismatch.d.ts.map +1 -1
  138. package/dist/detectors/footnote-mismatch.js +28 -7
  139. package/dist/detectors/footnote-mismatch.js.map +1 -1
  140. package/dist/detectors/graded-step.d.ts +20 -0
  141. package/dist/detectors/graded-step.d.ts.map +1 -0
  142. package/dist/detectors/graded-step.js +33 -0
  143. package/dist/detectors/graded-step.js.map +1 -0
  144. package/dist/detectors/gram-openers.d.ts +8 -0
  145. package/dist/detectors/gram-openers.d.ts.map +1 -0
  146. package/dist/detectors/gram-openers.js +16 -0
  147. package/dist/detectors/gram-openers.js.map +1 -0
  148. package/dist/detectors/gram-place.d.ts +3 -1
  149. package/dist/detectors/gram-place.d.ts.map +1 -1
  150. package/dist/detectors/gram-place.js +8 -0
  151. package/dist/detectors/gram-place.js.map +1 -1
  152. package/dist/detectors/gram-predicate.d.ts.map +1 -1
  153. package/dist/detectors/gram-predicate.js +37 -3
  154. package/dist/detectors/gram-predicate.js.map +1 -1
  155. package/dist/detectors/heading-echo.d.ts.map +1 -1
  156. package/dist/detectors/heading-echo.js +28 -2
  157. package/dist/detectors/heading-echo.js.map +1 -1
  158. package/dist/detectors/ime-residue.d.ts +1 -1
  159. package/dist/detectors/ime-residue.d.ts.map +1 -1
  160. package/dist/detectors/ime-residue.js +4 -3
  161. package/dist/detectors/ime-residue.js.map +1 -1
  162. package/dist/detectors/kanji-compound-slip.d.ts +3 -0
  163. package/dist/detectors/kanji-compound-slip.d.ts.map +1 -0
  164. package/dist/detectors/kanji-compound-slip.js +50 -0
  165. package/dist/detectors/kanji-compound-slip.js.map +1 -0
  166. package/dist/detectors/kutoten-consistency.d.ts +10 -2
  167. package/dist/detectors/kutoten-consistency.d.ts.map +1 -1
  168. package/dist/detectors/kutoten-consistency.js +76 -12
  169. package/dist/detectors/kutoten-consistency.js.map +1 -1
  170. package/dist/detectors/lexicon.d.ts.map +1 -1
  171. package/dist/detectors/lexicon.js +8 -2
  172. package/dist/detectors/lexicon.js.map +1 -1
  173. package/dist/detectors/link-shape.d.ts.map +1 -1
  174. package/dist/detectors/link-shape.js +9 -3
  175. package/dist/detectors/link-shape.js.map +1 -1
  176. package/dist/detectors/list-item-ending.d.ts +18 -0
  177. package/dist/detectors/list-item-ending.d.ts.map +1 -0
  178. package/dist/detectors/list-item-ending.js +35 -0
  179. package/dist/detectors/list-item-ending.js.map +1 -0
  180. package/dist/detectors/list-parallel.d.ts +18 -5
  181. package/dist/detectors/list-parallel.d.ts.map +1 -1
  182. package/dist/detectors/list-parallel.js +116 -36
  183. package/dist/detectors/list-parallel.js.map +1 -1
  184. package/dist/detectors/list-sentence.d.ts +4 -0
  185. package/dist/detectors/list-sentence.d.ts.map +1 -1
  186. package/dist/detectors/list-sentence.js +1 -1
  187. package/dist/detectors/list-sentence.js.map +1 -1
  188. package/dist/detectors/mark-shape.d.ts +11 -4
  189. package/dist/detectors/mark-shape.d.ts.map +1 -1
  190. package/dist/detectors/mark-shape.js +58 -10
  191. package/dist/detectors/mark-shape.js.map +1 -1
  192. package/dist/detectors/mentioned-phrase.d.ts +9 -0
  193. package/dist/detectors/mentioned-phrase.d.ts.map +1 -0
  194. package/dist/detectors/mentioned-phrase.js +25 -0
  195. package/dist/detectors/mentioned-phrase.js.map +1 -0
  196. package/dist/detectors/middle-dot.d.ts +2 -2
  197. package/dist/detectors/middle-dot.d.ts.map +1 -1
  198. package/dist/detectors/middle-dot.js +4 -2
  199. package/dist/detectors/middle-dot.js.map +1 -1
  200. package/dist/detectors/modal-conflict.d.ts.map +1 -1
  201. package/dist/detectors/modal-conflict.js +5 -2
  202. package/dist/detectors/modal-conflict.js.map +1 -1
  203. package/dist/detectors/notice-paragraph.d.ts +9 -0
  204. package/dist/detectors/notice-paragraph.d.ts.map +1 -0
  205. package/dist/detectors/notice-paragraph.js +21 -0
  206. package/dist/detectors/notice-paragraph.js.map +1 -0
  207. package/dist/detectors/number-style.d.ts.map +1 -1
  208. package/dist/detectors/number-style.js +15 -2
  209. package/dist/detectors/number-style.js.map +1 -1
  210. package/dist/detectors/number-unit.d.ts.map +1 -1
  211. package/dist/detectors/number-unit.js +24 -1
  212. package/dist/detectors/number-unit.js.map +1 -1
  213. package/dist/detectors/numeral-opener.d.ts +6 -0
  214. package/dist/detectors/numeral-opener.d.ts.map +1 -1
  215. package/dist/detectors/numeral-opener.js +24 -1
  216. package/dist/detectors/numeral-opener.js.map +1 -1
  217. package/dist/detectors/obligation-without-deadline.d.ts +5 -1
  218. package/dist/detectors/obligation-without-deadline.d.ts.map +1 -1
  219. package/dist/detectors/obligation-without-deadline.js +44 -23
  220. package/dist/detectors/obligation-without-deadline.js.map +1 -1
  221. package/dist/detectors/open-end-class.d.ts +16 -0
  222. package/dist/detectors/open-end-class.d.ts.map +1 -0
  223. package/dist/detectors/open-end-class.js +65 -0
  224. package/dist/detectors/open-end-class.js.map +1 -0
  225. package/dist/detectors/open-end-condition.d.ts +7 -0
  226. package/dist/detectors/open-end-condition.d.ts.map +1 -0
  227. package/dist/detectors/open-end-condition.js +35 -0
  228. package/dist/detectors/open-end-condition.js.map +1 -0
  229. package/dist/detectors/open-end-names.d.ts +9 -3
  230. package/dist/detectors/open-end-names.d.ts.map +1 -1
  231. package/dist/detectors/open-end-names.js +5 -2
  232. package/dist/detectors/open-end-names.js.map +1 -1
  233. package/dist/detectors/open-end-question.d.ts +15 -0
  234. package/dist/detectors/open-end-question.d.ts.map +1 -0
  235. package/dist/detectors/open-end-question.js +37 -0
  236. package/dist/detectors/open-end-question.js.map +1 -0
  237. package/dist/detectors/open-end-scope.d.ts +16 -0
  238. package/dist/detectors/open-end-scope.d.ts.map +1 -0
  239. package/dist/detectors/open-end-scope.js +47 -0
  240. package/dist/detectors/open-end-scope.js.map +1 -0
  241. package/dist/detectors/opening-date.d.ts +10 -0
  242. package/dist/detectors/opening-date.d.ts.map +1 -0
  243. package/dist/detectors/opening-date.js +33 -0
  244. package/dist/detectors/opening-date.js.map +1 -0
  245. package/dist/detectors/ordinary-phrase.d.ts +20 -0
  246. package/dist/detectors/ordinary-phrase.d.ts.map +1 -0
  247. package/dist/detectors/ordinary-phrase.js +54 -0
  248. package/dist/detectors/ordinary-phrase.js.map +1 -0
  249. package/dist/detectors/organisation-run.d.ts +5 -1
  250. package/dist/detectors/organisation-run.d.ts.map +1 -1
  251. package/dist/detectors/organisation-run.js +7 -0
  252. package/dist/detectors/organisation-run.js.map +1 -1
  253. package/dist/detectors/orthographic-variant.d.ts.map +1 -1
  254. package/dist/detectors/orthographic-variant.js +56 -20
  255. package/dist/detectors/orthographic-variant.js.map +1 -1
  256. package/dist/detectors/orthography.d.ts.map +1 -1
  257. package/dist/detectors/orthography.js +6 -1
  258. package/dist/detectors/orthography.js.map +1 -1
  259. package/dist/detectors/oxford-comma.d.ts.map +1 -1
  260. package/dist/detectors/oxford-comma.js +38 -1
  261. package/dist/detectors/oxford-comma.js.map +1 -1
  262. package/dist/detectors/person-consistency.d.ts +2 -1
  263. package/dist/detectors/person-consistency.d.ts.map +1 -1
  264. package/dist/detectors/person-consistency.js +5 -3
  265. package/dist/detectors/person-consistency.js.map +1 -1
  266. package/dist/detectors/phrase-match.d.ts +2 -0
  267. package/dist/detectors/phrase-match.d.ts.map +1 -1
  268. package/dist/detectors/phrase-match.js +11 -4
  269. package/dist/detectors/phrase-match.js.map +1 -1
  270. package/dist/detectors/placeholder-text.d.ts.map +1 -1
  271. package/dist/detectors/placeholder-text.js +18 -3
  272. package/dist/detectors/placeholder-text.js.map +1 -1
  273. package/dist/detectors/placeholder.d.ts +3 -1
  274. package/dist/detectors/placeholder.d.ts.map +1 -1
  275. package/dist/detectors/placeholder.js +8 -3
  276. package/dist/detectors/placeholder.js.map +1 -1
  277. package/dist/detectors/postcode-end.d.ts +3 -0
  278. package/dist/detectors/postcode-end.d.ts.map +1 -0
  279. package/dist/detectors/postcode-end.js +15 -0
  280. package/dist/detectors/postcode-end.js.map +1 -0
  281. package/dist/detectors/prose-total-words.d.ts +5 -0
  282. package/dist/detectors/prose-total-words.d.ts.map +1 -0
  283. package/dist/detectors/prose-total-words.js +14 -0
  284. package/dist/detectors/prose-total-words.js.map +1 -0
  285. package/dist/detectors/registry/kanji-compound-slip.d.ts +3 -0
  286. package/dist/detectors/registry/kanji-compound-slip.d.ts.map +1 -0
  287. package/dist/detectors/registry/kanji-compound-slip.js +3 -0
  288. package/dist/detectors/registry/kanji-compound-slip.js.map +1 -0
  289. package/dist/detectors/registry/phrase-in-use.d.ts +3 -0
  290. package/dist/detectors/registry/phrase-in-use.d.ts.map +1 -0
  291. package/dist/detectors/registry/phrase-in-use.js +3 -0
  292. package/dist/detectors/registry/phrase-in-use.js.map +1 -0
  293. package/dist/detectors/requirement-smell.d.ts.map +1 -1
  294. package/dist/detectors/requirement-smell.js +29 -5
  295. package/dist/detectors/requirement-smell.js.map +1 -1
  296. package/dist/detectors/scaled-figure-unit.d.ts +28 -0
  297. package/dist/detectors/scaled-figure-unit.d.ts.map +1 -0
  298. package/dist/detectors/scaled-figure-unit.js +78 -0
  299. package/dist/detectors/scaled-figure-unit.js.map +1 -0
  300. package/dist/detectors/sentence-counts.d.ts +2 -0
  301. package/dist/detectors/sentence-counts.d.ts.map +1 -1
  302. package/dist/detectors/sentence-counts.js +18 -7
  303. package/dist/detectors/sentence-counts.js.map +1 -1
  304. package/dist/detectors/sentence-ending.d.ts +2 -0
  305. package/dist/detectors/sentence-ending.d.ts.map +1 -1
  306. package/dist/detectors/sentence-ending.js +10 -3
  307. package/dist/detectors/sentence-ending.js.map +1 -1
  308. package/dist/detectors/sentence-load.d.ts +4 -1
  309. package/dist/detectors/sentence-load.d.ts.map +1 -1
  310. package/dist/detectors/sentence-load.js +9 -2
  311. package/dist/detectors/sentence-load.js.map +1 -1
  312. package/dist/detectors/signals.d.ts.map +1 -1
  313. package/dist/detectors/signals.js +30 -50
  314. package/dist/detectors/signals.js.map +1 -1
  315. package/dist/detectors/structure-tree.d.ts.map +1 -1
  316. package/dist/detectors/structure-tree.js +14 -13
  317. package/dist/detectors/structure-tree.js.map +1 -1
  318. package/dist/detectors/structure.d.ts.map +1 -1
  319. package/dist/detectors/structure.js +2 -1
  320. package/dist/detectors/structure.js.map +1 -1
  321. package/dist/detectors/subject-name-line.d.ts +8 -0
  322. package/dist/detectors/subject-name-line.d.ts.map +1 -0
  323. package/dist/detectors/subject-name-line.js +53 -0
  324. package/dist/detectors/subject-name-line.js.map +1 -0
  325. package/dist/detectors/table-header.d.ts.map +1 -1
  326. package/dist/detectors/table-header.js +13 -1
  327. package/dist/detectors/table-header.js.map +1 -1
  328. package/dist/detectors/technical-docs.d.ts.map +1 -1
  329. package/dist/detectors/technical-docs.js +4 -1
  330. package/dist/detectors/technical-docs.js.map +1 -1
  331. package/dist/detectors/term-mention-sentence.d.ts +4 -0
  332. package/dist/detectors/term-mention-sentence.d.ts.map +1 -0
  333. package/dist/detectors/term-mention-sentence.js +28 -0
  334. package/dist/detectors/term-mention-sentence.js.map +1 -0
  335. package/dist/detectors/titled-run.d.ts +2 -1
  336. package/dist/detectors/titled-run.d.ts.map +1 -1
  337. package/dist/detectors/titled-run.js +9 -4
  338. package/dist/detectors/titled-run.js.map +1 -1
  339. package/dist/detectors/token-shape.d.ts.map +1 -1
  340. package/dist/detectors/token-shape.js +13 -5
  341. package/dist/detectors/token-shape.js.map +1 -1
  342. package/dist/detectors/vague-density.d.ts +5 -2
  343. package/dist/detectors/vague-density.d.ts.map +1 -1
  344. package/dist/detectors/vague-density.js +17 -4
  345. package/dist/detectors/vague-density.js.map +1 -1
  346. package/dist/document-paragraphs.d.ts +4 -0
  347. package/dist/document-paragraphs.d.ts.map +1 -0
  348. package/dist/document-paragraphs.js +24 -0
  349. package/dist/document-paragraphs.js.map +1 -0
  350. package/dist/document.d.ts.map +1 -1
  351. package/dist/document.js +2 -19
  352. package/dist/document.js.map +1 -1
  353. package/dist/figure-references.d.ts +3 -0
  354. package/dist/figure-references.d.ts.map +1 -1
  355. package/dist/figure-references.js +6 -2
  356. package/dist/figure-references.js.map +1 -1
  357. package/dist/format-template.d.ts +3 -0
  358. package/dist/format-template.d.ts.map +1 -0
  359. package/dist/format-template.js +13 -0
  360. package/dist/format-template.js.map +1 -0
  361. package/dist/formula-brackets.d.ts +7 -0
  362. package/dist/formula-brackets.d.ts.map +1 -0
  363. package/dist/formula-brackets.js +29 -0
  364. package/dist/formula-brackets.js.map +1 -0
  365. package/dist/html/html-markdown.d.ts.map +1 -1
  366. package/dist/html/html-markdown.js +2 -1
  367. package/dist/html/html-markdown.js.map +1 -1
  368. package/dist/html/html-site-chrome.d.ts +5 -0
  369. package/dist/html/html-site-chrome.d.ts.map +1 -0
  370. package/dist/html/html-site-chrome.js +33 -0
  371. package/dist/html/html-site-chrome.js.map +1 -0
  372. package/dist/joined-view.d.ts.map +1 -1
  373. package/dist/joined-view.js +1 -0
  374. package/dist/joined-view.js.map +1 -1
  375. package/dist/kanji-compound-slip.d.ts +32 -0
  376. package/dist/kanji-compound-slip.d.ts.map +1 -0
  377. package/dist/kanji-compound-slip.js +63 -0
  378. package/dist/kanji-compound-slip.js.map +1 -0
  379. package/dist/latin-company-form.d.ts +6 -0
  380. package/dist/latin-company-form.d.ts.map +1 -0
  381. package/dist/latin-company-form.js +32 -0
  382. package/dist/latin-company-form.js.map +1 -0
  383. package/dist/latin-word-boundary.d.ts +15 -0
  384. package/dist/latin-word-boundary.d.ts.map +1 -0
  385. package/dist/latin-word-boundary.js +40 -0
  386. package/dist/latin-word-boundary.js.map +1 -0
  387. package/dist/lexicon-genres.d.ts +2 -0
  388. package/dist/lexicon-genres.d.ts.map +1 -1
  389. package/dist/lexicon-genres.js +1 -1
  390. package/dist/lexicon-genres.js.map +1 -1
  391. package/dist/link-shape.d.ts +5 -0
  392. package/dist/link-shape.d.ts.map +1 -1
  393. package/dist/link-shape.js +18 -0
  394. package/dist/link-shape.js.map +1 -1
  395. package/dist/link-title.d.ts +4 -0
  396. package/dist/link-title.d.ts.map +1 -0
  397. package/dist/link-title.js +33 -0
  398. package/dist/link-title.js.map +1 -0
  399. package/dist/list-item-paragraphs.d.ts +15 -0
  400. package/dist/list-item-paragraphs.d.ts.map +1 -0
  401. package/dist/list-item-paragraphs.js +19 -0
  402. package/dist/list-item-paragraphs.js.map +1 -0
  403. package/dist/literal-quote.d.ts +12 -0
  404. package/dist/literal-quote.d.ts.map +1 -0
  405. package/dist/literal-quote.js +43 -0
  406. package/dist/literal-quote.js.map +1 -0
  407. package/dist/nado-closed-list.d.ts.map +1 -1
  408. package/dist/nado-closed-list.js +39 -5
  409. package/dist/nado-closed-list.js.map +1 -1
  410. package/dist/name-separator.d.ts.map +1 -1
  411. package/dist/name-separator.js +25 -2
  412. package/dist/name-separator.js.map +1 -1
  413. package/dist/nominal-stem.d.ts +14 -1
  414. package/dist/nominal-stem.d.ts.map +1 -1
  415. package/dist/nominal-stem.js +42 -4
  416. package/dist/nominal-stem.js.map +1 -1
  417. package/dist/person-forms.d.ts +8 -0
  418. package/dist/person-forms.d.ts.map +1 -1
  419. package/dist/person-forms.js +12 -0
  420. package/dist/person-forms.js.map +1 -1
  421. package/dist/plugin.d.ts +5 -0
  422. package/dist/plugin.d.ts.map +1 -1
  423. package/dist/preamble-paragraphs.d.ts +3 -2
  424. package/dist/preamble-paragraphs.d.ts.map +1 -1
  425. package/dist/preamble-paragraphs.js +8 -2
  426. package/dist/preamble-paragraphs.js.map +1 -1
  427. package/dist/spacing-code.d.ts +1 -0
  428. package/dist/spacing-code.d.ts.map +1 -1
  429. package/dist/spacing-code.js +4 -1
  430. package/dist/spacing-code.js.map +1 -1
  431. package/dist/split-noun.d.ts +10 -0
  432. package/dist/split-noun.d.ts.map +1 -0
  433. package/dist/split-noun.js +50 -0
  434. package/dist/split-noun.js.map +1 -0
  435. package/dist/structure/attached-parts.d.ts +11 -0
  436. package/dist/structure/attached-parts.d.ts.map +1 -0
  437. package/dist/structure/attached-parts.js +31 -0
  438. package/dist/structure/attached-parts.js.map +1 -0
  439. package/dist/structure/build.d.ts.map +1 -1
  440. package/dist/structure/build.js +7 -4
  441. package/dist/structure/build.js.map +1 -1
  442. package/dist/structure/contents-lines.d.ts +7 -0
  443. package/dist/structure/contents-lines.d.ts.map +1 -0
  444. package/dist/structure/contents-lines.js +51 -0
  445. package/dist/structure/contents-lines.js.map +1 -0
  446. package/dist/structure/defining-names.d.ts +37 -0
  447. package/dist/structure/defining-names.d.ts.map +1 -0
  448. package/dist/structure/defining-names.js +122 -0
  449. package/dist/structure/defining-names.js.map +1 -0
  450. package/dist/structure/definition-use.d.ts +3 -2
  451. package/dist/structure/definition-use.d.ts.map +1 -1
  452. package/dist/structure/definition-use.js +99 -31
  453. package/dist/structure/definition-use.js.map +1 -1
  454. package/dist/structure/issues.d.ts +4 -1
  455. package/dist/structure/issues.d.ts.map +1 -1
  456. package/dist/structure/issues.js +5 -2
  457. package/dist/structure/issues.js.map +1 -1
  458. package/dist/structure/list-lead-in.d.ts +19 -0
  459. package/dist/structure/list-lead-in.d.ts.map +1 -0
  460. package/dist/structure/list-lead-in.js +85 -0
  461. package/dist/structure/list-lead-in.js.map +1 -0
  462. package/dist/structure/modal-conflict.d.ts +7 -2
  463. package/dist/structure/modal-conflict.d.ts.map +1 -1
  464. package/dist/structure/modal-conflict.js +36 -7
  465. package/dist/structure/modal-conflict.js.map +1 -1
  466. package/dist/structure/negated-act.d.ts +17 -0
  467. package/dist/structure/negated-act.d.ts.map +1 -0
  468. package/dist/structure/negated-act.js +32 -0
  469. package/dist/structure/negated-act.js.map +1 -0
  470. package/dist/structure/numbering.d.ts +3 -1
  471. package/dist/structure/numbering.d.ts.map +1 -1
  472. package/dist/structure/numbering.js +20 -8
  473. package/dist/structure/numbering.js.map +1 -1
  474. package/dist/structure/prose-total.d.ts +4 -0
  475. package/dist/structure/prose-total.d.ts.map +1 -1
  476. package/dist/structure/prose-total.js +16 -6
  477. package/dist/structure/prose-total.js.map +1 -1
  478. package/dist/structure/repeated-number.d.ts +24 -0
  479. package/dist/structure/repeated-number.d.ts.map +1 -0
  480. package/dist/structure/repeated-number.js +89 -0
  481. package/dist/structure/repeated-number.js.map +1 -0
  482. package/dist/structure/rounded-amount.d.ts +8 -0
  483. package/dist/structure/rounded-amount.d.ts.map +1 -0
  484. package/dist/structure/rounded-amount.js +48 -0
  485. package/dist/structure/rounded-amount.js.map +1 -0
  486. package/dist/structure/side-step.d.ts +8 -0
  487. package/dist/structure/side-step.d.ts.map +1 -0
  488. package/dist/structure/side-step.js +21 -0
  489. package/dist/structure/side-step.js.map +1 -0
  490. package/dist/structure/spanned-label-blank.d.ts +8 -0
  491. package/dist/structure/spanned-label-blank.d.ts.map +1 -0
  492. package/dist/structure/spanned-label-blank.js +37 -0
  493. package/dist/structure/spanned-label-blank.js.map +1 -0
  494. package/dist/structure/universal.d.ts +3 -1
  495. package/dist/structure/universal.d.ts.map +1 -1
  496. package/dist/structure/universal.js +9 -2
  497. package/dist/structure/universal.js.map +1 -1
  498. package/dist/structure/unspaced-heading.d.ts +10 -0
  499. package/dist/structure/unspaced-heading.d.ts.map +1 -0
  500. package/dist/structure/unspaced-heading.js +24 -0
  501. package/dist/structure/unspaced-heading.js.map +1 -0
  502. package/dist/structure/written-list-path.d.ts +10 -0
  503. package/dist/structure/written-list-path.d.ts.map +1 -0
  504. package/dist/structure/written-list-path.js +37 -0
  505. package/dist/structure/written-list-path.js.map +1 -0
  506. package/dist/structure/written-resolve.d.ts +1 -1
  507. package/dist/structure/written-resolve.d.ts.map +1 -1
  508. package/dist/structure/written-resolve.js +15 -9
  509. package/dist/structure/written-resolve.js.map +1 -1
  510. package/dist/title-line.d.ts +21 -0
  511. package/dist/title-line.d.ts.map +1 -0
  512. package/dist/title-line.js +27 -0
  513. package/dist/title-line.js.map +1 -0
  514. package/package.json +3 -3
  515. package/rules/all-caps-shouting.yaml +2 -2
  516. package/rules/announced-count-mismatch.yaml +2 -2
  517. package/rules/bare-url-mix.yaml +2 -2
  518. package/rules/bracket-nesting.yaml +2 -2
  519. package/rules/bracket-width-consistency.yaml +2 -2
  520. package/rules/broken-link.yaml +2 -2
  521. package/rules/cliche.yaml +4 -3
  522. package/rules/code-fence-language-mix.yaml +2 -2
  523. package/rules/comparative-without-baseline.yaml +2 -2
  524. package/rules/dangling-figure-reference.yaml +2 -2
  525. package/rules/date-without-year.yaml +10 -2
  526. package/rules/defined-term-form.yaml +2 -2
  527. package/rules/doubled-punctuation.yaml +4 -4
  528. package/rules/doubled-word.yaml +2 -2
  529. package/rules/duplicate-definition.yaml +2 -2
  530. package/rules/duration-mismatch.yaml +2 -2
  531. package/rules/email-greeting-closing.yaml +2 -2
  532. package/rules/email-subject-length.yaml +2 -2
  533. package/rules/empty-table-cell.yaml +2 -2
  534. package/rules/footnote-mismatch.yaml +2 -2
  535. package/rules/heading-echo.yaml +2 -2
  536. package/rules/ime-residue.yaml +2 -2
  537. package/rules/kanji-compound-slip.yaml +67 -0
  538. package/rules/kutoten-consistency.yaml +4 -4
  539. package/rules/latin-spacing.yaml +2 -2
  540. package/rules/list-item-form-mix.yaml +2 -2
  541. package/rules/max-kanji-continuous.yaml +6 -4
  542. package/rules/modal-conflict.yaml +1 -0
  543. package/rules/nado-closed-list.yaml +2 -2
  544. package/rules/ngram-repetition.yaml +2 -2
  545. package/rules/no-doubled-joshi.yaml +2 -2
  546. package/rules/no-mixed-desumasu.yaml +3 -3
  547. package/rules/no-nakaguro-parallel.yaml +2 -0
  548. package/rules/number-without-unit.yaml +9 -4
  549. package/rules/numbering-gap.yaml +7 -2
  550. package/rules/numeral-sentence-start.yaml +2 -2
  551. package/rules/obligation-without-deadline.yaml +2 -2
  552. package/rules/orthographic-variant.yaml +2 -2
  553. package/rules/oxford-comma-consistency.yaml +3 -3
  554. package/rules/preamble-length.yaml +2 -2
  555. package/rules/requirement-smell.yaml +3 -3
  556. package/rules/table-header-variant.yaml +2 -2
  557. package/rules/teki-overuse.yaml +3 -2
  558. package/rules/total-mismatch.yaml +2 -2
  559. package/rules/translationese-density.yaml +2 -2
  560. package/rules/undefined-acronym.yaml +4 -2
  561. package/rules/unfilled-placeholder.yaml +4 -4
  562. package/rules/use-before-definition.yaml +2 -2
  563. package/rules/vague-word-density.yaml +2 -2
  564. package/skills/chaff/SKILL.md +7 -1
  565. package/src/announced-count.ts +35 -8
  566. package/src/chronology-rows.ts +86 -0
  567. package/src/company-name-run.ts +37 -0
  568. package/src/compound-head.ts +25 -0
  569. package/src/date-format.ts +43 -4
  570. package/src/derived/count-per-period.ts +28 -0
  571. package/src/detectors/acronym-context.ts +30 -1
  572. package/src/detectors/acronym-expansion.ts +47 -6
  573. package/src/detectors/acronym-known.ts +40 -0
  574. package/src/detectors/acronym-link.ts +37 -0
  575. package/src/detectors/acronym-name.ts +27 -4
  576. package/src/detectors/addressee-edge.ts +16 -0
  577. package/src/detectors/announced-count.ts +1 -0
  578. package/src/detectors/bare-comparative.ts +44 -3
  579. package/src/detectors/blank-structure.ts +21 -10
  580. package/src/detectors/bracket-specified.ts +25 -0
  581. package/src/detectors/broken-link.ts +26 -2
  582. package/src/detectors/caps-caption.ts +42 -0
  583. package/src/detectors/caps-phrase.ts +17 -0
  584. package/src/detectors/char-shape.ts +30 -9
  585. package/src/detectors/cited-title.ts +15 -3
  586. package/src/detectors/counter-edge.ts +17 -2
  587. package/src/detectors/date-any-year.ts +27 -6
  588. package/src/detectors/date-format.ts +2 -1
  589. package/src/detectors/date-year.ts +64 -16
  590. package/src/detectors/deadline-unit.ts +40 -0
  591. package/src/detectors/defined-term-form.ts +32 -12
  592. package/src/detectors/definition-use.ts +7 -3
  593. package/src/detectors/derived-numbers.ts +19 -4
  594. package/src/detectors/doubled-across-aside.ts +36 -0
  595. package/src/detectors/doubled-punctuation.ts +28 -7
  596. package/src/detectors/doubled-word.ts +2 -1
  597. package/src/detectors/ellipsis.ts +16 -1
  598. package/src/detectors/email-letter.ts +97 -21
  599. package/src/detectors/field-label.ts +25 -0
  600. package/src/detectors/footnote-mismatch.ts +39 -7
  601. package/src/detectors/graded-step.ts +52 -0
  602. package/src/detectors/gram-openers.ts +21 -0
  603. package/src/detectors/gram-place.ts +10 -1
  604. package/src/detectors/gram-predicate.ts +43 -3
  605. package/src/detectors/heading-echo.ts +31 -3
  606. package/src/detectors/ime-residue.ts +4 -3
  607. package/src/detectors/kanji-compound-slip.ts +61 -0
  608. package/src/detectors/kutoten-consistency.ts +93 -14
  609. package/src/detectors/lexicon.ts +12 -3
  610. package/src/detectors/link-shape.ts +10 -3
  611. package/src/detectors/list-item-ending.ts +51 -0
  612. package/src/detectors/list-parallel.ts +158 -33
  613. package/src/detectors/list-sentence.ts +5 -1
  614. package/src/detectors/mark-shape.ts +64 -10
  615. package/src/detectors/mentioned-phrase.ts +38 -0
  616. package/src/detectors/middle-dot.ts +7 -2
  617. package/src/detectors/modal-conflict.ts +5 -2
  618. package/src/detectors/notice-paragraph.ts +33 -0
  619. package/src/detectors/number-style.ts +14 -2
  620. package/src/detectors/number-unit.ts +28 -1
  621. package/src/detectors/numeral-opener.ts +27 -1
  622. package/src/detectors/obligation-without-deadline.ts +29 -4
  623. package/src/detectors/open-end-class.ts +78 -0
  624. package/src/detectors/open-end-condition.ts +40 -0
  625. package/src/detectors/open-end-names.ts +9 -3
  626. package/src/detectors/open-end-question.ts +50 -0
  627. package/src/detectors/open-end-scope.ts +62 -0
  628. package/src/detectors/opening-date.ts +47 -0
  629. package/src/detectors/ordinary-phrase.ts +62 -0
  630. package/src/detectors/organisation-run.ts +13 -2
  631. package/src/detectors/orthographic-variant.ts +85 -25
  632. package/src/detectors/orthography.ts +10 -1
  633. package/src/detectors/oxford-comma.ts +40 -1
  634. package/src/detectors/person-consistency.ts +5 -3
  635. package/src/detectors/phrase-match.ts +16 -5
  636. package/src/detectors/placeholder-text.ts +18 -3
  637. package/src/detectors/placeholder.ts +10 -3
  638. package/src/detectors/postcode-end.ts +16 -0
  639. package/src/detectors/prose-total-words.ts +18 -0
  640. package/src/detectors/registry/kanji-compound-slip.ts +4 -0
  641. package/src/detectors/registry/phrase-in-use.ts +4 -0
  642. package/src/detectors/requirement-smell.ts +38 -5
  643. package/src/detectors/scaled-figure-unit.ts +103 -0
  644. package/src/detectors/sentence-counts.ts +24 -8
  645. package/src/detectors/sentence-ending.ts +12 -3
  646. package/src/detectors/sentence-load.ts +9 -2
  647. package/src/detectors/signals.ts +33 -51
  648. package/src/detectors/structure-tree.ts +14 -14
  649. package/src/detectors/structure.ts +2 -1
  650. package/src/detectors/subject-name-line.ts +66 -0
  651. package/src/detectors/table-header.ts +15 -1
  652. package/src/detectors/technical-docs.ts +5 -1
  653. package/src/detectors/term-mention-sentence.ts +31 -0
  654. package/src/detectors/titled-run.ts +9 -4
  655. package/src/detectors/token-shape.ts +14 -5
  656. package/src/detectors/vague-density.ts +20 -4
  657. package/src/document-paragraphs.ts +27 -0
  658. package/src/document.ts +3 -23
  659. package/src/figure-references.ts +7 -2
  660. package/src/format-template.ts +15 -0
  661. package/src/formula-brackets.ts +36 -0
  662. package/src/html/html-markdown.ts +2 -1
  663. package/src/html/html-site-chrome.ts +47 -0
  664. package/src/joined-view.ts +1 -0
  665. package/src/kanji-compound-slip.ts +98 -0
  666. package/src/latin-company-form.ts +38 -0
  667. package/src/latin-word-boundary.ts +46 -0
  668. package/src/lexicon-genres.ts +1 -1
  669. package/src/link-shape.ts +19 -0
  670. package/src/link-title.ts +38 -0
  671. package/src/list-item-paragraphs.ts +22 -0
  672. package/src/literal-quote.ts +55 -0
  673. package/src/nado-closed-list.ts +48 -5
  674. package/src/name-separator.ts +27 -2
  675. package/src/nominal-stem.ts +62 -5
  676. package/src/person-forms.ts +15 -0
  677. package/src/plugin.ts +5 -0
  678. package/src/preamble-paragraphs.ts +13 -3
  679. package/src/spacing-code.ts +5 -1
  680. package/src/split-noun.ts +63 -0
  681. package/src/structure/attached-parts.ts +32 -0
  682. package/src/structure/build.ts +7 -4
  683. package/src/structure/contents-lines.ts +63 -0
  684. package/src/structure/defining-names.ts +155 -0
  685. package/src/structure/definition-use.ts +116 -25
  686. package/src/structure/issues.ts +7 -3
  687. package/src/structure/list-lead-in.ts +94 -0
  688. package/src/structure/modal-conflict.ts +42 -7
  689. package/src/structure/negated-act.ts +49 -0
  690. package/src/structure/numbering.ts +24 -9
  691. package/src/structure/prose-total.ts +20 -6
  692. package/src/structure/repeated-number.ts +110 -0
  693. package/src/structure/rounded-amount.ts +55 -0
  694. package/src/structure/side-step.ts +21 -0
  695. package/src/structure/spanned-label-blank.ts +39 -0
  696. package/src/structure/universal.ts +11 -2
  697. package/src/structure/unspaced-heading.ts +27 -0
  698. package/src/structure/written-list-path.ts +46 -0
  699. package/src/structure/written-resolve.ts +16 -9
  700. package/src/title-line.ts +52 -0
@@ -0,0 +1,155 @@
1
+ import { escapeRegExp } from "../orthography.ts";
2
+
3
+ // 定義の括弧の前に書いた名前と、それを定義より前で写した所。純関数: 文(本文の一続き)と、その中の位置だけを見る。
4
+
5
+ /** 一つの文。start は文書の中の位置。 */
6
+ type Sentence = { readonly start: number; readonly text: string };
7
+
8
+ const OPENING_BRACKET = /[\s((]/u;
9
+
10
+ /** 定義の括弧を開けた字((、空白)を後ろから外した、括弧の前の名前。 */
11
+ export const nameBefore = (text: string): string => {
12
+ let end = text.length;
13
+ while (end > 0 && OPENING_BRACKET.test(text.charAt(end - 1))) end -= 1;
14
+ return text.slice(0, end);
15
+ };
16
+
17
+ /** 二つの文字列が後ろから何字重なるか。 */
18
+ export const sharedTail = (left: string, right: string): number => {
19
+ let shared = 0;
20
+ while (shared < left.length && shared < right.length && left.at(-1 - shared) === right.at(-1 - shared)) shared += 1;
21
+ return shared;
22
+ };
23
+
24
+ /** 漢字・仮名の名前の頭を決める字。名前はこの字の後ろから括弧の前まで(、株式会社ジェイアール東日本企画(以下「会社」))。 */
25
+ const NAME_BOUNDARY = /[\s、。,.,.;;::()()「」『』【】[][\]]/u;
26
+ const LATIN_NAME_END = /[\p{Script=Latin}\p{N}.]$/u;
27
+ const CAPITALISED = /^[\p{Lu}\p{N}&]/u;
28
+ /** 大文字の語のあいだにだけ入る、短い小文字の語(Board of Directors の of)。 */
29
+ const CONNECTOR = /^\p{Ll}{1,3}$/u;
30
+ const TOKEN = /\S+/gu;
31
+
32
+ /** 英語の名前: 括弧の前から遡った、大文字で始まる語の並び(Youth Sports Management, LLC、Board of Directors)。 */
33
+ const latinNameIn = (text: string): string => {
34
+ const tokens = [...text.matchAll(TOKEN)];
35
+ const kept = (index: number): boolean => {
36
+ const word = tokens[index]?.[0] ?? "";
37
+ const previous = tokens[index - 1]?.[0] ?? "";
38
+ return CAPITALISED.test(word) || (CONNECTOR.test(word) && CAPITALISED.test(previous) && !previous.endsWith(","));
39
+ };
40
+ const stop = tokens.findLastIndex((_, index) => !kept(index));
41
+ const first = tokens[stop + 1];
42
+ return first === undefined ? "" : text.slice(first.index);
43
+ };
44
+
45
+ /** 括弧の前の長い名前。英字で終われば大文字の語の並び、そうでなければ句読点・括弧の後ろから。 */
46
+ export const longNameIn = (text: string): string => {
47
+ if (LATIN_NAME_END.test(text)) return latinNameIn(text);
48
+ const chars = [...text];
49
+ return chars.slice(chars.findLastIndex((char) => NAME_BOUNDARY.test(char)) + 1).join("");
50
+ };
51
+
52
+ /** 定義の括弧の前の名前(name)、その前の文(before)、括弧の中の語より前(head: (this “Agreement”) の (this 。引用符は空白にする)。 */
53
+ export type DefiningName = { readonly name: string; readonly before: string; readonly head: string };
54
+
55
+ const LETTER_OR_SPACE = /[\p{L}\p{N}\s]/u;
56
+
57
+ /** 終わりの引用符と強調の印(“、「、***)を空白にしたもの。 */
58
+ const withoutOpeningQuote = (text: string): string => {
59
+ const chars = [...text];
60
+ return `${chars.slice(0, chars.findLastIndex((char) => LETTER_OR_SPACE.test(char)) + 1).join("")} `;
61
+ };
62
+ const ROUND_OPEN = new Set(["(", "("]);
63
+ const ROUND_CLOSE = new Set([")", ")"]);
64
+
65
+ /** text の終わりで開いたままの丸括弧の位置(…法律(令和6年法律第69号。以下「…」の()。無ければ undefined。 */
66
+ const openBracketAt = (text: string): number | undefined => {
67
+ const open: number[] = [];
68
+ text.split("").forEach((char, index) => {
69
+ if (ROUND_OPEN.has(char)) open.push(index);
70
+ else if (ROUND_CLOSE.has(char)) open.pop();
71
+ });
72
+ return open.at(-1);
73
+ };
74
+
75
+ /** 文の中の定義の範囲 [spanStart, spanEnd) から、定義の前の名前を取る。定義が括弧の中の注記の後ろにあれば、その括弧の前の名前。位置は文書の中の位置。 */
76
+ export const definingNameOf = (sentence: Sentence, spanStart: number, spanEnd: number, term: string): DefiningName => {
77
+ const beforeSpan = sentence.text.slice(0, spanStart - sentence.start);
78
+ const prefix = nameBefore(beforeSpan.slice(0, openBracketAt(beforeSpan) ?? beforeSpan.length));
79
+ const name = longNameIn(prefix);
80
+ const span = sentence.text.slice(spanStart - sentence.start, spanEnd - sentence.start);
81
+ const termAt = span.indexOf(term);
82
+ return { name, before: prefix.slice(0, prefix.length - name.length), head: termAt === -1 ? "" : withoutOpeningQuote(span.slice(0, termAt)) };
83
+ };
84
+
85
+ /** 長い名前の写しと読むのに、語のほかに名前と重ならなければならない字の数(手引き の前の「の」と「成」)。 */
86
+ export const MIN_ECHO = 2;
87
+ const LATIN_WORD_CHAR = /[\p{Script=Latin}\p{N}]/u;
88
+
89
+ /** 名前の from の位置が英語の語の途中なら、次の語の頭へ進める。limit を越えれば undefined。 */
90
+ const wordStartFrom = (name: string, from: number, limit: number): number | undefined => {
91
+ if (from === 0 || !LATIN_WORD_CHAR.test(name.charAt(from - 1)) || !LATIN_WORD_CHAR.test(name.charAt(from))) return from;
92
+ const space = name.slice(from).search(/\s/u);
93
+ return space === -1 || from + space + 1 > limit ? undefined : from + space + 1;
94
+ };
95
+
96
+ const indexesOf = (text: string, part: string): number[] => [...text.matchAll(new RegExp(escapeRegExp(part), "gu"))].map((match) => match.index);
97
+
98
+ /**
99
+ * 文の at にある語が、定義の名前 name(語を含む、語より長い名前)の写しの中にあるか(Board of Directors (the "Board") より前の
100
+ * Board of Directors、株式会社エムケイシステム(以下「会社」)より前の株式会社エムケイシステム)。写しは名前の終わりまで同じで、
101
+ * 頭は名前の語の切れ目から。
102
+ */
103
+ export const echoesName = (sentence: Sentence, at: number, term: string, name: string): boolean =>
104
+ name !== term &&
105
+ indexesOf(name, term).some((index) => {
106
+ if (!sentence.text.startsWith(name.slice(index + term.length), at + term.length)) return false;
107
+ const shared = Math.min(index, sharedTail(name.slice(0, index), sentence.text.slice(0, at)));
108
+ const start = wordStartFrom(name, index - shared, index);
109
+ return start !== undefined && name.length - start - term.length >= MIN_ECHO;
110
+ });
111
+
112
+ const LATIN_LETTER = /\p{Script=Latin}/u;
113
+
114
+ const SCRIPTS = [/\p{Script=Han}/u, /\p{Script=Hiragana}/u, /\p{Script=Katakana}/u];
115
+
116
+ /** 二つの字が同じ字の種類(漢字、平仮名、片仮名)か。基本規程の「本」は、前の「基」と一続きの語の一部。 */
117
+ const sameScript = (left: string, right: string): boolean => SCRIPTS.some((script) => script.test(left) && script.test(right));
118
+
119
+ /** 漢字・仮名の頭が、前の語に続かずに text の終わりにあるか(この規則、本規程。基本規程の本、そこの規則のこの は当たらない)。 */
120
+ const endsWithWordPrefix = (text: string, prefix: string): boolean =>
121
+ text.endsWith(prefix) && !sameScript(text.charAt(text.length - prefix.length - 1), prefix.charAt(0));
122
+
123
+ /** text の終わりが、文書自身を指す頭(この、本、this)か。英語の頭は語として(this の後ろに空白)、大文字小文字を問わない。 */
124
+ const endsWithSelfPrefix = (text: string, prefixes: readonly string[]): boolean =>
125
+ prefixes.some((prefix) =>
126
+ LATIN_LETTER.test(prefix) ? new RegExp(String.raw`(?<![\p{L}\p{N}])${escapeRegExp(prefix)}\s+$`, "iu").test(text) : endsWithWordPrefix(text, prefix),
127
+ );
128
+
129
+ /** 定義が文書自身の名前か(この就業規則(以下「規則」)、本規程(以下「規程」)、This Agreement … (this "Agreement"))。 */
130
+ export const namesItself = (defining: DefiningName, prefixes: readonly string[]): boolean =>
131
+ endsWithSelfPrefix(defining.head, prefixes) ||
132
+ endsWithSelfPrefix(defining.before, prefixes) ||
133
+ prefixes.some((prefix) => !LATIN_LETTER.test(prefix) && defining.name.startsWith(prefix));
134
+
135
+ /** 文の at の語の前が、文書自身を指す頭か(この規則、this Agreement)。 */
136
+ export const followsSelfPrefix = (sentence: Sentence, at: number, prefixes: readonly string[]): boolean =>
137
+ endsWithSelfPrefix(sentence.text.slice(0, at), prefixes);
138
+
139
+ /** 前の語を見るのに遡る字の数。 */
140
+ const LOOK_BEHIND_CHARS = 80;
141
+ /** 語の前が、文の頭ではない大文字の語か(Navistar Financial Corporation の Corporation)。The Company の The は文の頭なので当たらない。 */
142
+ const AFTER_CAPITALISED_WORD = /\p{L}[ \u00a0]\p{Lu}[\p{L}\p{N}&'’-]*[ \u00a0]$/u;
143
+
144
+ /** 英語の語が、前の大文字の語から続く長い名前の終わりにあるか。 */
145
+ export const endsLatinName = (sentence: Sentence, at: number, term: string): boolean =>
146
+ LATIN_LETTER.test(term.charAt(0)) && AFTER_CAPITALISED_WORD.test(sentence.text.slice(Math.max(0, at - LOOK_BEHIND_CHARS), at));
147
+
148
+ /** 語のすぐ後ろの括弧とその中身(こども性暴力防止法(…に関する法律)の(…に関する法律))。 */
149
+ const GLOSS_AFTER = /^\s*[((]([^))]*)[))]/u;
150
+
151
+ /** 文の at の語が、すぐ後ろの括弧に定義の長い名前を書き添えているか。語の意味はその場で分かる。 */
152
+ export const glossedWithName = (sentence: Sentence, at: number, term: string, name: string): boolean => {
153
+ const gloss = GLOSS_AFTER.exec(sentence.text.slice(at + term.length))?.[1];
154
+ return gloss !== undefined && name.length >= term.length + MIN_ECHO && gloss.trim().endsWith(name);
155
+ };
@@ -1,6 +1,19 @@
1
1
  import type { Span, StructureNode } from "../plugin.ts";
2
2
  import { escapeRegExp } from "../orthography.ts";
3
3
  import { inDocumentOrder } from "./issues.ts";
4
+ import { isTitleWords } from "./contents-lines.ts";
5
+ import {
6
+ definingNameOf,
7
+ echoesName,
8
+ endsLatinName,
9
+ followsSelfPrefix,
10
+ glossedWithName,
11
+ MIN_ECHO,
12
+ nameBefore,
13
+ namesItself,
14
+ sharedTail,
15
+ type DefiningName,
16
+ } from "./defining-names.ts";
4
17
 
5
18
  // 定義した語が本文で使われているか、定義より前に使われていないか。どちらも木の定義と、本文の文の字面だけで決まる。
6
19
 
@@ -135,6 +148,24 @@ const LABEL_SLACK = 3;
135
148
  const isLabel = (body: BodyText | undefined, term: string): boolean =>
136
149
  body !== undefined && (body.text.replaceAll(term, "").match(LETTER) ?? []).length <= LABEL_SLACK;
137
150
 
151
+ /** 番地の後ろの、条の題の始まり(Section 1.1 Closing. の Closing)。 */
152
+ const AFTER_ADDRESS = /\d\.?\s+(?=\p{Lu})/gu;
153
+ /** 文の終わりまでが、文の終わりの印を一つしか持たない(題の終わりの点)。 */
154
+ const CAPTION_TO_END = /^[^.!?。]*[.。]?$/u;
155
+
156
+ /**
157
+ * 条の題の中か。番地の後ろに題を置いて文がその題で終わる所(Section 1.1 Closing.)か、文が題だけのもの(Section 6.06. の後ろの
158
+ * Financing Covenant.、Company Stockholder Meeting; Company Proxy Statement.)。
159
+ */
160
+ const insideCaption = (body: BodyText | undefined, offset: number): boolean => {
161
+ if (body === undefined) return false;
162
+ const at = offset - body.start;
163
+ const starts = [...body.text.matchAll(AFTER_ADDRESS)].filter((start) => start.index + start[0].length <= at);
164
+ const last = starts.at(-1);
165
+ const caption = last === undefined ? body.text : body.text.slice(last.index + last[0].length);
166
+ return CAPTION_TO_END.test(caption.trimEnd()) && isTitleWords(caption);
167
+ };
168
+
138
169
  /** 文書の位置が、題・見出し・条の見出しの行にあるか。見出しは節の中身に名前を付けるだけで、語を使った所ではない。 */
139
170
  export type IsTitle = (offset: number) => boolean;
140
171
 
@@ -169,24 +200,6 @@ const insideKatakanaName = (texts: readonly BodyText[], term: string, offset: nu
169
200
  return (KATAKANA.test(term.charAt(0)) && joinedBefore) || (KATAKANA.test(term.at(-1) ?? "") && joinedAfter);
170
201
  };
171
202
 
172
- /** 長い名前の写しと読むのに、語の前で定義の名前と重ならなければならない字の数(手引き の前の「の」と「成」)。 */
173
- const MIN_ECHO = 2;
174
- const OPENING_BRACKET = /[\s((]/u;
175
-
176
- /** 定義の括弧を開けた字((、空白)を後ろから外した、括弧の前の名前。 */
177
- const nameBefore = (text: string): string => {
178
- let end = text.length;
179
- while (end > 0 && OPENING_BRACKET.test(text.charAt(end - 1))) end -= 1;
180
- return text.slice(0, end);
181
- };
182
-
183
- /** 二つの文字列が後ろから何字重なるか。 */
184
- const sharedTail = (left: string, right: string): number => {
185
- let shared = 0;
186
- while (shared < left.length && shared < right.length && left.at(-1 - shared) === right.at(-1 - shared)) shared += 1;
187
- return shared;
188
- };
189
-
190
203
  /**
191
204
  * 定義の括弧の前に書いた長い名前(…ガイドライン作成の手引き(以下、「手引き」という。))を、定義より前でそのまま書いた所。
192
205
  * 名前が語で終わり、語の前の字も名前と同じなら、それは長い名前で、定義した短い名前の使用ではない。
@@ -229,10 +242,81 @@ const CAPITAL_OR_NON_LATIN = /\p{Lu}|[^\p{Script=Latin}\p{N}\s\p{P}]/u;
229
242
  */
230
243
  const isDistinctive = (term: string): boolean => CAPITAL_OR_NON_LATIN.test(term) && !SINGLE_KANJI.test(term);
231
244
 
245
+ /** 文の途中の定義ごとの、括弧の前の名前。 */
246
+ const definingNames = (terms: readonly DefinedTerm[], texts: readonly BodyText[]): Map<DefinedTerm, DefiningName> =>
247
+ new Map(
248
+ terms.flatMap((term): [DefinedTerm, DefiningName][] => {
249
+ const sentence = term.inline ? sentenceAt(texts, term.span.start) : undefined;
250
+ return sentence === undefined ? [] : [[term, definingNameOf(sentence, term.span.start, term.span.end, term.term)]];
251
+ }),
252
+ );
253
+
254
+ /**
255
+ * 語を含む、もっと長い名前の中の現れか。別の語の定義の中((the "Certificate of Merger") の Certificate)、もっと長い定義した語
256
+ * (NFLN Agreement の NFLN)、定義の括弧の前の名前の写し(Board of Directors)、大文字の語に続く英語の名前の終わり。
257
+ */
258
+ type InsideLongerName = (term: string, offset: number) => boolean;
259
+
260
+ /** 位置がどれかの範囲の中か。範囲を頭の順に並べ、二分探索で引く(定義が何千あっても、現れ一つに対数)。 */
261
+ const insideAnyOf = (spans: readonly Span[]): ((offset: number) => boolean) => {
262
+ const sorted = spans.toSorted((left, right) => left.start - right.start);
263
+ const furthestEnd: number[] = [];
264
+ sorted.forEach((span, index) => furthestEnd.push(Math.max(furthestEnd[index - 1] ?? 0, span.end)));
265
+ return (offset) => {
266
+ let low = 0;
267
+ let high = sorted.length - 1;
268
+ while (low <= high) {
269
+ const middle = (low + high) >> 1;
270
+ if ((sorted[middle]?.start ?? 0) <= offset) low = middle + 1;
271
+ else high = middle - 1;
272
+ }
273
+ if (high < 0 || (furthestEnd[high] ?? 0) <= offset) return false;
274
+ return offset < (sorted[high]?.end ?? 0) || inside(offset, sorted.slice(0, high));
275
+ };
276
+ };
277
+
278
+ const insideLongerNameIn = (texts: readonly BodyText[], terms: readonly DefinedTerm[], names: ReadonlyMap<DefinedTerm, DefiningName>): InsideLongerName => {
279
+ const insideDefinition = insideAnyOf(terms.map((term) => term.span));
280
+ const longNames = [...new Set([...[...names.values()].map((defining) => defining.name), ...terms.map((term) => term.term)])];
281
+ const holding = new Map<string, readonly string[]>();
282
+ const namesHolding = (term: string): readonly string[] => {
283
+ const known = holding.get(term);
284
+ if (known !== undefined) return known;
285
+ const found = longNames.filter((name) => name !== term && name.includes(term));
286
+ holding.set(term, found);
287
+ return found;
288
+ };
289
+ return (term, offset) => {
290
+ if (insideDefinition(offset)) return true;
291
+ const sentence = sentenceAt(texts, offset);
292
+ if (sentence === undefined) return false;
293
+ const at = offset - sentence.start;
294
+ return endsLatinName(sentence, at, term) || namesHolding(term).some((name) => echoesName(sentence, at, term, name));
295
+ };
296
+ };
297
+
298
+ /**
299
+ * 前で書いても読み手が分かる所。文書自身の名前と定義した語(この就業規則(以下「規則」))を同じ頭を付けて書いた所(この規則は、…)と、
300
+ * すぐ後ろの括弧に定義の長い名前を書き添えた所(こども性暴力防止法(…に関する法律))。
301
+ */
302
+ const explainedInPlace = (
303
+ texts: readonly BodyText[],
304
+ term: string,
305
+ defining: DefiningName | undefined,
306
+ prefixes: readonly string[],
307
+ offset: number,
308
+ ): boolean => {
309
+ const sentence = sentenceAt(texts, offset);
310
+ if (defining === undefined || sentence === undefined) return false;
311
+ const at = offset - sentence.start;
312
+ return (namesItself(defining, prefixes) && followsSelfPrefix(sentence, at, prefixes)) || glossedWithName(sentence, at, term, defining.name);
313
+ };
314
+
232
315
  /**
233
316
  * 文の途中で括弧に入れて定義した語((the "Seller")、以下「甲」という)を、その定義の文より前で使っている所。最初の一つだけ。
234
317
  * 定義の文の中の現れ(株式会社GovTech東京(以下「GovTech東京」という。))は、定義する名前そのものなので数えない。札の文も、
235
- * isTitle が言う題・見出しの行も、漢字で終わる語にさらに漢字が続く長い語の一部も、定義の括弧の前の長い名前を写した所も数えない。
318
+ * isTitle が言う題・見出し・目次の行も、漢字で終わる語にさらに漢字が続く長い語の一部も、定義の括弧の前の長い名前を写した所も、
319
+ * 語を含むもっと長い名前の中も数えない。文書自身の名前を定義した語は、selfPrefixes(この、本、this)を付けて前で書いても数えない。
236
320
  * 定義の条("Seller" means、「甲」とは)は条の並びの頭にも終わりにも置く書き方があるので、前で使っても言わない。
237
321
  * 定義の語の無い括弧の引用(("The key point is"))は例の引用と同じ形なので、その語を引用符で挙げただけの現れ
238
322
  * (Its sentences open with "The key point is")は使用ではない。定義の語のある定義((the "Seller")、以下「買主」という)は、
@@ -243,21 +327,28 @@ export const usesBeforeDefinition = (
243
327
  texts: readonly BodyText[],
244
328
  mentioned: Mentioned = NEVER_MENTIONED,
245
329
  isTitle: IsTitle = NEVER_TITLE,
246
- ): TermUse[] =>
247
- groupsOf(terms)
330
+ selfPrefixes: readonly string[] = [],
331
+ ): TermUse[] => {
332
+ const inline = terms.filter((term) => term.inline);
333
+ const names = definingNames(terms, texts);
334
+ const insideLongerName = insideLongerNameIn(texts, terms, names);
335
+ return groupsOf(terms)
248
336
  .filter((group) => group.first.inline && isDistinctive(group.first.term))
249
337
  .flatMap(({ first: defined, spans }) => {
250
- const inline = terms.filter((term) => term.inline);
251
338
  const defining = sentenceAt(texts, defined.span.start)?.start ?? defined.span.start;
252
- const uses = usesOf(defined.term, texts, spans, "exact", defined.bare === true ? mentioned : NEVER_MENTIONED);
339
+ const uses = usesOf(defined.term, texts, spans, "exact", defined.bare === true ? mentioned : NEVER_MENTIONED).filter((offset) => offset < defining);
253
340
  const first = uses.find(
254
341
  (offset) =>
255
342
  !isLabel(sentenceAt(texts, offset), defined.term) &&
343
+ !insideCaption(sentenceAt(texts, offset), offset) &&
256
344
  !isTitle(offset) &&
257
345
  !continuesInHan(texts, defined.term, offset) &&
258
346
  !insideKatakanaName(texts, defined.term, offset) &&
259
347
  !echoesLongName(texts, defined, offset) &&
260
- !insideDefiningName(texts, inline, defined, offset),
348
+ !insideDefiningName(texts, inline, defined, offset) &&
349
+ !insideLongerName(defined.term, offset) &&
350
+ !explainedInPlace(texts, defined.term, names.get(defined), selfPrefixes, offset),
261
351
  );
262
- return first !== undefined && first < defining ? [{ term: defined, offset: first }] : [];
352
+ return first === undefined ? [] : [{ term: defined, offset: first }];
263
353
  });
354
+ };
@@ -1,5 +1,6 @@
1
1
  import type { StructureNode } from "../plugin.ts";
2
2
  import { writtenReading } from "./written-resolve.ts";
3
+ import { partAt } from "./attached-parts.ts";
3
4
  import { namedTargets, pointsToNamedSection } from "./section-names.ts";
4
5
  import { listedTags } from "./listed-tags.ts";
5
6
  import { namesAbsentUnit } from "./unit-word.ts";
@@ -9,7 +10,8 @@ import { referencesInCitedDocuments } from "./cited-document-section.ts";
9
10
  // 木だけで決まる誤り。参照先が無い、番号が飛ぶ・重なる、同じ語を二度定義する。
10
11
  // 意味の食い違いは判定しない。書いてあることから機械で決まるものだけを出す。
11
12
 
12
- export type StructureIssue = { readonly offset: number; readonly values: Readonly<Record<string, string | number>> };
13
+ /** variant は、rule の messages から文を選ぶ鍵。無ければ message。 */
14
+ export type StructureIssue = { readonly offset: number; readonly values: Readonly<Record<string, string | number>>; readonly variant?: string };
13
15
 
14
16
  const NUMBERED: ReadonlySet<string> = new Set(["chapter", "article", "item"]);
15
17
 
@@ -195,13 +197,15 @@ const definitionsInOrder = (tree: StructureNode): Definition[] => {
195
197
  /**
196
198
  * 同じ語の二度目以降の定義。どちらが正しいかは決めず、両方の場所を示す。
197
199
  * 範囲を限った定義(この条において「X」とは)は、同じ条の中でだけ比べる。別の条で定義し直すのは正しい書き方。
200
+ * 添付した文書(partStarts から始まる Exhibit A など)は別の文書なので、その中の定義はその中でだけ比べる。
198
201
  */
199
- export const duplicateDefinitions = (tree: StructureNode): StructureIssue[] => {
202
+ export const duplicateDefinitions = (tree: StructureNode, partStarts: readonly number[] = []): StructureIssue[] => {
200
203
  const first = new Map<string, StructureNode>();
201
204
  return definitionsInOrder(tree).flatMap(({ node, article }) => {
202
205
  const term = textOf(node, "term");
203
206
  const within = node.attrs["within"] === undefined ? article : textOf(node, "within");
204
- const key = node.attrs["scope"] === "local" ? `${within}\u0000${term}` : term;
207
+ const scoped = node.attrs["scope"] === "local" ? `${within}\u0000${term}` : term;
208
+ const key = `${partAt(partStarts, node.span.start)}\u0000${scoped}`;
205
209
  const earlier = first.get(key);
206
210
  if (earlier === undefined) {
207
211
  first.set(key, node);
@@ -0,0 +1,94 @@
1
+ import type { Sentence, Span } from "../plugin.ts";
2
+
3
+ // 箇条書きの項目の文が、どの導入の文の下にあるか。「(3) If … no such resolution is made—\n(a) the Commissioner must issue …」の
4
+ // (a) は (3) の条件の下にあり、(a) だけを読むと条件が落ちる。
5
+
6
+ /** 導入の文の終わり。この後に項目が並ぶ("must include—"、"is made:"、「承認した場合:」)。 */
7
+ const LEAD_IN_END = /[::—–]\s*$/u;
8
+
9
+ /** 空行。そこで段落が改まり、導入はそこを越えて探さない。 */
10
+ const BLANK_LINE = /\n[^\S\n]*\n/u;
11
+
12
+ /** 項目の頭の印: 開き括弧、番号か記号(囲み数字や仮名は 1 字)、閉じ括弧か点。 */
13
+ const MARKER = /^\s*([(([[]?)([0-90-9]+|[a-zA-Za-zA-Z]+|[一二三四五六七八九十]+|\S)([))]\]..]?)/u;
14
+
15
+ /** 1 字のローマ数字。英字の並び((h) の次の (i))でもありうる。 */
16
+ const SINGLE_ROMAN = /^[ivxIVX]$/u;
17
+ const ROMAN = /^(?:[ivxlc]{2,}|[IVXLC]{2,})$/u;
18
+
19
+ const labelClass = (label: string): string => {
20
+ if (/^[0-90-9]+$/u.test(label)) return "1";
21
+ if (ROMAN.test(label) || SINGLE_ROMAN.test(label)) return /[a-z]/u.test(label) ? "i" : "I";
22
+ if (/^[a-za-z]+$/u.test(label)) return "a";
23
+ if (/^[A-ZA-Z]+$/u.test(label)) return "A";
24
+ if (/^[一二三四五六七八九十]+$/u.test(label)) return "一";
25
+ if (/^[\p{Script=Katakana}\p{Script=Hiragana}]$/u.test(label)) return "イ";
26
+ if (/^[①-⑳㉑-㉟㊱-㊿]$/u.test(label)) return "①";
27
+ if (/^[⑴-⒇]$/u.test(label)) return "⑴";
28
+ return label;
29
+ };
30
+
31
+ /** 項目の文の頭の印の範囲("(a)"、"①")。番号は文の中身ではない。項目の文でなければ undefined。 */
32
+ export const itemMarkerSpan = (sentence: Sentence): Span | undefined => {
33
+ const match = sentence.listItem === true ? MARKER.exec(sentence.text) : null;
34
+ return match === null ? undefined : { start: sentence.span.start, end: sentence.span.start + match[0].length };
35
+ };
36
+
37
+ const previousLetter = (label: string): string => String.fromCharCode(label.charCodeAt(0) - 1);
38
+
39
+ /**
40
+ * 項目の印の形("(1)"、"(a)"、"(i)"、"①"、"・")。番号そのものは数えない。
41
+ * 1 字の i・v・x は、同じ括弧の英字の項目がその前の字(h・u・w)で終わっていれば英字、でなければローマ数字。
42
+ */
43
+ export const markerShape = (text: string, lastLetters: ReadonlyMap<string, string>): { readonly shape: string; readonly letter?: string } | undefined => {
44
+ const match = MARKER.exec(text);
45
+ const [, open = "", label, close = ""] = match ?? [];
46
+ if (label === undefined) return undefined;
47
+ const letterShape = `${open}${/[a-z]/u.test(label) ? "a" : "A"}${close}`;
48
+ const continuesLetters = SINGLE_ROMAN.test(label) && lastLetters.get(letterShape) === previousLetter(label);
49
+ const shape = continuesLetters ? letterShape : `${open}${labelClass(label)}${close}`;
50
+ return shape === letterShape ? { shape, letter: label } : { shape };
51
+ };
52
+
53
+ type Entry = { readonly sentence: Sentence; readonly leadIns: readonly Sentence[] };
54
+
55
+ /** 段落の中で、項目の形ごとの最後の項目と、すぐ前の文が導入ならその文。 */
56
+ type Block = {
57
+ readonly lastByShape: Map<string, { readonly at: number; readonly entry: Entry }>;
58
+ readonly lastLetters: Map<string, string>;
59
+ leadIn: { readonly at: number; readonly entry: Entry } | undefined;
60
+ };
61
+
62
+ const newBlock = (): Block => ({ lastByShape: new Map(), lastLetters: new Map(), leadIn: undefined });
63
+
64
+ /** 項目の導入の文: すぐ前の文が導入で、それより後に同じ形の項目(兄弟)が無ければその文とその導入、でなければ兄弟の導入。 */
65
+ const leadInsOf = (block: Block, shape: string): readonly Sentence[] => {
66
+ const sibling = block.lastByShape.get(shape);
67
+ const leadIn = block.leadIn;
68
+ if (sibling !== undefined && (leadIn === undefined || sibling.at >= leadIn.at)) return sibling.entry.leadIns;
69
+ return leadIn === undefined ? [] : [leadIn.entry.sentence, ...leadIn.entry.leadIns];
70
+ };
71
+
72
+ /**
73
+ * 箇条書きの項目の文(listItem)ごとの、導入の文。近い順で、導入が項目なら、その導入も続ける。
74
+ * 導入は、並びの最初の項目のすぐ前の文で、「—」「:」「:」で終わるもの。同じ段落(空行を挟まない)の中で、同じ形の項目どうしは
75
+ * 同じ導入の下にある。
76
+ * sentences は文書の順。source は sentences の span の座標の文字列。
77
+ */
78
+ export const listLeadIns = (source: string, sentences: readonly Sentence[]): Map<Sentence, readonly Sentence[]> => {
79
+ const found = new Map<Sentence, readonly Sentence[]>();
80
+ let block = newBlock();
81
+ sentences.forEach((sentence, at) => {
82
+ const previous = sentences[at - 1];
83
+ if (previous !== undefined && BLANK_LINE.test(source.slice(previous.span.end, sentence.span.start))) block = newBlock();
84
+ const marker = sentence.listItem === true ? markerShape(sentence.text, block.lastLetters) : undefined;
85
+ const entry: Entry = { sentence, leadIns: marker === undefined ? [] : leadInsOf(block, marker.shape) };
86
+ if (marker !== undefined) {
87
+ found.set(sentence, entry.leadIns);
88
+ block.lastByShape.set(marker.shape, { at, entry });
89
+ if (marker.letter !== undefined) block.lastLetters.set(marker.shape, marker.letter.at(-1) ?? "");
90
+ }
91
+ block.leadIn = LEAD_IN_END.test(sentence.text) ? { at, entry } : undefined;
92
+ });
93
+ return found;
94
+ };
@@ -1,4 +1,6 @@
1
+ import { isMarkedNegation, negatedWords } from "./negated-act.ts";
1
2
  import type { Sentence, Span, Token } from "../plugin.ts";
3
+ import { itemMarkerSpan } from "./list-lead-in.ts";
2
4
 
3
5
  // 同じ主語と行為に、逆の様相(しなければならない と してはならない、may と must not)を書いた二つの文。
4
6
  // 主語と行為は、文の中身の語(名詞・固有名詞・動詞・形容詞)の集まりで近似する。様相の語そのものは数えない。
@@ -24,13 +26,21 @@ const OPPOSITE: Readonly<Record<string, readonly string[]>> = {
24
26
 
25
27
  const overlaps = (token: Token, spans: readonly Span[]): boolean => spans.some((span) => token.span.start < span.end && span.start < token.span.end);
26
28
 
27
- /** 文の中身の語を、原形の小文字で、重ねずに並べたもの。様相の語の範囲にかかる語は外す。 */
29
+ /**
30
+ * 文の中身の語を、原形の小文字で、重ねずに並べたもの。様相の語の範囲にかかる語は外す。
31
+ * 打ち消しの印の付いた機能語(negationsMarked)は、近くの中身の語に印を付ける(negatedWords)。様相の語の中の打ち消し(must not)は数えない。
32
+ */
28
33
  export const contentKey = (tokens: readonly Token[], markers: readonly Span[]): string =>
29
34
  [
30
35
  ...new Set(
31
- tokens
32
- .filter((token) => CONTENT.has(token.pos) && !overlaps(token, markers))
33
- .map((token) => (token.lemma !== undefined && token.lemma !== "" ? token.lemma : token.surface).toLowerCase()),
36
+ negatedWords(
37
+ tokens
38
+ .filter((token) => (CONTENT.has(token.pos) || isMarkedNegation(token)) && !overlaps(token, markers))
39
+ .map((token) => ({
40
+ word: (token.lemma !== undefined && token.lemma !== "" ? token.lemma : token.surface).toLowerCase(),
41
+ negation: !CONTENT.has(token.pos),
42
+ })),
43
+ ),
34
44
  ),
35
45
  ]
36
46
  .toSorted((left, right) => left.localeCompare(right, "en"))
@@ -51,11 +61,21 @@ const sentenceAt = (sentences: readonly Sentence[], offset: number): Sentence |
51
61
  return undefined;
52
62
  };
53
63
 
64
+ /** 中身の語の集まりを足し合わせたもの。並べ方は contentKey と同じ。 */
65
+ const joinedKey = (keys: readonly string[]): string =>
66
+ [...new Set(keys.flatMap((key) => (key === "" ? [] : key.split(" "))))].toSorted((left, right) => left.localeCompare(right, "en")).join(" ");
67
+
54
68
  /**
55
69
  * 様相の語を一つだけ持つ文の、様相と中身。二つ以上持つ文(shall do A and shall not do B)は、どの語がどの行為にかかるかが
56
70
  * 中身の語の集まりからは分からないので外す。品詞の無い文も外す。
71
+ * leadIns は箇条書きの項目の文の導入の文(listLeadIns)。項目の中身には導入の中身の語を足す。導入の条件("If … is made—")は
72
+ * 項目の行為の条件なので、違う導入の下の項目どうしは、同じ行為を決めていない。
57
73
  */
58
- export const modalStatements = (obligations: readonly Obligation[], sentences: readonly Sentence[]): ModalStatement[] => {
74
+ export const modalStatements = (
75
+ obligations: readonly Obligation[],
76
+ sentences: readonly Sentence[],
77
+ leadIns: ReadonlyMap<Sentence, readonly Sentence[]> = new Map(),
78
+ ): ModalStatement[] => {
59
79
  const bySentence = new Map<Sentence, Obligation[]>();
60
80
  obligations.forEach((obligation) => {
61
81
  const sentence = sentenceAt(sentences, obligation.start);
@@ -64,11 +84,26 @@ export const modalStatements = (obligations: readonly Obligation[], sentences: r
64
84
  if (found === undefined) bySentence.set(sentence, [obligation]);
65
85
  else found.push(obligation);
66
86
  });
87
+ /** 様相の語と項目の印を除いた中身。 */
88
+ const keyOf = (sentence: Sentence, tokens: readonly Token[], markers: readonly Span[]): string => {
89
+ const item = itemMarkerSpan(sentence);
90
+ return contentKey(tokens, item === undefined ? markers : [...markers, item]);
91
+ };
92
+ const leadInKeys = new Map<Sentence, string>();
93
+ const leadInKey = (sentence: Sentence): string => {
94
+ const known = leadInKeys.get(sentence);
95
+ if (known !== undefined) return known;
96
+ const key = sentence.tokens === undefined ? "" : keyOf(sentence, sentence.tokens, bySentence.get(sentence) ?? []);
97
+ leadInKeys.set(sentence, key);
98
+ return key;
99
+ };
67
100
  return [...bySentence].flatMap(([sentence, found]) => {
68
101
  const [only] = found;
69
102
  if (found.length !== 1 || only === undefined || sentence.tokens === undefined) return [];
70
- const key = contentKey(sentence.tokens, [only]);
71
- return key.split(" ").length < MIN_CONTENT_WORDS ? [] : [{ offset: sentence.span.start, type: only.type, marker: only.marker, key }];
103
+ const own = keyOf(sentence, sentence.tokens, [only]);
104
+ if (own.split(" ").length < MIN_CONTENT_WORDS) return [];
105
+ const key = joinedKey([own, ...(leadIns.get(sentence) ?? []).map(leadInKey)]);
106
+ return [{ offset: sentence.span.start, type: only.type, marker: only.marker, key }];
72
107
  });
73
108
  };
74
109
 
@@ -0,0 +1,49 @@
1
+ import type { Lexicon, Sentence, Token } from "../plugin.ts";
2
+
3
+ // 行為や条件の中の打ち消し("goods that do not conform"、「承認しなかった場合」)。打ち消しの語は中身の語ではないが、
4
+ // 打ち消しだけが違う二つの文は、違うことを決めている。
5
+
6
+ /** 打ち消しの語に付ける印(Token.features の名前)。 */
7
+ const NEGATION_FEATURE = "negation";
8
+
9
+ /** 打ち消しを受けた語の印。語の後ろに付ける。 */
10
+ export const NEGATED = "¬";
11
+
12
+ /** 語彙表の打ち消しの語(group の無いもの。接頭の「不」「未」は語の一部なので見ない)。英語は小文字で照らす。 */
13
+ export const negationWords = (lexicon: Lexicon | undefined): ReadonlySet<string> =>
14
+ new Set((lexicon ?? []).filter((entry) => entry.group === undefined).map((entry) => entry.pattern.toLowerCase()));
15
+
16
+ const isNegation = (token: Token, words: ReadonlySet<string>): boolean =>
17
+ words.has(token.surface.toLowerCase()) || (token.lemma !== undefined && words.has(token.lemma.toLowerCase()));
18
+
19
+ export const isMarkedNegation = (token: Token): boolean => token.features?.[NEGATION_FEATURE] !== undefined;
20
+
21
+ /** 打ち消しの語に印を付けた文。品詞の無い文はそのまま。 */
22
+ export const negationsMarked = (sentences: readonly Sentence[], words: ReadonlySet<string>): Sentence[] =>
23
+ sentences.map((sentence) =>
24
+ sentence.tokens === undefined
25
+ ? sentence
26
+ : {
27
+ ...sentence,
28
+ tokens: sentence.tokens.map((token) => (isNegation(token, words) ? { ...token, features: { ...token.features, [NEGATION_FEATURE]: "yes" } } : token)),
29
+ },
30
+ );
31
+
32
+ type Placed = { readonly out: readonly string[]; readonly pending: boolean };
33
+
34
+ /**
35
+ * 語の並びの打ち消しを、すぐ前の語(文頭なら次の語)に NEGATED として付けたもの。どの語にかかるかは読まない。
36
+ * 打ち消しのある文と無い文を分けるための印で、二つ重なっても一つにする。
37
+ */
38
+ export const negatedWords = (words: readonly { readonly word: string; readonly negation: boolean }[]): string[] =>
39
+ words
40
+ .reduce<Placed>(
41
+ ({ out, pending }, { word, negation }) => {
42
+ if (!negation) return { out: [...out, pending ? `${word}${NEGATED}` : word], pending: false };
43
+ const last = out.at(-1);
44
+ if (last === undefined) return { out, pending: true };
45
+ return { out: last.endsWith(NEGATED) ? out : [...out.slice(0, -1), `${last}${NEGATED}`], pending: false };
46
+ },
47
+ { out: [], pending: false },
48
+ )
49
+ .out.slice();