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.
- package/dist/announced-count.d.ts +2 -0
- package/dist/announced-count.d.ts.map +1 -1
- package/dist/announced-count.js +4 -3
- package/dist/announced-count.js.map +1 -1
- package/dist/contacts/addresses.d.ts +21 -0
- package/dist/contacts/addresses.d.ts.map +1 -0
- package/dist/contacts/addresses.js +144 -0
- package/dist/contacts/addresses.js.map +1 -0
- package/dist/contacts/emails.d.ts +11 -0
- package/dist/contacts/emails.d.ts.map +1 -0
- package/dist/contacts/emails.js +51 -0
- package/dist/contacts/emails.js.map +1 -0
- package/dist/contacts/variants.d.ts +14 -0
- package/dist/contacts/variants.d.ts.map +1 -0
- package/dist/contacts/variants.js +25 -0
- package/dist/contacts/variants.js.map +1 -0
- package/dist/derived/battery-runtime.d.ts +85 -0
- package/dist/derived/battery-runtime.d.ts.map +1 -0
- package/dist/derived/battery-runtime.js +217 -0
- package/dist/derived/battery-runtime.js.map +1 -0
- package/dist/derived/period-counts.d.ts +48 -0
- package/dist/derived/period-counts.d.ts.map +1 -0
- package/dist/derived/period-counts.js +128 -0
- package/dist/derived/period-counts.js.map +1 -0
- package/dist/derived/session-hours.d.ts +26 -0
- package/dist/derived/session-hours.d.ts.map +1 -0
- package/dist/derived/session-hours.js +29 -0
- package/dist/derived/session-hours.js.map +1 -0
- package/dist/derived/time-lengths.d.ts +37 -0
- package/dist/derived/time-lengths.d.ts.map +1 -0
- package/dist/derived/time-lengths.js +171 -0
- package/dist/derived/time-lengths.js.map +1 -0
- package/dist/derived/unit-counts.d.ts +7 -0
- package/dist/derived/unit-counts.d.ts.map +1 -0
- package/dist/derived/unit-counts.js +13 -0
- package/dist/derived/unit-counts.js.map +1 -0
- package/dist/derived/use-interval.d.ts +33 -0
- package/dist/derived/use-interval.d.ts.map +1 -0
- package/dist/derived/use-interval.js +93 -0
- package/dist/derived/use-interval.js.map +1 -0
- package/dist/derived/working-hours.d.ts +36 -0
- package/dist/derived/working-hours.d.ts.map +1 -0
- package/dist/derived/working-hours.js +55 -0
- package/dist/derived/working-hours.js.map +1 -0
- package/dist/detectors/acronym-expansion.d.ts +1 -1
- package/dist/detectors/acronym-expansion.d.ts.map +1 -1
- package/dist/detectors/acronym-expansion.js +46 -2
- package/dist/detectors/acronym-expansion.js.map +1 -1
- package/dist/detectors/announced-count.d.ts.map +1 -1
- package/dist/detectors/announced-count.js +1 -0
- package/dist/detectors/announced-count.js.map +1 -1
- package/dist/detectors/capacity-runtime.d.ts +7 -0
- package/dist/detectors/capacity-runtime.d.ts.map +1 -0
- package/dist/detectors/capacity-runtime.js +98 -0
- package/dist/detectors/capacity-runtime.js.map +1 -0
- package/dist/detectors/capitalised-term.d.ts +44 -0
- package/dist/detectors/capitalised-term.d.ts.map +1 -0
- package/dist/detectors/capitalised-term.js +101 -0
- package/dist/detectors/capitalised-term.js.map +1 -0
- package/dist/detectors/contact-variant.d.ts +3 -0
- package/dist/detectors/contact-variant.d.ts.map +1 -0
- package/dist/detectors/contact-variant.js +21 -0
- package/dist/detectors/contact-variant.js.map +1 -0
- package/dist/detectors/count-over-capacity.d.ts +4 -0
- package/dist/detectors/count-over-capacity.d.ts.map +1 -0
- package/dist/detectors/count-over-capacity.js +26 -0
- package/dist/detectors/count-over-capacity.js.map +1 -0
- package/dist/detectors/date-outside-period.d.ts.map +1 -1
- package/dist/detectors/date-outside-period.js +5 -1
- package/dist/detectors/date-outside-period.js.map +1 -1
- package/dist/detectors/date-stamp-order.d.ts +3 -0
- package/dist/detectors/date-stamp-order.d.ts.map +1 -0
- package/dist/detectors/date-stamp-order.js +24 -0
- package/dist/detectors/date-stamp-order.js.map +1 -0
- package/dist/detectors/defined-term-form.d.ts.map +1 -1
- package/dist/detectors/defined-term-form.js +57 -7
- package/dist/detectors/defined-term-form.js.map +1 -1
- package/dist/detectors/definition-use.d.ts +1 -1
- package/dist/detectors/definition-use.d.ts.map +1 -1
- package/dist/detectors/definition-use.js +4 -2
- package/dist/detectors/definition-use.js.map +1 -1
- package/dist/detectors/derived-numbers.d.ts +11 -1
- package/dist/detectors/derived-numbers.d.ts.map +1 -1
- package/dist/detectors/derived-numbers.js +74 -20
- package/dist/detectors/derived-numbers.js.map +1 -1
- package/dist/detectors/due-date.d.ts.map +1 -1
- package/dist/detectors/due-date.js +6 -0
- package/dist/detectors/due-date.js.map +1 -1
- package/dist/detectors/en-shape.d.ts.map +1 -1
- package/dist/detectors/en-shape.js +7 -4
- package/dist/detectors/en-shape.js.map +1 -1
- package/dist/detectors/event-deadline.d.ts +3 -0
- package/dist/detectors/event-deadline.d.ts.map +1 -0
- package/dist/detectors/event-deadline.js +24 -0
- package/dist/detectors/event-deadline.js.map +1 -0
- package/dist/detectors/fact-consistency.d.ts +1 -1
- package/dist/detectors/fact-consistency.d.ts.map +1 -1
- package/dist/detectors/fact-consistency.js +52 -3
- package/dist/detectors/fact-consistency.js.map +1 -1
- package/dist/detectors/heading-case.d.ts +5 -2
- package/dist/detectors/heading-case.d.ts.map +1 -1
- package/dist/detectors/heading-case.js +12 -4
- package/dist/detectors/heading-case.js.map +1 -1
- package/dist/detectors/heading-echo.d.ts +5 -0
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +14 -1
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/layout-code.d.ts +5 -0
- package/dist/detectors/layout-code.d.ts.map +1 -0
- package/dist/detectors/layout-code.js +124 -0
- package/dist/detectors/layout-code.js.map +1 -0
- package/dist/detectors/measured-facts.d.ts +8 -0
- package/dist/detectors/measured-facts.d.ts.map +1 -0
- package/dist/detectors/measured-facts.js +18 -0
- package/dist/detectors/measured-facts.js.map +1 -0
- package/dist/detectors/name-variant-model-codes.d.ts +11 -0
- package/dist/detectors/name-variant-model-codes.d.ts.map +1 -0
- package/dist/detectors/name-variant-model-codes.js +21 -0
- package/dist/detectors/name-variant-model-codes.js.map +1 -0
- package/dist/detectors/name-variant.d.ts.map +1 -1
- package/dist/detectors/name-variant.js +78 -10
- package/dist/detectors/name-variant.js.map +1 -1
- package/dist/detectors/orthographic-variant.d.ts.map +1 -1
- package/dist/detectors/orthographic-variant.js +62 -7
- package/dist/detectors/orthographic-variant.js.map +1 -1
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +12 -6
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/party-order.d.ts +3 -0
- package/dist/detectors/party-order.d.ts.map +1 -0
- package/dist/detectors/party-order.js +46 -0
- package/dist/detectors/party-order.js.map +1 -0
- package/dist/detectors/quantity-range.d.ts +3 -0
- package/dist/detectors/quantity-range.d.ts.map +1 -0
- package/dist/detectors/quantity-range.js +35 -0
- package/dist/detectors/quantity-range.js.map +1 -0
- package/dist/detectors/range-band.d.ts +5 -0
- package/dist/detectors/range-band.d.ts.map +1 -0
- package/dist/detectors/range-band.js +80 -0
- package/dist/detectors/range-band.js.map +1 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.d.ts +3 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.js +3 -0
- package/dist/detectors/registry/capacity-runtime-mismatch.js.map +1 -0
- package/dist/detectors/registry/connection-time-short.d.ts +3 -0
- package/dist/detectors/registry/connection-time-short.d.ts.map +1 -0
- package/dist/detectors/registry/connection-time-short.js +3 -0
- package/dist/detectors/registry/connection-time-short.js.map +1 -0
- package/dist/detectors/registry/contact-variant.d.ts +3 -0
- package/dist/detectors/registry/contact-variant.d.ts.map +1 -0
- package/dist/detectors/registry/contact-variant.js +3 -0
- package/dist/detectors/registry/contact-variant.js.map +1 -0
- package/dist/detectors/registry/count-over-capacity.d.ts +3 -0
- package/dist/detectors/registry/count-over-capacity.d.ts.map +1 -0
- package/dist/detectors/registry/count-over-capacity.js +3 -0
- package/dist/detectors/registry/count-over-capacity.js.map +1 -0
- package/dist/detectors/registry/date-stamp-order.d.ts +3 -0
- package/dist/detectors/registry/date-stamp-order.d.ts.map +1 -0
- package/dist/detectors/registry/date-stamp-order.js +3 -0
- package/dist/detectors/registry/date-stamp-order.js.map +1 -0
- package/dist/detectors/registry/duration-product-mismatch.d.ts +3 -0
- package/dist/detectors/registry/duration-product-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/duration-product-mismatch.js +3 -0
- package/dist/detectors/registry/duration-product-mismatch.js.map +1 -0
- package/dist/detectors/registry/event-deadline.d.ts +3 -0
- package/dist/detectors/registry/event-deadline.d.ts.map +1 -0
- package/dist/detectors/registry/event-deadline.js +3 -0
- package/dist/detectors/registry/event-deadline.js.map +1 -0
- package/dist/detectors/registry/interval-count-mismatch.d.ts +3 -0
- package/dist/detectors/registry/interval-count-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/interval-count-mismatch.js +3 -0
- package/dist/detectors/registry/interval-count-mismatch.js.map +1 -0
- package/dist/detectors/registry/layout-code-mismatch.d.ts +3 -0
- package/dist/detectors/registry/layout-code-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/layout-code-mismatch.js +3 -0
- package/dist/detectors/registry/layout-code-mismatch.js.map +1 -0
- package/dist/detectors/registry/party-order.d.ts +3 -0
- package/dist/detectors/registry/party-order.d.ts.map +1 -0
- package/dist/detectors/registry/party-order.js +3 -0
- package/dist/detectors/registry/party-order.js.map +1 -0
- package/dist/detectors/registry/quantity-range.d.ts +3 -0
- package/dist/detectors/registry/quantity-range.d.ts.map +1 -0
- package/dist/detectors/registry/quantity-range.js +3 -0
- package/dist/detectors/registry/quantity-range.js.map +1 -0
- package/dist/detectors/registry/range-band-mismatch.d.ts +3 -0
- package/dist/detectors/registry/range-band-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/range-band-mismatch.js +3 -0
- package/dist/detectors/registry/range-band-mismatch.js.map +1 -0
- package/dist/detectors/registry/rent-multiple-mismatch.d.ts +3 -0
- package/dist/detectors/registry/rent-multiple-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/rent-multiple-mismatch.js +3 -0
- package/dist/detectors/registry/rent-multiple-mismatch.js.map +1 -0
- package/dist/detectors/registry/requirement-lists.d.ts +3 -0
- package/dist/detectors/registry/requirement-lists.d.ts.map +1 -0
- package/dist/detectors/registry/requirement-lists.js +3 -0
- package/dist/detectors/registry/requirement-lists.js.map +1 -0
- package/dist/detectors/registry/return-type.d.ts +3 -0
- package/dist/detectors/registry/return-type.d.ts.map +1 -0
- package/dist/detectors/registry/return-type.js +3 -0
- package/dist/detectors/registry/return-type.js.map +1 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.d.ts +3 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.js +3 -0
- package/dist/detectors/registry/scaled-quantity-mismatch.js.map +1 -0
- package/dist/detectors/registry/step-time-sum-mismatch.d.ts +3 -0
- package/dist/detectors/registry/step-time-sum-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/step-time-sum-mismatch.js +3 -0
- package/dist/detectors/registry/step-time-sum-mismatch.js.map +1 -0
- package/dist/detectors/registry/time-range-reversed.d.ts +3 -0
- package/dist/detectors/registry/time-range-reversed.d.ts.map +1 -0
- package/dist/detectors/registry/time-range-reversed.js +3 -0
- package/dist/detectors/registry/time-range-reversed.js.map +1 -0
- package/dist/detectors/registry/unlisted-item-used.d.ts +3 -0
- package/dist/detectors/registry/unlisted-item-used.d.ts.map +1 -0
- package/dist/detectors/registry/unlisted-item-used.js +3 -0
- package/dist/detectors/registry/unlisted-item-used.js.map +1 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.d.ts +3 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.d.ts.map +1 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.js +3 -0
- package/dist/detectors/registry/walk-time-distance-mismatch.js.map +1 -0
- package/dist/detectors/rent-multiple-mismatch.d.ts +3 -0
- package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -0
- package/dist/detectors/rent-multiple-mismatch.js +39 -0
- package/dist/detectors/rent-multiple-mismatch.js.map +1 -0
- package/dist/detectors/requirement-lists.d.ts +3 -0
- package/dist/detectors/requirement-lists.d.ts.map +1 -0
- package/dist/detectors/requirement-lists.js +46 -0
- package/dist/detectors/requirement-lists.js.map +1 -0
- package/dist/detectors/return-type.d.ts +3 -0
- package/dist/detectors/return-type.d.ts.map +1 -0
- package/dist/detectors/return-type.js +24 -0
- package/dist/detectors/return-type.js.map +1 -0
- package/dist/detectors/scaled-quantity.d.ts +3 -0
- package/dist/detectors/scaled-quantity.d.ts.map +1 -0
- package/dist/detectors/scaled-quantity.js +37 -0
- package/dist/detectors/scaled-quantity.js.map +1 -0
- package/dist/detectors/signals.d.ts.map +1 -1
- package/dist/detectors/signals.js +3 -1
- package/dist/detectors/signals.js.map +1 -1
- package/dist/detectors/step-time-sum.d.ts +3 -0
- package/dist/detectors/step-time-sum.d.ts.map +1 -0
- package/dist/detectors/step-time-sum.js +46 -0
- package/dist/detectors/step-time-sum.js.map +1 -0
- package/dist/detectors/structure-tree.d.ts +2 -0
- package/dist/detectors/structure-tree.d.ts.map +1 -1
- package/dist/detectors/structure-tree.js +31 -6
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/structure.js +1 -1
- package/dist/detectors/structure.js.map +1 -1
- package/dist/detectors/technical-docs.d.ts +5 -0
- package/dist/detectors/technical-docs.d.ts.map +1 -1
- package/dist/detectors/technical-docs.js +12 -0
- package/dist/detectors/technical-docs.js.map +1 -1
- package/dist/detectors/time-order.d.ts +2 -0
- package/dist/detectors/time-order.d.ts.map +1 -1
- package/dist/detectors/time-order.js +33 -5
- package/dist/detectors/time-order.js.map +1 -1
- package/dist/detectors/undefined-term.d.ts.map +1 -1
- package/dist/detectors/undefined-term.js +19 -3
- package/dist/detectors/undefined-term.js.map +1 -1
- package/dist/detectors/unit-consistency.d.ts +1 -1
- package/dist/detectors/unit-consistency.d.ts.map +1 -1
- package/dist/detectors/unit-consistency.js +46 -38
- package/dist/detectors/unit-consistency.js.map +1 -1
- package/dist/detectors/unlisted-items.d.ts +3 -0
- package/dist/detectors/unlisted-items.d.ts.map +1 -0
- package/dist/detectors/unlisted-items.js +57 -0
- package/dist/detectors/unlisted-items.js.map +1 -0
- package/dist/detectors/use-count-words.d.ts +8 -0
- package/dist/detectors/use-count-words.d.ts.map +1 -0
- package/dist/detectors/use-count-words.js +20 -0
- package/dist/detectors/use-count-words.js.map +1 -0
- package/dist/detectors/use-interval.d.ts +3 -0
- package/dist/detectors/use-interval.d.ts.map +1 -0
- package/dist/detectors/use-interval.js +39 -0
- package/dist/detectors/use-interval.js.map +1 -0
- package/dist/detectors/walk-time-distance-mismatch.d.ts +4 -0
- package/dist/detectors/walk-time-distance-mismatch.d.ts.map +1 -0
- package/dist/detectors/walk-time-distance-mismatch.js +38 -0
- package/dist/detectors/walk-time-distance-mismatch.js.map +1 -0
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +4 -1
- package/dist/document.js.map +1 -1
- package/dist/facts/approximate-tolerance.d.ts +7 -0
- package/dist/facts/approximate-tolerance.d.ts.map +1 -0
- package/dist/facts/approximate-tolerance.js +16 -0
- package/dist/facts/approximate-tolerance.js.map +1 -0
- package/dist/facts/item-amounts.d.ts +18 -0
- package/dist/facts/item-amounts.d.ts.map +1 -0
- package/dist/facts/item-amounts.js +67 -0
- package/dist/facts/item-amounts.js.map +1 -0
- package/dist/facts/labelled-facts.d.ts +4 -0
- package/dist/facts/labelled-facts.d.ts.map +1 -1
- package/dist/facts/labelled-facts.js +21 -6
- package/dist/facts/labelled-facts.js.map +1 -1
- package/dist/facts/measure-units.d.ts +5 -0
- package/dist/facts/measure-units.d.ts.map +1 -0
- package/dist/facts/measure-units.js +28 -0
- package/dist/facts/measure-units.js.map +1 -0
- package/dist/facts/measures.d.ts +23 -4
- package/dist/facts/measures.d.ts.map +1 -1
- package/dist/facts/measures.js +68 -19
- package/dist/facts/measures.js.map +1 -1
- package/dist/facts/retention-periods.d.ts +51 -0
- package/dist/facts/retention-periods.d.ts.map +1 -0
- package/dist/facts/retention-periods.js +148 -0
- package/dist/facts/retention-periods.js.map +1 -0
- package/dist/facts/table-facts.d.ts +2 -0
- package/dist/facts/table-facts.d.ts.map +1 -1
- package/dist/facts/table-facts.js +2 -0
- package/dist/facts/table-facts.js.map +1 -1
- package/dist/facts/unit-conflicts.d.ts +3 -2
- package/dist/facts/unit-conflicts.d.ts.map +1 -1
- package/dist/facts/unit-conflicts.js +15 -7
- package/dist/facts/unit-conflicts.js.map +1 -1
- package/dist/facts/unit-equivalents.d.ts +13 -0
- package/dist/facts/unit-equivalents.d.ts.map +1 -0
- package/dist/facts/unit-equivalents.js +56 -0
- package/dist/facts/unit-equivalents.js.map +1 -0
- package/dist/facts/unit-tolerance.d.ts +4 -0
- package/dist/facts/unit-tolerance.d.ts.map +1 -0
- package/dist/facts/unit-tolerance.js +13 -0
- package/dist/facts/unit-tolerance.js.map +1 -0
- package/dist/facts/unit-word.d.ts +8 -0
- package/dist/facts/unit-word.d.ts.map +1 -0
- package/dist/facts/unit-word.js +34 -0
- package/dist/facts/unit-word.js.map +1 -0
- package/dist/facts/written-precision.d.ts +10 -0
- package/dist/facts/written-precision.d.ts.map +1 -0
- package/dist/facts/written-precision.js +20 -0
- package/dist/facts/written-precision.js.map +1 -0
- package/dist/kana-spelling.d.ts +12 -0
- package/dist/kana-spelling.d.ts.map +1 -1
- package/dist/kana-spelling.js +23 -0
- package/dist/kana-spelling.js.map +1 -1
- package/dist/model-codes.d.ts +23 -0
- package/dist/model-codes.d.ts.map +1 -0
- package/dist/model-codes.js +81 -0
- package/dist/model-codes.js.map +1 -0
- package/dist/name-spelling-chars.d.ts +16 -0
- package/dist/name-spelling-chars.d.ts.map +1 -0
- package/dist/name-spelling-chars.js +14 -0
- package/dist/name-spelling-chars.js.map +1 -0
- package/dist/name-variants.d.ts +8 -2
- package/dist/name-variants.d.ts.map +1 -1
- package/dist/name-variants.js +72 -7
- package/dist/name-variants.js.map +1 -1
- package/dist/name-word-order.d.ts +49 -0
- package/dist/name-word-order.d.ts.map +1 -0
- package/dist/name-word-order.js +114 -0
- package/dist/name-word-order.js.map +1 -0
- package/dist/phone-numbers.d.ts.map +1 -1
- package/dist/phone-numbers.js +22 -5
- package/dist/phone-numbers.js.map +1 -1
- package/dist/place-names.d.ts +12 -5
- package/dist/place-names.d.ts.map +1 -1
- package/dist/place-names.js +34 -14
- package/dist/place-names.js.map +1 -1
- package/dist/plugin.d.ts +12 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/preamble-paragraphs.d.ts +2 -1
- package/dist/preamble-paragraphs.d.ts.map +1 -1
- package/dist/preamble-paragraphs.js +16 -1
- package/dist/preamble-paragraphs.js.map +1 -1
- package/dist/profile/parse.d.ts.map +1 -1
- package/dist/profile/parse.js +11 -0
- package/dist/profile/parse.js.map +1 -1
- package/dist/return-type.d.ts +33 -0
- package/dist/return-type.d.ts.map +1 -0
- package/dist/return-type.js +302 -0
- package/dist/return-type.js.map +1 -0
- package/dist/spacing-code.d.ts +6 -0
- package/dist/spacing-code.d.ts.map +1 -0
- package/dist/spacing-code.js +16 -0
- package/dist/spacing-code.js.map +1 -0
- package/dist/structure/amount-range.d.ts +2 -2
- package/dist/structure/amount-range.d.ts.map +1 -1
- package/dist/structure/amount-range.js +12 -5
- package/dist/structure/amount-range.js.map +1 -1
- package/dist/structure/capacity-count.d.ts +59 -0
- package/dist/structure/capacity-count.d.ts.map +1 -0
- package/dist/structure/capacity-count.js +118 -0
- package/dist/structure/capacity-count.js.map +1 -0
- package/dist/structure/connection-times.d.ts +56 -0
- package/dist/structure/connection-times.d.ts.map +1 -0
- package/dist/structure/connection-times.js +165 -0
- package/dist/structure/connection-times.js.map +1 -0
- package/dist/structure/counted-amounts.d.ts +6 -0
- package/dist/structure/counted-amounts.d.ts.map +1 -1
- package/dist/structure/counted-amounts.js +4 -4
- package/dist/structure/counted-amounts.js.map +1 -1
- package/dist/structure/date-order-tie.d.ts +13 -0
- package/dist/structure/date-order-tie.d.ts.map +1 -0
- package/dist/structure/date-order-tie.js +61 -0
- package/dist/structure/date-order-tie.js.map +1 -0
- package/dist/structure/date-order.d.ts +2 -2
- package/dist/structure/date-order.d.ts.map +1 -1
- package/dist/structure/date-order.js +26 -9
- package/dist/structure/date-order.js.map +1 -1
- package/dist/structure/date-outside-period.d.ts +8 -0
- package/dist/structure/date-outside-period.d.ts.map +1 -1
- package/dist/structure/date-outside-period.js +52 -3
- package/dist/structure/date-outside-period.js.map +1 -1
- package/dist/structure/date-range.d.ts +6 -2
- package/dist/structure/date-range.d.ts.map +1 -1
- package/dist/structure/date-range.js +37 -9
- package/dist/structure/date-range.js.map +1 -1
- package/dist/structure/deadline-after-event.d.ts +23 -0
- package/dist/structure/deadline-after-event.d.ts.map +1 -0
- package/dist/structure/deadline-after-event.js +42 -0
- package/dist/structure/deadline-after-event.js.map +1 -0
- package/dist/structure/definition-use.d.ts +1 -1
- package/dist/structure/definition-use.d.ts.map +1 -1
- package/dist/structure/definition-use.js +55 -2
- package/dist/structure/definition-use.js.map +1 -1
- package/dist/structure/due-date.d.ts.map +1 -1
- package/dist/structure/due-date.js +5 -27
- package/dist/structure/due-date.js.map +1 -1
- package/dist/structure/labelled-date-order.d.ts +38 -0
- package/dist/structure/labelled-date-order.d.ts.map +1 -0
- package/dist/structure/labelled-date-order.js +41 -0
- package/dist/structure/labelled-date-order.js.map +1 -0
- package/dist/structure/layout-codes.d.ts +68 -0
- package/dist/structure/layout-codes.d.ts.map +1 -0
- package/dist/structure/layout-codes.js +161 -0
- package/dist/structure/layout-codes.js.map +1 -0
- package/dist/structure/leg-times.d.ts +2 -0
- package/dist/structure/leg-times.d.ts.map +1 -1
- package/dist/structure/leg-times.js +13 -0
- package/dist/structure/leg-times.js.map +1 -1
- package/dist/structure/measured-amounts.d.ts +17 -0
- package/dist/structure/measured-amounts.d.ts.map +1 -0
- package/dist/structure/measured-amounts.js +110 -0
- package/dist/structure/measured-amounts.js.map +1 -0
- package/dist/structure/party-order.d.ts +33 -0
- package/dist/structure/party-order.d.ts.map +1 -0
- package/dist/structure/party-order.js +79 -0
- package/dist/structure/party-order.js.map +1 -0
- package/dist/structure/quantity-range.d.ts +8 -0
- package/dist/structure/quantity-range.d.ts.map +1 -0
- package/dist/structure/quantity-range.js +36 -0
- package/dist/structure/quantity-range.js.map +1 -0
- package/dist/structure/range-bands.d.ts +75 -0
- package/dist/structure/range-bands.d.ts.map +1 -0
- package/dist/structure/range-bands.js +244 -0
- package/dist/structure/range-bands.js.map +1 -0
- package/dist/structure/rent-multiple.d.ts +76 -0
- package/dist/structure/rent-multiple.d.ts.map +1 -0
- package/dist/structure/rent-multiple.js +186 -0
- package/dist/structure/rent-multiple.js.map +1 -0
- package/dist/structure/requirement-lists.d.ts +37 -0
- package/dist/structure/requirement-lists.d.ts.map +1 -0
- package/dist/structure/requirement-lists.js +89 -0
- package/dist/structure/requirement-lists.js.map +1 -0
- package/dist/structure/scaled-quantities.d.ts +50 -0
- package/dist/structure/scaled-quantities.d.ts.map +1 -0
- package/dist/structure/scaled-quantities.js +153 -0
- package/dist/structure/scaled-quantities.js.map +1 -0
- package/dist/structure/stamp-order.d.ts +16 -0
- package/dist/structure/stamp-order.d.ts.map +1 -0
- package/dist/structure/stamp-order.js +78 -0
- package/dist/structure/stamp-order.js.map +1 -0
- package/dist/structure/step-times.d.ts +49 -0
- package/dist/structure/step-times.d.ts.map +1 -0
- package/dist/structure/step-times.js +128 -0
- package/dist/structure/step-times.js.map +1 -0
- package/dist/structure/term-deadline.d.ts +19 -0
- package/dist/structure/term-deadline.d.ts.map +1 -0
- package/dist/structure/term-deadline.js +28 -0
- package/dist/structure/term-deadline.js.map +1 -0
- package/dist/structure/time-order.d.ts +21 -0
- package/dist/structure/time-order.d.ts.map +1 -1
- package/dist/structure/time-order.js +5 -6
- package/dist/structure/time-order.js.map +1 -1
- package/dist/structure/time-range.d.ts +16 -0
- package/dist/structure/time-range.d.ts.map +1 -0
- package/dist/structure/time-range.js +57 -0
- package/dist/structure/time-range.js.map +1 -0
- package/dist/structure/total-line.d.ts +16 -0
- package/dist/structure/total-line.d.ts.map +1 -0
- package/dist/structure/total-line.js +44 -0
- package/dist/structure/total-line.js.map +1 -0
- package/dist/structure/total.d.ts +7 -1
- package/dist/structure/total.d.ts.map +1 -1
- package/dist/structure/total.js +28 -5
- package/dist/structure/total.js.map +1 -1
- package/dist/structure/unlisted-items.d.ts +70 -0
- package/dist/structure/unlisted-items.d.ts.map +1 -0
- package/dist/structure/unlisted-items.js +152 -0
- package/dist/structure/unlisted-items.js.map +1 -0
- package/dist/structure/version-order.d.ts +2 -0
- package/dist/structure/version-order.d.ts.map +1 -1
- package/dist/structure/version-order.js +10 -0
- package/dist/structure/version-order.js.map +1 -1
- package/dist/structure/walk-time.d.ts +87 -0
- package/dist/structure/walk-time.d.ts.map +1 -0
- package/dist/structure/walk-time.js +195 -0
- package/dist/structure/walk-time.js.map +1 -0
- package/dist/surname-near.d.ts +6 -0
- package/dist/surname-near.d.ts.map +1 -0
- package/dist/surname-near.js +31 -0
- package/dist/surname-near.js.map +1 -0
- package/dist/table-cells.d.ts +13 -0
- package/dist/table-cells.d.ts.map +1 -0
- package/dist/table-cells.js +36 -0
- package/dist/table-cells.js.map +1 -0
- package/dist/table-names.d.ts +42 -0
- package/dist/table-names.d.ts.map +1 -0
- package/dist/table-names.js +94 -0
- package/dist/table-names.js.map +1 -0
- package/package.json +3 -3
- package/profiles/listing.yaml +20 -0
- package/rules/announced-count-mismatch.yaml +2 -2
- package/rules/capacity-runtime-mismatch.yaml +66 -0
- package/rules/connection-time-short.yaml +52 -0
- package/rules/contact-variant.yaml +76 -0
- package/rules/count-over-capacity.yaml +55 -0
- package/rules/date-outside-period.yaml +4 -4
- package/rules/date-stamp-order.yaml +82 -0
- package/rules/deadline-after-event.yaml +67 -0
- package/rules/defined-name-repeated.yaml +2 -2
- package/rules/due-before-issue.yaml +11 -7
- package/rules/duration-mismatch.yaml +7 -4
- package/rules/duration-product-mismatch.yaml +73 -0
- package/rules/fact-conflict.yaml +9 -4
- package/rules/heading-echo.yaml +2 -2
- package/rules/interval-count-mismatch.yaml +54 -0
- package/rules/latin-spacing.yaml +2 -2
- package/rules/layout-code-mismatch.yaml +68 -0
- package/rules/name-variant.yaml +14 -2
- package/rules/no-em-dash.yaml +2 -2
- package/rules/party-order.yaml +84 -0
- package/rules/preamble-length.yaml +2 -2
- package/rules/quantity-range-reversed.yaml +53 -0
- package/rules/range-band-mismatch.yaml +70 -0
- package/rules/rent-multiple-mismatch.yaml +61 -0
- package/rules/required-listed-as-preferred.yaml +102 -0
- package/rules/return-type-mismatch.yaml +79 -0
- package/rules/scaled-quantity-mismatch.yaml +69 -0
- package/rules/step-time-sum-mismatch.yaml +65 -0
- package/rules/time-range-reversed.yaml +48 -0
- package/rules/title-case-consistency.yaml +2 -2
- package/rules/total-mismatch.yaml +2 -2
- package/rules/undefined-term.yaml +21 -7
- package/rules/unit-mismatch.yaml +12 -4
- package/rules/unlisted-item-used.yaml +90 -0
- package/rules/unused-definition.yaml +2 -2
- package/rules/use-before-definition.yaml +2 -2
- package/rules/walk-time-distance-mismatch.yaml +55 -0
- package/src/announced-count.ts +6 -3
- package/src/contacts/addresses.ts +179 -0
- package/src/contacts/emails.ts +63 -0
- package/src/contacts/variants.ts +33 -0
- package/src/derived/battery-runtime.ts +284 -0
- package/src/derived/period-counts.ts +178 -0
- package/src/derived/session-hours.ts +62 -0
- package/src/derived/time-lengths.ts +229 -0
- package/src/derived/unit-counts.ts +18 -0
- package/src/derived/use-interval.ts +151 -0
- package/src/derived/working-hours.ts +97 -0
- package/src/detectors/acronym-expansion.ts +57 -2
- package/src/detectors/announced-count.ts +1 -0
- package/src/detectors/capacity-runtime.ts +128 -0
- package/src/detectors/capitalised-term.ts +147 -0
- package/src/detectors/contact-variant.ts +24 -0
- package/src/detectors/count-over-capacity.ts +37 -0
- package/src/detectors/date-outside-period.ts +5 -1
- package/src/detectors/date-stamp-order.ts +28 -0
- package/src/detectors/defined-term-form.ts +63 -7
- package/src/detectors/definition-use.ts +6 -2
- package/src/detectors/derived-numbers.ts +97 -24
- package/src/detectors/due-date.ts +10 -2
- package/src/detectors/en-shape.ts +11 -4
- package/src/detectors/event-deadline.ts +29 -0
- package/src/detectors/fact-consistency.ts +64 -3
- package/src/detectors/heading-case.ts +13 -4
- package/src/detectors/heading-echo.ts +17 -1
- package/src/detectors/layout-code.ts +174 -0
- package/src/detectors/measured-facts.ts +20 -0
- package/src/detectors/name-variant-model-codes.ts +27 -0
- package/src/detectors/name-variant.ts +99 -15
- package/src/detectors/orthographic-variant.ts +78 -9
- package/src/detectors/orthography.ts +16 -6
- package/src/detectors/party-order.ts +50 -0
- package/src/detectors/quantity-range.ts +47 -0
- package/src/detectors/range-band.ts +125 -0
- package/src/detectors/registry/capacity-runtime-mismatch.ts +4 -0
- package/src/detectors/registry/connection-time-short.ts +4 -0
- package/src/detectors/registry/contact-variant.ts +4 -0
- package/src/detectors/registry/count-over-capacity.ts +4 -0
- package/src/detectors/registry/date-stamp-order.ts +4 -0
- package/src/detectors/registry/duration-product-mismatch.ts +4 -0
- package/src/detectors/registry/event-deadline.ts +4 -0
- package/src/detectors/registry/interval-count-mismatch.ts +4 -0
- package/src/detectors/registry/layout-code-mismatch.ts +4 -0
- package/src/detectors/registry/party-order.ts +4 -0
- package/src/detectors/registry/quantity-range.ts +4 -0
- package/src/detectors/registry/range-band-mismatch.ts +4 -0
- package/src/detectors/registry/rent-multiple-mismatch.ts +4 -0
- package/src/detectors/registry/requirement-lists.ts +4 -0
- package/src/detectors/registry/return-type.ts +4 -0
- package/src/detectors/registry/scaled-quantity-mismatch.ts +4 -0
- package/src/detectors/registry/step-time-sum-mismatch.ts +4 -0
- package/src/detectors/registry/time-range-reversed.ts +4 -0
- package/src/detectors/registry/unlisted-item-used.ts +4 -0
- package/src/detectors/registry/walk-time-distance-mismatch.ts +4 -0
- package/src/detectors/rent-multiple-mismatch.ts +56 -0
- package/src/detectors/requirement-lists.ts +65 -0
- package/src/detectors/return-type.ts +30 -0
- package/src/detectors/scaled-quantity.ts +49 -0
- package/src/detectors/signals.ts +3 -1
- package/src/detectors/step-time-sum.ts +55 -0
- package/src/detectors/structure-tree.ts +42 -6
- package/src/detectors/structure.ts +1 -1
- package/src/detectors/technical-docs.ts +12 -0
- package/src/detectors/time-order.ts +45 -6
- package/src/detectors/undefined-term.ts +20 -3
- package/src/detectors/unit-consistency.ts +50 -38
- package/src/detectors/unlisted-items.ts +72 -0
- package/src/detectors/use-count-words.ts +32 -0
- package/src/detectors/use-interval.ts +51 -0
- package/src/detectors/walk-time-distance-mismatch.ts +45 -0
- package/src/document.ts +4 -1
- package/src/facts/approximate-tolerance.ts +19 -0
- package/src/facts/item-amounts.ts +94 -0
- package/src/facts/labelled-facts.ts +29 -7
- package/src/facts/measure-units.ts +31 -0
- package/src/facts/measures.ts +113 -25
- package/src/facts/retention-periods.ts +216 -0
- package/src/facts/table-facts.ts +3 -0
- package/src/facts/unit-conflicts.ts +22 -9
- package/src/facts/unit-equivalents.ts +70 -0
- package/src/facts/unit-tolerance.ts +17 -0
- package/src/facts/unit-word.ts +35 -0
- package/src/facts/written-precision.ts +23 -0
- package/src/kana-spelling.ts +25 -0
- package/src/model-codes.ts +104 -0
- package/src/name-spelling-chars.ts +34 -0
- package/src/name-variants.ts +83 -7
- package/src/name-word-order.ts +149 -0
- package/src/phone-numbers.ts +26 -6
- package/src/place-names.ts +53 -14
- package/src/plugin.ts +7 -0
- package/src/preamble-paragraphs.ts +21 -2
- package/src/profile/parse.ts +10 -0
- package/src/return-type.ts +361 -0
- package/src/spacing-code.ts +26 -0
- package/src/structure/amount-range.ts +16 -7
- package/src/structure/capacity-count.ts +196 -0
- package/src/structure/connection-times.ts +226 -0
- package/src/structure/counted-amounts.ts +5 -4
- package/src/structure/date-order-tie.ts +86 -0
- package/src/structure/date-order.ts +41 -14
- package/src/structure/date-outside-period.ts +77 -7
- package/src/structure/date-range.ts +45 -9
- package/src/structure/deadline-after-event.ts +67 -0
- package/src/structure/definition-use.ts +59 -2
- package/src/structure/due-date.ts +8 -32
- package/src/structure/labelled-date-order.ts +84 -0
- package/src/structure/layout-codes.ts +210 -0
- package/src/structure/leg-times.ts +15 -0
- package/src/structure/measured-amounts.ts +138 -0
- package/src/structure/party-order.ts +115 -0
- package/src/structure/quantity-range.ts +54 -0
- package/src/structure/range-bands.ts +316 -0
- package/src/structure/rent-multiple.ts +277 -0
- package/src/structure/requirement-lists.ts +145 -0
- package/src/structure/scaled-quantities.ts +225 -0
- package/src/structure/stamp-order.ts +100 -0
- package/src/structure/step-times.ts +169 -0
- package/src/structure/term-deadline.ts +35 -0
- package/src/structure/time-order.ts +8 -7
- package/src/structure/time-range.ts +64 -0
- package/src/structure/total-line.ts +63 -0
- package/src/structure/total.ts +41 -6
- package/src/structure/unlisted-items.ts +207 -0
- package/src/structure/version-order.ts +10 -0
- package/src/structure/walk-time.ts +285 -0
- package/src/surname-near.ts +31 -0
- package/src/table-cells.ts +41 -0
- package/src/table-names.ts +113 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
import { escapeRegExp } from "../orthography.ts";
|
|
3
|
+
import type { Mark } from "../structure/time-marks.ts";
|
|
4
|
+
import { linesOf } from "../structure/lines.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Counts of uses in a period (1日3回, 3 times a day, twice in 24 hours) and the words that make a number a limit (まで,
|
|
8
|
+
* no more than, 最大量). The words come from the language packages; nothing here knows a language. Pure.
|
|
9
|
+
*/
|
|
10
|
+
export type PeriodWord = Mark & { readonly hours: number };
|
|
11
|
+
|
|
12
|
+
/** A number written as a word, with its value (three, twice). alone: it counts uses without a counter (twice a day). */
|
|
13
|
+
export type CountWord = { readonly pattern: string; readonly value: number; readonly alone: boolean };
|
|
14
|
+
|
|
15
|
+
export type PeriodCountWords = {
|
|
16
|
+
/** Periods: before the number (1日3回) or after the counter (3 times a day). */
|
|
17
|
+
readonly periods: readonly PeriodWord[];
|
|
18
|
+
/** What a count of uses is written in (回, times, doses). */
|
|
19
|
+
readonly counters: readonly string[];
|
|
20
|
+
readonly numberWords: readonly CountWord[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type PeriodCount = Span & { readonly amount: number; readonly hours: number };
|
|
24
|
+
|
|
25
|
+
const alternation = (words: readonly string[]): string =>
|
|
26
|
+
[...new Set(words.filter((word) => word !== ""))]
|
|
27
|
+
.toSorted((left, right) => right.length - left.length)
|
|
28
|
+
.map(escapeRegExp)
|
|
29
|
+
.join("|");
|
|
30
|
+
|
|
31
|
+
/** A word that does not run on from a Latin letter or a digit before it, or into a Latin letter after it. */
|
|
32
|
+
const bounded = (pattern: string): string => `(?<![A-Za-z0-9])(?:${pattern})(?![A-Za-z])`;
|
|
33
|
+
|
|
34
|
+
const DIGITS = "(?<![\\p{N}.,.])\\p{Nd}+(?:[..]\\p{Nd}+)?";
|
|
35
|
+
|
|
36
|
+
const numberAt = (written: string, words: readonly CountWord[]): number =>
|
|
37
|
+
words.find((word) => word.pattern.toLowerCase() === written.toLowerCase())?.value ?? Number(written.normalize("NFKC"));
|
|
38
|
+
|
|
39
|
+
const periodsOn = (words: PeriodCountWords, side: "before" | "after"): PeriodWord[] => words.periods.filter((period) => (period.position ?? "before") === side);
|
|
40
|
+
|
|
41
|
+
const hoursOf = (words: PeriodCountWords, written: string): number =>
|
|
42
|
+
words.periods.find((period) => period.pattern.toLowerCase() === written.toLowerCase())?.hours ?? 0;
|
|
43
|
+
|
|
44
|
+
/** The hours of the longest period word the match starts with. */
|
|
45
|
+
const periodHoursIn = (written: string, words: PeriodCountWords): number => {
|
|
46
|
+
const found = periodsOn(words, "before")
|
|
47
|
+
.toSorted((left, right) => right.pattern.length - left.pattern.length)
|
|
48
|
+
.find((period) => written.toLowerCase().startsWith(period.pattern.toLowerCase()));
|
|
49
|
+
return found?.hours ?? 0;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** At most this many letters (の, に, あたり) between a period written first and its count: 1日に3回. */
|
|
53
|
+
const JOINER = "[^\\p{N}\\s。、,,..!?!?]{0,4}";
|
|
54
|
+
|
|
55
|
+
/** 1日3回: the period first, then the number and the counter. */
|
|
56
|
+
const periodFirst = (text: string, words: PeriodCountWords): PeriodCount[] => {
|
|
57
|
+
const [periods, counters] = [alternation(periodsOn(words, "before").map((word) => word.pattern)), alternation(words.counters)];
|
|
58
|
+
if (periods === "" || counters === "") return [];
|
|
59
|
+
const pattern = new RegExp(`${bounded(periods)}${JOINER}\\s?(?<digits>${DIGITS})\\s?(?:${counters})`, "giu");
|
|
60
|
+
return [...text.matchAll(pattern)].map((match) => ({
|
|
61
|
+
start: match.index,
|
|
62
|
+
end: match.index + match[0].length,
|
|
63
|
+
amount: Number((match.groups?.["digits"] ?? "").normalize("NFKC")),
|
|
64
|
+
hours: periodHoursIn(match[0], words),
|
|
65
|
+
}));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const namesOf = (words: readonly CountWord[], alone: boolean): string => alternation(words.filter((word) => word.alone === alone).map((word) => word.pattern));
|
|
69
|
+
|
|
70
|
+
/** 3 times a day, twice in 24 hours: the number and the counter first, then the period. */
|
|
71
|
+
const periodLast = (text: string, words: PeriodCountWords): PeriodCount[] => {
|
|
72
|
+
const [periods, counters] = [alternation(periodsOn(words, "after").map((word) => word.pattern)), alternation(words.counters)];
|
|
73
|
+
if (periods === "" || counters === "") return [];
|
|
74
|
+
const [counted, alone] = [namesOf(words.numberWords, false), namesOf(words.numberWords, true)];
|
|
75
|
+
const number = counted === "" ? `(?<digits>${DIGITS})` : `(?:(?<digits>${DIGITS})|(?<word>${bounded(counted)}))`;
|
|
76
|
+
const once = alone === "" ? "" : `|(?<alone>${bounded(alone)})`;
|
|
77
|
+
const pattern = new RegExp(`(?:${number}\\s?${bounded(counters)}${once})\\s+(?<period>${bounded(periods)})`, "giu");
|
|
78
|
+
return [...text.matchAll(pattern)].map((match) => {
|
|
79
|
+
const groups = match.groups ?? {};
|
|
80
|
+
const written = groups["digits"] ?? groups["word"] ?? groups["alone"] ?? "";
|
|
81
|
+
return {
|
|
82
|
+
start: match.index,
|
|
83
|
+
end: match.index + match[0].length,
|
|
84
|
+
amount: numberAt(written, words.numberWords),
|
|
85
|
+
hours: hoursOf(words, groups["period"] ?? ""),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const FULL_WIDTH_ZERO = 0xff10;
|
|
91
|
+
const ASCII_ZERO = 0x30;
|
|
92
|
+
|
|
93
|
+
/** Full-width digits as ASCII ones, one for one, so offsets stay (1日 reads as 1日). */
|
|
94
|
+
export const asciiDigits = (text: string): string =>
|
|
95
|
+
text.replace(/[0-9]/gu, (digit) => String.fromCharCode(digit.charCodeAt(0) - FULL_WIDTH_ZERO + ASCII_ZERO));
|
|
96
|
+
|
|
97
|
+
/** Counts of uses in a period, in the order written. */
|
|
98
|
+
export const periodCounts = (written: string, words: PeriodCountWords): PeriodCount[] => {
|
|
99
|
+
const text = asciiDigits(written);
|
|
100
|
+
return [...periodFirst(text, words), ...periodLast(text, words)]
|
|
101
|
+
.filter((count) => Number.isFinite(count.amount) && count.amount > 0 && count.hours > 0)
|
|
102
|
+
.toSorted((left, right) => left.start - right.start);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/** The periods written in text, wherever they stand (the maximum daily dose, 1日の最大量). */
|
|
106
|
+
export const periodsIn = (written: string, words: PeriodCountWords): (Span & { readonly hours: number })[] => {
|
|
107
|
+
const text = asciiDigits(written);
|
|
108
|
+
const all = alternation(words.periods.map((word) => word.pattern));
|
|
109
|
+
if (all === "") return [];
|
|
110
|
+
return [...text.matchAll(new RegExp(bounded(all), "giu"))].map((match) => ({
|
|
111
|
+
start: match.index,
|
|
112
|
+
end: match.index + match[0].length,
|
|
113
|
+
hours: hoursOf(words, match[0]),
|
|
114
|
+
}));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const SENTENCE_END = /[。.!?!?\n]|\.(?=\s|$)/gu;
|
|
118
|
+
|
|
119
|
+
/** How far back the window of a number reaches at most, in characters. */
|
|
120
|
+
const WINDOW_REACH = 120;
|
|
121
|
+
|
|
122
|
+
/** The index of the last of values (ascending) at or before at, or -1. */
|
|
123
|
+
export const lastIndexAtOrBefore = (values: readonly number[], at: number): number => {
|
|
124
|
+
let [low, high, found] = [0, values.length - 1, -1];
|
|
125
|
+
while (low <= high) {
|
|
126
|
+
const middle = (low + high) >> 1;
|
|
127
|
+
if ((values[middle] ?? Infinity) <= at) [found, low] = [middle, middle + 1];
|
|
128
|
+
else high = middle - 1;
|
|
129
|
+
}
|
|
130
|
+
return found;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** Where the stretch a number is read in begins: after the sentence end, or the last of takenEnds (ascending), before at. */
|
|
134
|
+
export const windowStart = (text: string, at: number, takenEnds: readonly number[]): number => {
|
|
135
|
+
const reach = Math.max(0, at - WINDOW_REACH);
|
|
136
|
+
const ends = [...text.slice(reach, at).matchAll(SENTENCE_END)].map((match) => reach + match.index + match[0].length);
|
|
137
|
+
return Math.max(reach, takenEnds[lastIndexAtOrBefore(takenEnds, at)] ?? 0, ...ends);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export type LimitWords = {
|
|
141
|
+
/** Limit words; a word in group limit-negated ("more than") is one only with a negation before it ("do not take more than"). */
|
|
142
|
+
readonly limits: readonly Mark[];
|
|
143
|
+
readonly negations: readonly string[];
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const NEGATED = "limit-negated";
|
|
147
|
+
const LIMIT_GAP_AFTER = /^[\s、,]*/u;
|
|
148
|
+
const LIMIT_REACH_AFTER = 24;
|
|
149
|
+
|
|
150
|
+
const containsWord = (text: string, word: string): boolean => new RegExp(bounded(escapeRegExp(word.toLowerCase())), "u").test(text);
|
|
151
|
+
|
|
152
|
+
/** A limit word in the window before span (no more than, 最大量は), or right after it (まで, を超えて). */
|
|
153
|
+
export const isLimited = (text: string, span: Span, from: number, words: LimitWords): boolean => {
|
|
154
|
+
const before = text.slice(from, span.start).toLowerCase();
|
|
155
|
+
const after = text.slice(span.end, span.end + LIMIT_REACH_AFTER).toLowerCase();
|
|
156
|
+
const gap = LIMIT_GAP_AFTER.exec(after)?.[0].length ?? 0;
|
|
157
|
+
const negated = words.negations.some((word) => containsWord(before, word));
|
|
158
|
+
return words.limits.some((limit) =>
|
|
159
|
+
(limit.position ?? "before") === "before"
|
|
160
|
+
? containsWord(before, limit.pattern) && (limit.group !== NEGATED || negated)
|
|
161
|
+
: after.startsWith(limit.pattern.toLowerCase(), gap),
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const TABLE_ROW = /^\s*\|/u;
|
|
166
|
+
|
|
167
|
+
const isProse = (line: { readonly text: string } | undefined): boolean => line !== undefined && line.text.trim() !== "" && !TABLE_ROW.test(line.text);
|
|
168
|
+
|
|
169
|
+
/** Runs of non-blank lines that are not table rows: the paragraphs a statement of use is read in. */
|
|
170
|
+
export const paragraphsOf = (text: string): Span[] =>
|
|
171
|
+
linesOf(text).reduce<Span[]>((runs, line, index, lines) => {
|
|
172
|
+
if (!isProse(line)) return runs;
|
|
173
|
+
const end = line.start + line.text.length;
|
|
174
|
+
const last = runs.at(-1);
|
|
175
|
+
if (isProse(lines[index - 1]) && last !== undefined) runs[runs.length - 1] = { start: last.start, end };
|
|
176
|
+
else runs.push({ start: line.start, end });
|
|
177
|
+
return runs;
|
|
178
|
+
}, []);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
import type { Mark } from "../structure/time-marks.ts";
|
|
3
|
+
import { labelOf, readScopes, within, type TimeLength } from "./time-lengths.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A number of sessions times the length of one against the stated total (90分×15回(計24時間), 15 sessions of 90
|
|
7
|
+
* minutes, 24 hours in total). Read in one line, or in a list whose items say it together, holding exactly one count of
|
|
8
|
+
* two or more sessions, one length of a session and one total. A total written as approximate (約24時間, about 24 hours)
|
|
9
|
+
* may differ by less than the smallest unit it is written in. Pure.
|
|
10
|
+
*/
|
|
11
|
+
export type Count = Span & { readonly amount: number };
|
|
12
|
+
|
|
13
|
+
export type SessionHoursWords = { readonly totals: readonly Mark[]; readonly approximate: readonly Mark[] };
|
|
14
|
+
|
|
15
|
+
export type SessionHoursMismatch = {
|
|
16
|
+
readonly count: Count;
|
|
17
|
+
readonly length: TimeLength;
|
|
18
|
+
readonly total: TimeLength;
|
|
19
|
+
/** The count times the length, in minutes. */
|
|
20
|
+
readonly expected: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const overlaps = (left: Span, right: Span): boolean => left.start < right.end && right.start < left.end;
|
|
24
|
+
|
|
25
|
+
type Statement = { readonly count: Count; readonly length: TimeLength; readonly total: TimeLength };
|
|
26
|
+
|
|
27
|
+
/** The length with a rough mark written before it taken in (計約24時間 reads its label past 約). */
|
|
28
|
+
const withRoughMark = (text: string, length: TimeLength, words: SessionHoursWords): Span => {
|
|
29
|
+
const mark = labelOf(text, length, words.approximate);
|
|
30
|
+
return mark !== undefined && mark.end <= length.start ? { start: mark.start, end: length.end } : length;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const statementIn = (text: string, scope: Span, counts: readonly Count[], lengths: readonly TimeLength[], words: SessionHoursWords) => {
|
|
34
|
+
const inCounts = counts.filter((count) => count.amount > 1 && within(count, scope));
|
|
35
|
+
const inLengths = lengths.filter((length) => within(length, scope));
|
|
36
|
+
const totals = inLengths.filter((length) => labelOf(text, withRoughMark(text, length, words), words.totals) !== undefined);
|
|
37
|
+
const sessions = inLengths.filter((length) => !totals.includes(length));
|
|
38
|
+
const [count, length, total] = [inCounts[0], sessions[0], totals[0]];
|
|
39
|
+
if (inCounts.length !== 1 || sessions.length !== 1 || totals.length !== 1 || count === undefined || length === undefined || total === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (overlaps(count, length) || labelOf(text, length, words.approximate) !== undefined) return undefined;
|
|
42
|
+
return { count, length, total } satisfies Statement;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const allowanceOf = (text: string, total: TimeLength, words: SessionHoursWords): number =>
|
|
46
|
+
labelOf(text, total, words.approximate) === undefined ? 0 : total.unit;
|
|
47
|
+
|
|
48
|
+
/** Sessions whose count times length is not the stated total. counts are the written counts of sessions (15回, 15 sessions). */
|
|
49
|
+
export const sessionHoursMismatches = (
|
|
50
|
+
text: string,
|
|
51
|
+
source: string,
|
|
52
|
+
sentences: readonly Span[],
|
|
53
|
+
counts: readonly Count[],
|
|
54
|
+
lengths: readonly TimeLength[],
|
|
55
|
+
words: SessionHoursWords,
|
|
56
|
+
): SessionHoursMismatch[] =>
|
|
57
|
+
readScopes(text, source, { sentences, times: [] }, (scope) => statementIn(text, scope, counts, lengths, words)).flatMap(({ count, length, total }) => {
|
|
58
|
+
const expected = count.amount * length.minutes;
|
|
59
|
+
const allowance = allowanceOf(text, total, words);
|
|
60
|
+
if (allowance === 0 ? expected === total.minutes : Math.abs(expected - total.minutes) < allowance) return [];
|
|
61
|
+
return [{ count, length, total, expected }];
|
|
62
|
+
});
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
import { escapeRegExp } from "../orthography.ts";
|
|
3
|
+
import { isWordAt, wordOffsets, type Mark } from "../structure/time-marks.ts";
|
|
4
|
+
import { linesOf } from "../structure/lines.ts";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Lengths of time in hours and minutes (8時間, 7時間30分, 90分, 1-hour, one-hour, 7.5 hours), the words written beside
|
|
8
|
+
* them (実働8時間, 8 hours a day), and the stretches of text a statement of hours is read in. The words come from the
|
|
9
|
+
* language packages; nothing here knows a language. Pure.
|
|
10
|
+
*/
|
|
11
|
+
export type TimeLength = Span & {
|
|
12
|
+
readonly minutes: number;
|
|
13
|
+
/** The smallest unit the length is written in, in minutes: 60 for 24時間, 1 for 90分 or 7時間30分, 30 for 7時間半. */
|
|
14
|
+
readonly unit: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type LengthWords = {
|
|
18
|
+
readonly hourUnits: readonly string[];
|
|
19
|
+
readonly minuteUnits: readonly string[];
|
|
20
|
+
/** What written right after the hour unit adds half an hour (7時間半). */
|
|
21
|
+
readonly halves: readonly string[];
|
|
22
|
+
/** Numbers written as words, in order from one. */
|
|
23
|
+
readonly numberWords: readonly string[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const MINUTES_PER_HOUR = 60;
|
|
27
|
+
const HALF_HOUR = 30;
|
|
28
|
+
|
|
29
|
+
const alternation = (words: readonly string[]): string =>
|
|
30
|
+
words
|
|
31
|
+
.filter((word) => word !== "")
|
|
32
|
+
.toSorted((left, right) => right.length - left.length)
|
|
33
|
+
.map(escapeRegExp)
|
|
34
|
+
.join("|");
|
|
35
|
+
|
|
36
|
+
const LATIN_END = /[a-z]$/iu;
|
|
37
|
+
const LATIN_START = /^[a-z]/iu;
|
|
38
|
+
|
|
39
|
+
const numberOf = (written: string, numberWords: readonly string[]): number => {
|
|
40
|
+
const position = numberWords.findIndex((word) => word.toLowerCase() === written.toLowerCase());
|
|
41
|
+
return position >= 0 ? position + 1 : Number(written.normalize("NFKC"));
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/** A digit number not inside a longer one, or a number word standing as a word of its own. */
|
|
45
|
+
const numberPattern = (numberWords: readonly string[]): string => {
|
|
46
|
+
const words = alternation(numberWords);
|
|
47
|
+
const digits = "(?<![\\p{N}.,./⁄])(?<digits>\\p{Nd}+(?:[..]\\p{Nd}+)?)";
|
|
48
|
+
return words === "" ? digits : `(?:${digits}|(?<![\\p{L}\\p{N}\\p{Pd}])(?<word>${words}))`;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const SEPARATOR = "[\\s\\-‐]?";
|
|
52
|
+
|
|
53
|
+
type Read = { readonly span: Span; readonly minutes: number; readonly unit: number };
|
|
54
|
+
|
|
55
|
+
/** A Latin unit runs on into a longer word ("8 hoursx") or a letter-led tail: not a length. */
|
|
56
|
+
const runsOn = (text: string, end: number): boolean => LATIN_END.test(text.slice(end - 1, end)) && LATIN_START.test(text.charAt(end));
|
|
57
|
+
|
|
58
|
+
const DECIMAL_BASE = 10;
|
|
59
|
+
|
|
60
|
+
/** 7.5 hours is written to a tenth of an hour, 6 minutes. */
|
|
61
|
+
const hourStep = (number: string | undefined): number => {
|
|
62
|
+
const decimals = (number ?? "").normalize("NFKC").split(".")[1]?.length ?? 0;
|
|
63
|
+
return MINUTES_PER_HOUR / DECIMAL_BASE ** decimals;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const smallestUnit = (number: string | undefined, minutes: string | undefined, half: string | undefined): number => {
|
|
67
|
+
if (minutes !== undefined) return 1;
|
|
68
|
+
return half === undefined ? hourStep(number) : Math.min(HALF_HOUR, hourStep(number));
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const hourLengths = (text: string, words: LengthWords): Read[] => {
|
|
72
|
+
const [hours, minutes, halves] = [alternation(words.hourUnits), alternation(words.minuteUnits), alternation(words.halves)];
|
|
73
|
+
if (hours === "") return [];
|
|
74
|
+
const minutePart = minutes === "" ? "" : `|\\s?(?<extra>\\p{Nd}{1,2})\\s?(?:${minutes})`;
|
|
75
|
+
const halfPart = halves === "" ? "" : `|\\s?(?<half>${halves})`;
|
|
76
|
+
const tail = minutePart === "" && halfPart === "" ? "" : `(?:${minutePart.slice(1)}${halfPart})?`;
|
|
77
|
+
const pattern = new RegExp(`${numberPattern(words.numberWords)}${SEPARATOR}(?:${hours})${tail}`, "giu");
|
|
78
|
+
return [...text.matchAll(pattern)].flatMap((match): Read[] => {
|
|
79
|
+
const end = match.index + match[0].length;
|
|
80
|
+
if (runsOn(text, end)) return [];
|
|
81
|
+
const { digits, word, extra, half } = match.groups ?? {};
|
|
82
|
+
const amount = numberOf(digits ?? word ?? "", words.numberWords);
|
|
83
|
+
const extraMinutes = extra === undefined ? 0 : Number(extra.normalize("NFKC"));
|
|
84
|
+
const halfMinutes = half === undefined ? 0 : HALF_HOUR;
|
|
85
|
+
return [{ span: { start: match.index, end }, minutes: amount * MINUTES_PER_HOUR + extraMinutes + halfMinutes, unit: smallestUnit(digits, extra, half) }];
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const minuteLengths = (text: string, words: LengthWords): Read[] => {
|
|
90
|
+
const minutes = alternation(words.minuteUnits);
|
|
91
|
+
if (minutes === "") return [];
|
|
92
|
+
const pattern = new RegExp(`${numberPattern(words.numberWords)}${SEPARATOR}(?:${minutes})`, "giu");
|
|
93
|
+
return [...text.matchAll(pattern)].flatMap((match): Read[] => {
|
|
94
|
+
const end = match.index + match[0].length;
|
|
95
|
+
return runsOn(text, end)
|
|
96
|
+
? []
|
|
97
|
+
: [{ span: { start: match.index, end }, minutes: numberOf(match.groups?.["digits"] ?? match.groups?.["word"] ?? "", words.numberWords), unit: 1 }];
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const overlaps = (left: Span, right: Span): boolean => left.start < right.end && right.start < left.end;
|
|
102
|
+
|
|
103
|
+
/** Lengths of time in hours and minutes, in the order written. A minutes-only reading inside an hours one is dropped. */
|
|
104
|
+
export const timeLengths = (text: string, words: LengthWords): TimeLength[] => {
|
|
105
|
+
const hours = hourLengths(text, words);
|
|
106
|
+
const minutes = minuteLengths(text, words).filter((read) => !hours.some((hour) => overlaps(hour.span, read.span)));
|
|
107
|
+
return [...hours, ...minutes]
|
|
108
|
+
.filter((read) => Number.isFinite(read.minutes) && read.minutes > 0)
|
|
109
|
+
.map((read) => ({ ...read.span, minutes: read.minutes, unit: read.unit }))
|
|
110
|
+
.toSorted((left, right) => left.start - right.start);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const longestFirst = (marks: readonly Mark[]): Mark[] => marks.toSorted((left, right) => right.pattern.length - left.pattern.length);
|
|
114
|
+
|
|
115
|
+
const LABEL_GAP_BEFORE = new Set([" ", "\t", "\u3000", ":", ":"]);
|
|
116
|
+
const LABEL_GAP_AFTER = /^\s*/u;
|
|
117
|
+
const LOOK_AROUND = 40;
|
|
118
|
+
|
|
119
|
+
const lower = (text: string): string => text.toLowerCase();
|
|
120
|
+
|
|
121
|
+
/** Where the separators that may stand between a label and its length (休憩:1時間、Break: 1 hour) begin, reading back from at. */
|
|
122
|
+
const gapStart = (text: string, at: number): number => {
|
|
123
|
+
let start = at;
|
|
124
|
+
while (start > 0 && LABEL_GAP_BEFORE.has(text.charAt(start - 1))) start -= 1;
|
|
125
|
+
return start;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** Whether pattern is written as a word at offset at, case aside. Reads a window around it, not the whole text. */
|
|
129
|
+
const isLabelAt = (text: string, at: number, pattern: string): boolean => {
|
|
130
|
+
if (at < 0) return false;
|
|
131
|
+
const from = Math.max(0, at - 1);
|
|
132
|
+
return isWordAt(lower(text.slice(from, at + pattern.length + 1)), at - from, lower(pattern));
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** The label written right before span (after separators only), or undefined. */
|
|
136
|
+
const labelBefore = (text: string, span: Span, marks: readonly Mark[]): Span | undefined => {
|
|
137
|
+
const end = gapStart(text, span.start);
|
|
138
|
+
if (span.start - end > LOOK_AROUND) return undefined;
|
|
139
|
+
const found = longestFirst(marks.filter((mark) => (mark.position ?? "before") === "before")).find((mark) =>
|
|
140
|
+
isLabelAt(text, end - mark.pattern.length, mark.pattern),
|
|
141
|
+
);
|
|
142
|
+
return found === undefined ? undefined : { start: end - found.pattern.length, end };
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/** The label written right after span (after white space only), or undefined. */
|
|
146
|
+
const labelAfter = (text: string, span: Span, marks: readonly Mark[]): Span | undefined => {
|
|
147
|
+
const start = span.end + (LABEL_GAP_AFTER.exec(text.slice(span.end, span.end + LOOK_AROUND))?.[0].length ?? 0);
|
|
148
|
+
const found = longestFirst(marks.filter((mark) => mark.position === "after")).find((mark) => isLabelAt(text, start, mark.pattern));
|
|
149
|
+
return found === undefined ? undefined : { start, end: start + found.pattern.length };
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/** The label beside span, before or after it. */
|
|
153
|
+
export const labelOf = (text: string, span: Span, marks: readonly Mark[]): Span | undefined => labelBefore(text, span, marks) ?? labelAfter(text, span, marks);
|
|
154
|
+
|
|
155
|
+
/** Where the words of marks are written inside scope. */
|
|
156
|
+
export const mentionsIn = (text: string, scope: Span, marks: readonly Mark[]): Span[] => {
|
|
157
|
+
const inside = lower(text.slice(scope.start, scope.end));
|
|
158
|
+
return [...new Set(marks.map((mark) => lower(mark.pattern)))].flatMap((pattern) =>
|
|
159
|
+
wordOffsets(inside, pattern).map((at) => ({ start: scope.start + at, end: scope.start + at + pattern.length })),
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const within = (inner: Span, outer: Span): boolean => inner.start >= outer.start && inner.end <= outer.end;
|
|
164
|
+
|
|
165
|
+
const LIST_ITEM = /^\s*(?:[-*+・•]|\d{1,3}[.)])\s/u;
|
|
166
|
+
|
|
167
|
+
type Paragraph = { readonly span: Span; readonly lines: readonly Span[]; readonly listed: boolean };
|
|
168
|
+
|
|
169
|
+
type Run = { readonly lines: Span[]; listed: boolean };
|
|
170
|
+
|
|
171
|
+
/** Runs of non-blank lines. listed: every line of the run is a list item in the source. */
|
|
172
|
+
const paragraphsOf = (text: string, source: string): Paragraph[] => {
|
|
173
|
+
const listedLines = linesOf(source).map((line) => LIST_ITEM.test(line.text));
|
|
174
|
+
const runs: Run[] = [{ lines: [], listed: true }];
|
|
175
|
+
linesOf(text).forEach((line, index) => {
|
|
176
|
+
const run = runs[runs.length - 1];
|
|
177
|
+
if (line.text.trim() === "") runs.push({ lines: [], listed: true });
|
|
178
|
+
else if (run !== undefined) {
|
|
179
|
+
run.lines.push({ start: line.start, end: line.start + line.text.length });
|
|
180
|
+
run.listed = run.listed && listedLines[index] === true;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
return runs.flatMap(({ lines, listed }) => {
|
|
184
|
+
const [first, last] = [lines[0], lines[lines.length - 1]];
|
|
185
|
+
return first === undefined || last === undefined ? [] : [{ span: { start: first.start, end: last.end }, lines, listed }];
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const clipped = (inner: Span, outer: Span): Span => ({ start: Math.max(inner.start, outer.start), end: Math.min(inner.end, outer.end) });
|
|
190
|
+
|
|
191
|
+
/** A sentence that ends on a clock time ("5:30 p.m. (a one-hour break") runs on into the next. */
|
|
192
|
+
const joinedAtTimes = (sentences: readonly Span[], times: readonly Span[]): Span[] =>
|
|
193
|
+
sentences.reduce<Span[]>((joined, sentence) => {
|
|
194
|
+
const last = joined[joined.length - 1];
|
|
195
|
+
if (last !== undefined && times.some((time) => time.start < last.end && last.end <= time.end)) {
|
|
196
|
+
joined[joined.length - 1] = { start: last.start, end: sentence.end };
|
|
197
|
+
} else joined.push(sentence);
|
|
198
|
+
return joined;
|
|
199
|
+
}, []);
|
|
200
|
+
|
|
201
|
+
/** The sentences of a line, clipped to it; the line itself when no sentence lies on it. */
|
|
202
|
+
const sentencesOn = (line: Span, sentences: readonly Span[]): Span[] => {
|
|
203
|
+
const on = sentences.filter((sentence) => overlaps(sentence, line)).map((sentence) => clipped(sentence, line));
|
|
204
|
+
return on.length === 0 ? [line] : on;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The stretches a statement is read in: each sentence of a line, and a list whose items say it together (勤務時間 /
|
|
209
|
+
* 休憩 / 実働 on three items). read returns undefined when a stretch does not hold exactly one statement; a list that
|
|
210
|
+
* reads as one is not read again line by line.
|
|
211
|
+
*/
|
|
212
|
+
export const readScopes = <T>(
|
|
213
|
+
text: string,
|
|
214
|
+
source: string,
|
|
215
|
+
split: { readonly sentences: readonly Span[]; readonly times: readonly Span[] },
|
|
216
|
+
read: (scope: Span) => T | undefined,
|
|
217
|
+
): T[] => {
|
|
218
|
+
const sentences = joinedAtTimes(split.sentences, split.times);
|
|
219
|
+
return paragraphsOf(text, source).flatMap((paragraph) => {
|
|
220
|
+
const whole = paragraph.listed && paragraph.lines.length > 1 ? read(paragraph.span) : undefined;
|
|
221
|
+
if (whole !== undefined) return [whole];
|
|
222
|
+
return paragraph.lines.flatMap((line) =>
|
|
223
|
+
sentencesOn(line, sentences).flatMap((scope) => {
|
|
224
|
+
const result = read(scope);
|
|
225
|
+
return result === undefined ? [] : [result];
|
|
226
|
+
}),
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
import { escapeRegExp } from "../orthography.ts";
|
|
3
|
+
|
|
4
|
+
export type UnitCount = Span & { readonly amount: number };
|
|
5
|
+
|
|
6
|
+
const LATIN_UNIT = /^[a-z]+$/iu;
|
|
7
|
+
|
|
8
|
+
/** 数字と単位(3泊、3 nights、15回)。英字の単位は語の切れ目まで。Pure. */
|
|
9
|
+
export const numeralCounts = (text: string, units: readonly string[]): UnitCount[] =>
|
|
10
|
+
units.flatMap((unit) => {
|
|
11
|
+
const tail = LATIN_UNIT.test(unit) ? "(?![\\p{L}\\p{N}])" : "";
|
|
12
|
+
const pattern = new RegExp(`(?<![\\p{N}.,])(\\p{Nd}+)\\s?${escapeRegExp(unit)}${tail}`, "giu");
|
|
13
|
+
return [...text.matchAll(pattern)].map((match) => ({
|
|
14
|
+
start: match.index,
|
|
15
|
+
end: match.index + match[0].length,
|
|
16
|
+
amount: Number((match[1] ?? "").normalize("NFKC")),
|
|
17
|
+
}));
|
|
18
|
+
});
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { Span } from "../plugin.ts";
|
|
2
|
+
import { escapeRegExp } from "../orthography.ts";
|
|
3
|
+
import type { Mark } from "../structure/time-marks.ts";
|
|
4
|
+
import { linesOf, type Line } from "../structure/lines.ts";
|
|
5
|
+
import { cellsOf, tablesOf } from "../facts/table-facts.ts";
|
|
6
|
+
import type { TimeLength } from "./time-lengths.ts";
|
|
7
|
+
import {
|
|
8
|
+
asciiDigits,
|
|
9
|
+
isLimited,
|
|
10
|
+
lastIndexAtOrBefore,
|
|
11
|
+
periodCounts,
|
|
12
|
+
windowStart,
|
|
13
|
+
type LimitWords,
|
|
14
|
+
type PeriodCount,
|
|
15
|
+
type PeriodCountWords,
|
|
16
|
+
type PeriodWord,
|
|
17
|
+
} from "./period-counts.ts";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A minimum interval between uses and a maximum count of uses in a period that cannot both hold, and a maximum count that
|
|
21
|
+
* differs from the times a day of the dosing table in the same section. Read in one paragraph holding exactly one
|
|
22
|
+
* minimum interval (4時間以上あけ, at least 4 hours between doses) and exactly one count with a limit (1日6回を超えて,
|
|
23
|
+
* no more than 6 doses a day). n uses at least h hours apart take (n - 1) × h hours from the first to the last; when
|
|
24
|
+
* that reaches the length of the period, the n-th use falls outside it. Pure.
|
|
25
|
+
*/
|
|
26
|
+
export type IntervalWords = PeriodCountWords &
|
|
27
|
+
LimitWords & {
|
|
28
|
+
/** What makes a length a minimum: before it (at least 4 hours) or right after it (4時間以上). */
|
|
29
|
+
readonly minimum: readonly Mark[];
|
|
30
|
+
/** What makes a minimum length an interval between uses, in the same sentence (間隔, between). */
|
|
31
|
+
readonly cues: readonly string[];
|
|
32
|
+
/** Table headings of a count of uses in a period (1日の服用回数, Times a day), with the period's hours. */
|
|
33
|
+
readonly columns: readonly PeriodWord[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type IntervalMismatch =
|
|
37
|
+
| { readonly kind: "interval"; readonly count: PeriodCount; readonly interval: TimeLength; readonly neededHours: number }
|
|
38
|
+
| { readonly kind: "table"; readonly count: PeriodCount; readonly column: string; readonly tableCount: number };
|
|
39
|
+
|
|
40
|
+
const MINUTES_PER_HOUR = 60;
|
|
41
|
+
|
|
42
|
+
const lower = (text: string): string => text.toLowerCase();
|
|
43
|
+
|
|
44
|
+
const SENTENCE_END = /[。.!?!?\n]|\.(?=\s|$)/u;
|
|
45
|
+
|
|
46
|
+
/** How far after a length its sentence is read, in characters. */
|
|
47
|
+
const SENTENCE_REACH = 120;
|
|
48
|
+
|
|
49
|
+
const sentenceAround = (text: string, span: Span): Span => {
|
|
50
|
+
const end = SENTENCE_END.exec(text.slice(span.end, span.end + SENTENCE_REACH));
|
|
51
|
+
return { start: windowStart(text, span.start, []), end: end === null ? Math.min(text.length, span.end + SENTENCE_REACH) : span.end + end.index };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const hasWord = (text: string, word: string): boolean => new RegExp(`(?<![a-z])${escapeRegExp(lower(word))}(?![a-z])`, "u").test(lower(text));
|
|
55
|
+
|
|
56
|
+
/** 4時間以上, at least 4 hours: a length with a minimum mark beside it and an interval word in its sentence. */
|
|
57
|
+
const isMinimumInterval = (text: string, length: TimeLength, words: IntervalWords): boolean => {
|
|
58
|
+
const sentence = sentenceAround(text, length);
|
|
59
|
+
const before = lower(text.slice(sentence.start, length.start)).trimEnd();
|
|
60
|
+
const after = lower(text.slice(length.end, sentence.end)).trimStart();
|
|
61
|
+
const marked = words.minimum.some((mark) =>
|
|
62
|
+
(mark.position ?? "after") === "after" ? after.startsWith(lower(mark.pattern)) : before.endsWith(lower(mark.pattern)),
|
|
63
|
+
);
|
|
64
|
+
return marked && words.cues.some((cue) => hasWord(text.slice(sentence.start, sentence.end), cue));
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const inside = (scope: Span) => (span: Span) => span.start >= scope.start && span.end <= scope.end;
|
|
68
|
+
|
|
69
|
+
type Statement = { readonly count: PeriodCount; readonly interval: TimeLength };
|
|
70
|
+
|
|
71
|
+
const statementIn = (scope: Span, counts: readonly PeriodCount[], intervals: readonly TimeLength[]): Statement | undefined => {
|
|
72
|
+
const [inCounts, inIntervals] = [counts.filter(inside(scope)), intervals.filter(inside(scope))];
|
|
73
|
+
const [count, interval] = [inCounts[0], inIntervals[0]];
|
|
74
|
+
return inCounts.length === 1 && inIntervals.length === 1 && count !== undefined && interval !== undefined ? { count, interval } : undefined;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const HEADING = /^ {0,3}#{1,6}\s/u;
|
|
78
|
+
|
|
79
|
+
const plain = (cell: string): string => lower(asciiDigits(cell).normalize("NFKC").replace(/[*_`]/gu, "").trim());
|
|
80
|
+
|
|
81
|
+
const COUNT_CELL = /^(\d+)(?:\s*[〜~\-–]\s*(\d+))?$/u;
|
|
82
|
+
|
|
83
|
+
/** A cell that holds only a count: 3, 3回, 2〜3回, three, twice. The larger end of a range. */
|
|
84
|
+
const cellCount = (cell: string, words: PeriodCountWords): number | undefined => {
|
|
85
|
+
const text = plain(cell);
|
|
86
|
+
const counter = words.counters.find((word) => text.endsWith(lower(word)));
|
|
87
|
+
const bare = counter === undefined ? text : text.slice(0, -counter.length).trim();
|
|
88
|
+
const digits = COUNT_CELL.exec(bare);
|
|
89
|
+
if (digits !== null) return Number(digits[2] ?? digits[1]);
|
|
90
|
+
return words.numberWords.find((word) => lower(word.pattern) === bare)?.value;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
type TableCount = { readonly at: number; readonly column: string; readonly count: number; readonly hours: number };
|
|
94
|
+
|
|
95
|
+
/** The largest count of each counted column, in every table. */
|
|
96
|
+
const tableCountsOf = (lines: readonly Line[], words: IntervalWords): TableCount[] =>
|
|
97
|
+
tablesOf(lines).flatMap(({ header, rows }) =>
|
|
98
|
+
cellsOf(header).flatMap((cell, index): TableCount[] => {
|
|
99
|
+
const column = words.columns.find((word) => plain(word.pattern) === plain(cell.text));
|
|
100
|
+
const values = rows.flatMap((row) => {
|
|
101
|
+
const value = cellCount(cellsOf(row)[index]?.text ?? "", words);
|
|
102
|
+
return value === undefined ? [] : [value];
|
|
103
|
+
});
|
|
104
|
+
if (column === undefined || values.length === 0) return [];
|
|
105
|
+
return [{ at: header.start, column: cell.text.trim(), count: Math.max(...values), hours: column.hours }];
|
|
106
|
+
}),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
type Layout = { readonly headings: readonly number[]; readonly tables: readonly TableCount[]; readonly length: number };
|
|
110
|
+
|
|
111
|
+
const layoutOf = (text: string, words: IntervalWords): Layout => {
|
|
112
|
+
const lines = linesOf(text);
|
|
113
|
+
return { headings: lines.filter((line) => HEADING.test(line.text)).map((line) => line.start), tables: tableCountsOf(lines, words), length: text.length };
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** The heading-delimited section that holds at. */
|
|
117
|
+
const sectionOf = (layout: Layout, at: number): Span => {
|
|
118
|
+
const index = lastIndexAtOrBefore(layout.headings, at);
|
|
119
|
+
return { start: layout.headings[index] ?? 0, end: layout.headings[index + 1] ?? layout.length };
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const tableMismatch = (layout: Layout, count: PeriodCount): IntervalMismatch | undefined => {
|
|
123
|
+
const section = sectionOf(layout, count.start);
|
|
124
|
+
const tables = layout.tables.filter((table) => table.at >= section.start && table.at < section.end && table.hours === count.hours);
|
|
125
|
+
const table = tables[0];
|
|
126
|
+
if (tables.length !== 1 || table === undefined || table.count === count.amount) return undefined;
|
|
127
|
+
return { kind: "table", count, column: table.column, tableCount: table.count };
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const mismatchOf = (layout: Layout, statement: Statement): IntervalMismatch | undefined => {
|
|
131
|
+
const { count, interval } = statement;
|
|
132
|
+
const neededMinutes = (count.amount - 1) * interval.minutes;
|
|
133
|
+
if (neededMinutes >= count.hours * MINUTES_PER_HOUR) return { kind: "interval", count, interval, neededHours: neededMinutes / MINUTES_PER_HOUR };
|
|
134
|
+
return tableMismatch(layout, count);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/** Limited counts that a minimum interval in the same paragraph, or the table of the section, contradicts. */
|
|
138
|
+
export const intervalCountMismatches = (written: string, scopes: readonly Span[], lengths: readonly TimeLength[], words: IntervalWords): IntervalMismatch[] => {
|
|
139
|
+
const text = asciiDigits(written);
|
|
140
|
+
const all = periodCounts(text, words);
|
|
141
|
+
const takenEnds = all.map((count) => count.end);
|
|
142
|
+
const counts = all.filter((count) => isLimited(text, count, windowStart(text, count.start, takenEnds), words));
|
|
143
|
+
const intervals = lengths.filter((length) => isMinimumInterval(text, length, words));
|
|
144
|
+
if (counts.length === 0 || intervals.length === 0) return [];
|
|
145
|
+
const layout = layoutOf(text, words);
|
|
146
|
+
return scopes.flatMap((scope) => {
|
|
147
|
+
const statement = statementIn(scope, counts, intervals);
|
|
148
|
+
const found = statement === undefined ? undefined : mismatchOf(layout, statement);
|
|
149
|
+
return found === undefined ? [] : [found];
|
|
150
|
+
});
|
|
151
|
+
};
|