chaffjs 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/time-lengths.d.ts.map +1 -1
- package/dist/derived/time-lengths.js +12 -9
- package/dist/derived/time-lengths.js.map +1 -1
- 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/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/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 +2 -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 +9 -1
- package/dist/detectors/derived-numbers.d.ts.map +1 -1
- package/dist/detectors/derived-numbers.js +2 -2
- 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 +25 -4
- 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/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/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/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/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/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/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 +22 -5
- 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 +1 -0
- package/dist/detectors/time-order.d.ts.map +1 -1
- package/dist/detectors/time-order.js +3 -1
- package/dist/detectors/time-order.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/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/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 +3 -2
- package/dist/name-variants.d.ts.map +1 -1
- package/dist/name-variants.js +4 -7
- package/dist/name-variants.js.map +1 -1
- 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 +5 -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/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/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-outside-period.d.ts +3 -1
- package/dist/structure/date-outside-period.d.ts.map +1 -1
- package/dist/structure/date-outside-period.js +8 -5
- package/dist/structure/date-outside-period.js.map +1 -1
- package/dist/structure/date-range.d.ts +1 -0
- package/dist/structure/date-range.d.ts.map +1 -1
- package/dist/structure/date-range.js +1 -1
- 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/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/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/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-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/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/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/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/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 +8 -2
- package/rules/no-em-dash.yaml +2 -2
- 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/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/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/time-lengths.ts +12 -9
- package/src/derived/use-interval.ts +151 -0
- package/src/detectors/announced-count.ts +1 -0
- package/src/detectors/capacity-runtime.ts +128 -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 +2 -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 +3 -3
- 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 +32 -6
- package/src/detectors/orthographic-variant.ts +78 -9
- package/src/detectors/orthography.ts +16 -6
- 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/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/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/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/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/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 +30 -5
- package/src/detectors/structure.ts +1 -1
- package/src/detectors/technical-docs.ts +12 -0
- package/src/detectors/time-order.ts +5 -1
- 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/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/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 +8 -7
- package/src/phone-numbers.ts +26 -6
- package/src/place-names.ts +53 -14
- package/src/plugin.ts +2 -0
- package/src/preamble-paragraphs.ts +21 -2
- package/src/profile/parse.ts +10 -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/counted-amounts.ts +5 -4
- package/src/structure/date-outside-period.ts +11 -6
- package/src/structure/date-range.ts +1 -1
- 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/measured-amounts.ts +138 -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/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-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/walk-time.ts +285 -0
package/src/detectors/signals.ts
CHANGED
|
@@ -371,7 +371,9 @@ export const concreteEvidence: Detector = (doc, options): Finding[] => {
|
|
|
371
371
|
* ダッシュの多用。英語では正当な用法が多いが、2026 年時点で最も知られた生成文のシグナルでもある。
|
|
372
372
|
* 日本語の組版ではそもそも扱いが難しい。どちらが正しいかは severity の言語別指定で分ける。spec §12.4。
|
|
373
373
|
*/
|
|
374
|
-
|
|
374
|
+
// An en dash between two digits (45–58, 10:00–10:15) is a range mark, the typographer's correct choice, not a dash that
|
|
375
|
+
// sets off words.
|
|
376
|
+
const DASH = /[\u2014\u2015]|(?<!\p{Nd})\u2013|\u2013(?!\p{Nd})/gu;
|
|
375
377
|
|
|
376
378
|
export const dashDensity: Detector = (doc, options): Finding[] => {
|
|
377
379
|
const hits = findIn(doc, DASH);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// A stated total time (所要時間:60分, Total time: 1 hr) that is not the sum of the times written in the numbered steps. The
|
|
2
|
+
// units, the total labels, the rough-figure marks and the marks of a time that is not a step's own come from the language packages.
|
|
3
|
+
import { stepTimeSlip, type StepTimeWords } from "../structure/step-times.ts";
|
|
4
|
+
import type { Mark } from "../structure/time-marks.ts";
|
|
5
|
+
import { quoteAt } from "./structure-tree.ts";
|
|
6
|
+
import type { Detector, Finding, ProseDocument } from "../plugin.ts";
|
|
7
|
+
|
|
8
|
+
const SECONDS_PER_HOUR = 3600;
|
|
9
|
+
const SECONDS_PER_MINUTE = 60;
|
|
10
|
+
|
|
11
|
+
const patternsOf = (doc: ProseDocument, id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
|
|
12
|
+
|
|
13
|
+
const marksOf = (doc: ProseDocument, id: string): Mark[] =>
|
|
14
|
+
(doc.lexicons[id] ?? []).map((entry) => ({ pattern: entry.pattern, position: entry.position, group: entry.group }));
|
|
15
|
+
|
|
16
|
+
const unitsWeighing = (doc: ProseDocument, seconds: number): string[] =>
|
|
17
|
+
(doc.lexicons["unit-time"] ?? []).filter((entry) => entry.weight === seconds).map((entry) => entry.pattern);
|
|
18
|
+
|
|
19
|
+
const RANGE_GROUP = "range";
|
|
20
|
+
|
|
21
|
+
const wordsOf = (doc: ProseDocument): StepTimeWords => {
|
|
22
|
+
const notOwn = marksOf(doc, "step-time-not-own");
|
|
23
|
+
return {
|
|
24
|
+
lengths: {
|
|
25
|
+
hourUnits: unitsWeighing(doc, SECONDS_PER_HOUR),
|
|
26
|
+
minuteUnits: unitsWeighing(doc, SECONDS_PER_MINUTE),
|
|
27
|
+
halves: patternsOf(doc, "length-half"),
|
|
28
|
+
numberWords: patternsOf(doc, "count-number"),
|
|
29
|
+
},
|
|
30
|
+
totals: marksOf(doc, "step-time-total"),
|
|
31
|
+
approximate: marksOf(doc, "approximate-marker"),
|
|
32
|
+
notOwn: notOwn.filter((mark) => mark.group !== RANGE_GROUP),
|
|
33
|
+
rangeJoiners: notOwn.filter((mark) => mark.group === RANGE_GROUP).map((mark) => mark.pattern),
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const stepTimeSumMismatch: Detector = (doc): Finding[] => {
|
|
38
|
+
const slip = stepTimeSlip(doc.source, wordsOf(doc));
|
|
39
|
+
if (slip === undefined) return [];
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
rule: "step-time-sum-mismatch",
|
|
43
|
+
severity: "warning",
|
|
44
|
+
line: 0,
|
|
45
|
+
column: 0,
|
|
46
|
+
quote: quoteAt(doc.source, slip.total.start),
|
|
47
|
+
values: {
|
|
48
|
+
written: doc.source.slice(slip.total.start, slip.total.end),
|
|
49
|
+
sum: slip.sum,
|
|
50
|
+
steps: slip.steps,
|
|
51
|
+
offset: slip.total.start,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
};
|
|
@@ -6,8 +6,11 @@ import { weekdayMismatches } from "../structure/weekday.ts";
|
|
|
6
6
|
import { documentDateOf } from "./document-date.ts";
|
|
7
7
|
import { dateOrderBreaks } from "../structure/date-order.ts";
|
|
8
8
|
import { totalMismatches, type Amount } from "../structure/total.ts";
|
|
9
|
+
import { isTotalLine } from "../structure/total-line.ts";
|
|
9
10
|
import { proseTotalMismatches } from "../structure/prose-total.ts";
|
|
10
11
|
import { countedAmounts, type SummedCounter } from "../structure/counted-amounts.ts";
|
|
12
|
+
import { measuredAmounts, type MeasureMarks, type SummedMeasure } from "../structure/measured-amounts.ts";
|
|
13
|
+
import { measureUnitsOf } from "../facts/measure-units.ts";
|
|
11
14
|
import { rangeFrameOf, reversedRanges, type DatedSpan, type RangeWords } from "../structure/date-range.ts";
|
|
12
15
|
import { afterLabel } from "../structure/stated-period.ts";
|
|
13
16
|
import { percentSumMismatches, type ShareWords } from "../structure/percent-sum.ts";
|
|
@@ -184,11 +187,33 @@ const summableAmountsOf = (doc: ProseDocument, tree: NonNullable<ProseDocument["
|
|
|
184
187
|
return [...known, ...countedAmounts(doc.source, summedCountersOf(doc), known)].toSorted((left, right) => left.offset - right.offset);
|
|
185
188
|
};
|
|
186
189
|
|
|
187
|
-
/**
|
|
190
|
+
/** 合計の行で足す量の種類。換算の倍率は語彙表の weight。 */
|
|
191
|
+
const SUMMED_DIMENSIONS = ["unit-mass", "unit-volume", "unit-length"] as const;
|
|
192
|
+
|
|
193
|
+
/** 足す単位。零点のずれた単位(°F)は足せないので除く。 */
|
|
194
|
+
const summedMeasuresOf = (doc: ProseDocument): SummedMeasure[] =>
|
|
195
|
+
measureUnitsOf(doc).filter((unit) => unit.zero === 0 && SUMMED_DIMENSIONS.some((dimension) => dimension === unit.dimension));
|
|
196
|
+
|
|
197
|
+
const positionedOf = (doc: ProseDocument, lexicon: string, position: "before" | "after"): string[] =>
|
|
198
|
+
(doc.lexicons[lexicon] ?? []).filter((entry) => (entry.position ?? "before") === position).map((entry) => entry.pattern);
|
|
199
|
+
|
|
200
|
+
const measureMarksOf = (doc: ProseDocument): MeasureMarks => ({
|
|
201
|
+
roughBefore: positionedOf(doc, "approximate-marker", "before"),
|
|
202
|
+
roughAfter: positionedOf(doc, "approximate-marker", "after"),
|
|
203
|
+
connectors: patternsOf(doc, "range-connector"),
|
|
204
|
+
perMarks: patternsOf(doc, "measure-per-mark"),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
/** 表と箇条書きの合計で足す数量。金額と助数詞の数に、単位の付いた量(300 mg、0.5 g)を加える。 */
|
|
208
|
+
const lineAmountsOf = (doc: ProseDocument, amounts: readonly Amount[]): Amount[] =>
|
|
209
|
+
[...amounts, ...measuredAmounts(doc.source, summedMeasuresOf(doc), measureMarksOf(doc), amounts)].toSorted((left, right) => left.offset - right.offset);
|
|
210
|
+
|
|
211
|
+
/** 合計の行と内訳の行、文の中の合計と内訳。同じ金額は一度だけ言う。単位の付いた量は、表と箇条書きの合計だけで足す。 */
|
|
188
212
|
const totalIssues = (doc: ProseDocument, tree: NonNullable<ProseDocument["structure"]>): StructureIssue[] => {
|
|
189
213
|
const amounts = summableAmountsOf(doc, tree);
|
|
190
|
-
const
|
|
191
|
-
const
|
|
214
|
+
const words = { labels: patternsOf(doc, "total-label"), qualifiers: doc.lexicons["total-label-qualifier"] ?? [] };
|
|
215
|
+
const lines = totalMismatches(doc.source, lineAmountsOf(doc, amounts), (text) => isTotalLine(text, words));
|
|
216
|
+
const phrases = {
|
|
192
217
|
totals: patternsOf(doc, "total-phrase"),
|
|
193
218
|
breakdowns: patternsOf(doc, "breakdown-phrase"),
|
|
194
219
|
discounts: patternsOf(doc, "discount-word"),
|
|
@@ -198,7 +223,7 @@ const totalIssues = (doc: ProseDocument, tree: NonNullable<ProseDocument["struct
|
|
|
198
223
|
doc.source,
|
|
199
224
|
doc.sentences.map((sentence) => sentence.span),
|
|
200
225
|
amounts,
|
|
201
|
-
|
|
226
|
+
phrases,
|
|
202
227
|
);
|
|
203
228
|
return [...lines, ...prose.filter((issue) => !lines.some((line) => line.offset === issue.offset))];
|
|
204
229
|
};
|
|
@@ -219,7 +244,7 @@ export const totalMismatch: Detector = (doc): Finding[] =>
|
|
|
219
244
|
values: { ...issue.values, offset: issue.offset },
|
|
220
245
|
}));
|
|
221
246
|
|
|
222
|
-
const rangeWordsOf = (doc: ProseDocument): RangeWords => ({
|
|
247
|
+
export const rangeWordsOf = (doc: ProseDocument): RangeWords => ({
|
|
223
248
|
connectors: (doc.lexicons["range-connector"] ?? []).map((entry) => entry.pattern),
|
|
224
249
|
openers: (doc.lexicons["range-opener"] ?? []).map((entry) => entry.pattern),
|
|
225
250
|
closers: (doc.lexicons["range-closer"] ?? []).map((entry) => entry.pattern),
|
|
@@ -138,7 +138,7 @@ const withoutDateStamps = (doc: ProseDocument, paragraphs: readonly Paragraph[])
|
|
|
138
138
|
export const preambleLength: Detector = (doc, options): Finding[] => {
|
|
139
139
|
const body = bodySectionOf(doc.sections);
|
|
140
140
|
if (body === undefined) return [];
|
|
141
|
-
const all = preambleParagraphs(doc.paragraphs, body.span.start);
|
|
141
|
+
const all = preambleParagraphs(doc.paragraphs, body.span.start, doc.source);
|
|
142
142
|
if (all.length <= options.limit) return [];
|
|
143
143
|
const before = withoutDateStamps(doc, all);
|
|
144
144
|
if (before.length <= options.limit) return [];
|
|
@@ -191,6 +191,17 @@ const isInflected = (token: Token): boolean =>
|
|
|
191
191
|
token.lemma !== undefined && token.lemma !== "" && token.surface.toLowerCase() !== token.lemma && token.features?.["VerbForm"] !== "Part";
|
|
192
192
|
|
|
193
193
|
const NOMINAL = new Set(["NOUN", "PROPN"]);
|
|
194
|
+
/** What an adjective at the head of a subject goes on to: the noun it describes, or another word describing it. */
|
|
195
|
+
const DESCRIBED = new Set(["NOUN", "PROPN", "ADJ"]);
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* An adjective opening a subject is followed by what it describes (Each user, Large files). One followed by a preposition
|
|
199
|
+
* or an article is an imperative the tagger read as an adjective (Bake in the oven, Open the file).
|
|
200
|
+
*/
|
|
201
|
+
export const opensWithMisreadImperative = (tokens: readonly Token[]): boolean => {
|
|
202
|
+
const [head, next] = tokens.filter((token) => !SKIPPED.has(token.pos));
|
|
203
|
+
return head?.pos === "ADJ" && !DESCRIBED.has(next?.pos ?? "");
|
|
204
|
+
};
|
|
194
205
|
|
|
195
206
|
/** A determiner right after a noun opens a clause inside the phrase (a buffer that is big): the verb after it is the clause's, not a subject's. */
|
|
196
207
|
const opensClause = (words: readonly Token[]): boolean => words.some((token, at) => at > 0 && token.pos === "DET" && NOMINAL.has(words[at - 1]?.pos ?? ""));
|
|
@@ -204,6 +215,7 @@ export const isStatement = (tokens: readonly Token[]): boolean => {
|
|
|
204
215
|
const words = tokens.filter((token) => !SKIPPED.has(token.pos));
|
|
205
216
|
const [head, ...rest] = words;
|
|
206
217
|
if (head === undefined || words.length < MIN_STEP_TOKENS || !SUBJECT_HEAD.has(head.pos)) return false;
|
|
218
|
+
if (opensWithMisreadImperative(words)) return false;
|
|
207
219
|
const verbAt = rest.findIndex((token) => PREDICATE.has(token.pos));
|
|
208
220
|
const verb = rest[verbAt];
|
|
209
221
|
const subject = [head, ...rest.slice(0, verbAt)];
|
|
@@ -7,7 +7,8 @@ import { scheduleDays, timeOrderBreaks } from "../structure/time-order.ts";
|
|
|
7
7
|
import { arrivalOffsets, arrivalsBeforeDeparture } from "../structure/leg-times.ts";
|
|
8
8
|
import { shortConnections, type TravelLength, type TravelWords } from "../structure/connection-times.ts";
|
|
9
9
|
import { timeLengths } from "../derived/time-lengths.ts";
|
|
10
|
-
import {
|
|
10
|
+
import { reversedTimeRanges } from "../structure/time-range.ts";
|
|
11
|
+
import { quoteAt, rangeWordsOf } from "./structure-tree.ts";
|
|
11
12
|
|
|
12
13
|
// 一日の予定の時刻の順番(time-order)、発より前の着(arrival-before-departure)、移動の時間より短い乗り継ぎ(connection-time-short)。時刻は compare/clock-time.ts が読み、
|
|
13
14
|
// 発着・翌日・時間帯の語は言語パッケージの語彙表(leg-mark、leg-column、day-shift-mark、time-zone、local-time-mark)から取る。
|
|
@@ -49,6 +50,9 @@ const detectorOf =
|
|
|
49
50
|
|
|
50
51
|
export const timeOrder: Detector = detectorOf("time-order", timeOrderBreaks);
|
|
51
52
|
export const arrivalBeforeDeparture: Detector = detectorOf("arrival-before-departure", arrivalsBeforeDeparture);
|
|
53
|
+
export const timeRangeReversed: Detector = detectorOf("time-range-reversed", (source, times, _dates, words, doc) =>
|
|
54
|
+
reversedTimeRanges(source, times, rangeWordsOf(doc), words),
|
|
55
|
+
);
|
|
52
56
|
|
|
53
57
|
const SECONDS_PER_HOUR = 3600;
|
|
54
58
|
const SECONDS_PER_MINUTE = 60;
|
|
@@ -1,55 +1,67 @@
|
|
|
1
1
|
import type { Detector, Finding, ProseDocument, StructureNode } from "../plugin.ts";
|
|
2
|
-
import { nameSpans } from "../compare/proper-nouns.ts";
|
|
3
|
-
import { overlapsAny, spanIndex } from "../compare/spans.ts";
|
|
4
|
-
import { factValues, type FactValue } from "../facts/fact-values.ts";
|
|
5
2
|
import { labelledFacts } from "../facts/labelled-facts.ts";
|
|
6
3
|
import { tableFacts } from "../facts/table-facts.ts";
|
|
7
4
|
import { scopedFacts } from "../facts/fact-scope.ts";
|
|
8
|
-
import {
|
|
5
|
+
import type { Measured } from "../facts/measures.ts";
|
|
9
6
|
import { unitConflicts } from "../facts/unit-conflicts.ts";
|
|
7
|
+
import { unitPairs } from "../facts/unit-equivalents.ts";
|
|
8
|
+
import { itemConflicts } from "../facts/item-amounts.ts";
|
|
10
9
|
import { factWordsOf } from "./fact-consistency.ts";
|
|
10
|
+
import { measuredOf, valuesWith } from "./measured-facts.ts";
|
|
11
11
|
import { quoteAt } from "./structure-tree.ts";
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
const DIMENSIONS = ["unit-length", "unit-mass", "unit-time", "unit-volume", "unit-data"] as const;
|
|
13
|
+
const patternsOf = (doc: ProseDocument, id: string): string[] => (doc.lexicons[id] ?? []).map((entry) => entry.pattern);
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
DIMENSIONS.flatMap((dimension) => {
|
|
18
|
-
const factors = new Map<string, number[]>();
|
|
19
|
-
(doc.lexicons[dimension] ?? []).forEach((entry) => {
|
|
20
|
-
if (entry.weight !== undefined) factors.set(entry.pattern, [...(factors.get(entry.pattern) ?? []), entry.weight]);
|
|
21
|
-
});
|
|
22
|
-
return [...factors.entries()].map(([pattern, weights]) => ({ pattern, dimension, factors: weights }));
|
|
23
|
-
});
|
|
15
|
+
type Mismatch = { readonly label: string; readonly measured: Measured; readonly other: Measured; readonly variant?: string };
|
|
24
16
|
|
|
25
|
-
/**
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
17
|
+
/** 同じ範囲の同じ名前の量(「距離:5 km」と「距離:3000 m」)。名前と量のあいだの目安の語(「重さは約1.2kg」)は外して読む。 */
|
|
18
|
+
const labelledMismatches = (doc: ProseDocument, tree: StructureNode, measured: readonly Measured[]): Mismatch[] => {
|
|
19
|
+
const byStart = new Map(measured.map((value) => [value.start, value]));
|
|
20
|
+
const values = valuesWith(tree, doc, measured);
|
|
21
|
+
const words = { ...factWordsOf(doc), valueLeads: patternsOf(doc, "measure-value-lead") };
|
|
22
|
+
const facts = [...labelledFacts(doc.source, values, words), ...tableFacts(doc.source, values)];
|
|
23
|
+
const scoped = scopedFacts(facts, tree, doc.source, []);
|
|
24
|
+
return unitConflicts(scoped, (fact) => byStart.get(fact.value.start), doc.source).map((conflict) => ({
|
|
25
|
+
label: conflict.fact.label,
|
|
26
|
+
measured: conflict.measured,
|
|
27
|
+
other: conflict.other,
|
|
28
|
+
}));
|
|
30
29
|
};
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
const mismatchesOf = (doc: ProseDocument, tree: StructureNode, measured: readonly Measured[]): Mismatch[] => [
|
|
32
|
+
...labelledMismatches(doc, tree, measured),
|
|
33
|
+
...unitPairs(doc.source, measured, patternsOf(doc, "unit-equivalent-hedge")).map((pair) => ({ label: "", ...pair, variant: "bracket" })),
|
|
34
|
+
...itemConflicts(doc.source, measured, { links: patternsOf(doc, "unit-item-link"), determiners: patternsOf(doc, "fact-label-drop") }).map((conflict) => ({
|
|
35
|
+
label: conflict.name,
|
|
36
|
+
measured: conflict.measured,
|
|
37
|
+
other: conflict.other,
|
|
38
|
+
variant: "item",
|
|
39
|
+
})),
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const findingOf = (doc: ProseDocument, mismatch: Mismatch): Finding => ({
|
|
43
|
+
rule: "unit-mismatch",
|
|
44
|
+
severity: "warning",
|
|
45
|
+
line: 0,
|
|
46
|
+
column: 0,
|
|
47
|
+
quote: quoteAt(doc.source, mismatch.measured.start),
|
|
48
|
+
values: {
|
|
49
|
+
label: mismatch.label,
|
|
50
|
+
value: doc.source.slice(mismatch.measured.start, mismatch.measured.end),
|
|
51
|
+
other: doc.source.slice(mismatch.other.start, mismatch.other.end),
|
|
52
|
+
offset: mismatch.measured.start,
|
|
53
|
+
},
|
|
54
|
+
...(mismatch.variant === undefined ? {} : { variant: mismatch.variant }),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** 同じ量を、違う単位で書いて、換算すると合わない。一つの量は一度だけ言う。 */
|
|
33
58
|
export const unitMismatch: Detector = (doc): Finding[] => {
|
|
34
59
|
const tree = doc.structure;
|
|
35
60
|
if (tree === undefined) return [];
|
|
36
|
-
const measured =
|
|
61
|
+
const measured = measuredOf(doc);
|
|
37
62
|
if (measured.length === 0) return [];
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return unitConflicts(scoped, (fact) => byStart.get(fact.value.start)).map((conflict) => ({
|
|
43
|
-
rule: "unit-mismatch",
|
|
44
|
-
severity: "warning",
|
|
45
|
-
line: 0,
|
|
46
|
-
column: 0,
|
|
47
|
-
quote: quoteAt(doc.source, conflict.measured.start),
|
|
48
|
-
values: {
|
|
49
|
-
label: conflict.fact.label,
|
|
50
|
-
value: doc.source.slice(conflict.measured.start, conflict.measured.end),
|
|
51
|
-
other: doc.source.slice(conflict.other.start, conflict.other.end),
|
|
52
|
-
offset: conflict.measured.start,
|
|
53
|
-
},
|
|
54
|
-
}));
|
|
63
|
+
const mismatches = mismatchesOf(doc, tree, measured);
|
|
64
|
+
return mismatches
|
|
65
|
+
.filter((mismatch, index) => mismatches.findIndex((other) => other.measured.start === mismatch.measured.start) === index)
|
|
66
|
+
.map((mismatch) => findingOf(doc, mismatch));
|
|
55
67
|
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// unlisted-item-used: a numbered step uses a thing the document's list of what is needed does not name
|
|
2
|
+
// (structure/unlisted-items.ts). The words are the language package's item-list-heading, item-use, item-amount,
|
|
3
|
+
// item-at-hand and enumeration-joiner.
|
|
4
|
+
import type { Detector, Finding, ProseDocument, Token } from "../plugin.ts";
|
|
5
|
+
import { itemLists, unlistedItems, type ItemStep, type ItemToken, type ItemWords, type UseGroup, type UseMarker } from "../structure/unlisted-items.ts";
|
|
6
|
+
import { quoteAround } from "./quote-around.ts";
|
|
7
|
+
|
|
8
|
+
const USE_GROUPS: readonly UseGroup[] = ["object", "place", "instrument"];
|
|
9
|
+
|
|
10
|
+
const patternsOf = (doc: ProseDocument, lexicon: string, group?: string): string[] =>
|
|
11
|
+
(doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group).map((entry) => entry.pattern);
|
|
12
|
+
|
|
13
|
+
const markersOf = (doc: ProseDocument): UseMarker[] =>
|
|
14
|
+
(doc.lexicons["item-use"] ?? []).flatMap((entry) => {
|
|
15
|
+
const group = USE_GROUPS.find((use) => use === entry.group);
|
|
16
|
+
return group === undefined || entry.position === undefined ? [] : [{ word: entry.pattern, group, position: entry.position }];
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const wordsOf = (doc: ProseDocument): ItemWords => ({
|
|
20
|
+
ingredientHeadings: patternsOf(doc, "item-list-heading", "ingredient"),
|
|
21
|
+
toolHeadings: patternsOf(doc, "item-list-heading", "tool"),
|
|
22
|
+
markers: markersOf(doc),
|
|
23
|
+
verbs: Object.fromEntries(USE_GROUPS.map((group) => [group, patternsOf(doc, "item-use", `${group}-verb`)])),
|
|
24
|
+
negations: patternsOf(doc, "item-use", "negation"),
|
|
25
|
+
actions: patternsOf(doc, "item-use", "action"),
|
|
26
|
+
joiners: patternsOf(doc, "enumeration-joiner"),
|
|
27
|
+
partitives: patternsOf(doc, "item-use", "partitive"),
|
|
28
|
+
amounts: patternsOf(doc, "item-amount"),
|
|
29
|
+
atHand: patternsOf(doc, "item-at-hand"),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const NUMBERED_LINE = /^[ \t]*\d+[.).)][ \t]+/gmu;
|
|
33
|
+
|
|
34
|
+
const itemToken = (token: Token): ItemToken => ({
|
|
35
|
+
surface: token.surface,
|
|
36
|
+
pos: token.pos,
|
|
37
|
+
lemma: token.lemma ?? token.surface,
|
|
38
|
+
start: token.span.start,
|
|
39
|
+
end: token.span.end,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/** Each numbered line, with the words of the sentences that start on it. */
|
|
43
|
+
const stepsOf = (doc: ProseDocument): ItemStep[] =>
|
|
44
|
+
[...doc.source.matchAll(NUMBERED_LINE)].map((match) => {
|
|
45
|
+
const lineEnd = doc.source.indexOf("\n", match.index);
|
|
46
|
+
const end = lineEnd === -1 ? doc.source.length : lineEnd;
|
|
47
|
+
const sentences = doc.sentences.filter((sentence) => sentence.span.start >= match.index && sentence.span.start < end);
|
|
48
|
+
return { start: match.index, tokens: sentences.flatMap((sentence) => (sentence.tokens ?? []).map(itemToken)) };
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const headingsOf = (doc: ProseDocument): string[] => doc.sections.filter((section) => section.depth > 0).map((section) => section.heading);
|
|
52
|
+
|
|
53
|
+
/** Every word the tagger read, in the text and in the table cells (a list of ingredients is often a table). */
|
|
54
|
+
const wordsWritten = (doc: ProseDocument): ItemToken[] => [
|
|
55
|
+
...doc.sentences.flatMap((sentence) => (sentence.tokens ?? []).map(itemToken)),
|
|
56
|
+
...(doc.tableCells?.() ?? []).flatMap((cell) => (cell.tokens ?? []).map(itemToken)),
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
export const unlistedItemUsed: Detector = (doc): Finding[] => {
|
|
60
|
+
const words = wordsOf(doc);
|
|
61
|
+
const headings = headingsOf(doc);
|
|
62
|
+
// Reading every table cell is not free; a document without a list heading needs none of it.
|
|
63
|
+
if (itemLists(headings, words).length === 0) return [];
|
|
64
|
+
return unlistedItems({ source: doc.source, words: wordsWritten(doc) }, headings, stepsOf(doc), words).map((item) => ({
|
|
65
|
+
rule: "unlisted-item-used",
|
|
66
|
+
severity: "warning",
|
|
67
|
+
line: 0,
|
|
68
|
+
column: 0,
|
|
69
|
+
quote: quoteAround(doc.source, item.offset, item.offset + item.item.length),
|
|
70
|
+
values: { item: item.item, list: item.list, offset: item.offset },
|
|
71
|
+
}));
|
|
72
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ProseDocument } from "../plugin.ts";
|
|
2
|
+
import type { Mark } from "../structure/time-marks.ts";
|
|
3
|
+
import type { CountWord, LimitWords, PeriodCountWords, PeriodWord } from "../derived/period-counts.ts";
|
|
4
|
+
|
|
5
|
+
// 期間あたりの回数の語(use-count-word)と、語で書いた数(count-number)。product-exceeds-maximum と interval-count-mismatch が読む。
|
|
6
|
+
|
|
7
|
+
const entriesOf = (doc: ProseDocument, lexicon: string, group?: string) =>
|
|
8
|
+
(doc.lexicons[lexicon] ?? []).filter((entry) => group === undefined || entry.group === group);
|
|
9
|
+
|
|
10
|
+
export const marksOf = (doc: ProseDocument, lexicon: string, group?: string): Mark[] =>
|
|
11
|
+
entriesOf(doc, lexicon, group).map((entry) => ({ pattern: entry.pattern, position: entry.position, group: entry.group }));
|
|
12
|
+
|
|
13
|
+
const periodsOf = (doc: ProseDocument): PeriodWord[] =>
|
|
14
|
+
entriesOf(doc, "use-count-word", "period").map((entry) => ({ pattern: entry.pattern, position: entry.position, hours: entry.weight ?? 0 }));
|
|
15
|
+
|
|
16
|
+
/** one, two, …: the position in count-number is the value. once, twice: the weight in use-count-word. */
|
|
17
|
+
const numberWordsOf = (doc: ProseDocument): CountWord[] => [
|
|
18
|
+
...entriesOf(doc, "count-number").map((entry, index) => ({ pattern: entry.pattern, value: index + 1, alone: false })),
|
|
19
|
+
...entriesOf(doc, "use-count-word", "alone").map((entry) => ({ pattern: entry.pattern, value: entry.weight ?? 0, alone: true })),
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export const periodCountWordsOf = (doc: ProseDocument): PeriodCountWords => ({
|
|
23
|
+
periods: periodsOf(doc),
|
|
24
|
+
counters: entriesOf(doc, "use-count-word", "counter").map((entry) => entry.pattern),
|
|
25
|
+
numberWords: numberWordsOf(doc),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** The limit words, the ones that need a negation before them ("more than"), and the negations. */
|
|
29
|
+
export const limitWordsOf = (doc: ProseDocument): LimitWords => ({
|
|
30
|
+
limits: [...marksOf(doc, "use-count-word", "limit"), ...marksOf(doc, "use-count-word", "limit-negated")],
|
|
31
|
+
negations: marksOf(doc, "use-count-word", "negation").map((mark) => mark.pattern),
|
|
32
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Detector, Finding, ProseDocument, Span } from "../plugin.ts";
|
|
2
|
+
import { intervalCountMismatches, type IntervalMismatch, type IntervalWords } from "../derived/use-interval.ts";
|
|
3
|
+
import { paragraphsOf } from "../derived/period-counts.ts";
|
|
4
|
+
import { timeLengths, type TimeLength } from "../derived/time-lengths.ts";
|
|
5
|
+
import { limitWordsOf, marksOf, periodCountWordsOf } from "./use-count-words.ts";
|
|
6
|
+
import { quoteAt } from "./structure-tree.ts";
|
|
7
|
+
|
|
8
|
+
// 使う間隔の下限と期間あたりの回数の上限が両立しない、または表の回数と合わない(interval-count-mismatch)。
|
|
9
|
+
// 語は use-count-word と use-interval-word、長さの単位は unit-time から取る。
|
|
10
|
+
|
|
11
|
+
const SECONDS_PER_HOUR = 3600;
|
|
12
|
+
const SECONDS_PER_MINUTE = 60;
|
|
13
|
+
|
|
14
|
+
const patternsOf = (doc: ProseDocument, lexicon: string, weight?: number): string[] =>
|
|
15
|
+
(doc.lexicons[lexicon] ?? []).filter((entry) => weight === undefined || entry.weight === weight).map((entry) => entry.pattern);
|
|
16
|
+
|
|
17
|
+
const lengthsOf = (doc: ProseDocument): TimeLength[] =>
|
|
18
|
+
timeLengths(doc.source, {
|
|
19
|
+
hourUnits: patternsOf(doc, "unit-time", SECONDS_PER_HOUR),
|
|
20
|
+
minuteUnits: patternsOf(doc, "unit-time", SECONDS_PER_MINUTE),
|
|
21
|
+
halves: patternsOf(doc, "length-half"),
|
|
22
|
+
numberWords: patternsOf(doc, "count-number"),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const wordsOf = (doc: ProseDocument): IntervalWords => ({
|
|
26
|
+
...periodCountWordsOf(doc),
|
|
27
|
+
...limitWordsOf(doc),
|
|
28
|
+
minimum: marksOf(doc, "use-interval-word", "minimum"),
|
|
29
|
+
cues: marksOf(doc, "use-interval-word", "cue").map((mark) => mark.pattern),
|
|
30
|
+
columns: (doc.lexicons["use-interval-word"] ?? [])
|
|
31
|
+
.filter((entry) => entry.group === "column")
|
|
32
|
+
.map((entry) => ({ pattern: entry.pattern, hours: entry.weight ?? 0 })),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const written = (doc: ProseDocument, span: Span): string => doc.source.slice(span.start, span.end);
|
|
36
|
+
|
|
37
|
+
const valuesOf = (doc: ProseDocument, mismatch: IntervalMismatch): Record<string, string | number> =>
|
|
38
|
+
mismatch.kind === "interval"
|
|
39
|
+
? { count: written(doc, mismatch.count), interval: written(doc, mismatch.interval), needed: mismatch.neededHours, hours: mismatch.count.hours }
|
|
40
|
+
: { count: written(doc, mismatch.count), column: mismatch.column, table: mismatch.tableCount };
|
|
41
|
+
|
|
42
|
+
export const intervalCountMismatch: Detector = (doc): Finding[] =>
|
|
43
|
+
intervalCountMismatches(doc.source, paragraphsOf(doc.source), lengthsOf(doc), wordsOf(doc)).map((mismatch) => ({
|
|
44
|
+
rule: "interval-count-mismatch",
|
|
45
|
+
severity: "warning",
|
|
46
|
+
line: 0,
|
|
47
|
+
column: 0,
|
|
48
|
+
quote: quoteAt(doc.source, mismatch.count.start),
|
|
49
|
+
values: { ...valuesOf(doc, mismatch), offset: mismatch.count.start },
|
|
50
|
+
...(mismatch.kind === "table" ? { variant: "table" } : {}),
|
|
51
|
+
}));
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// A walking time that does not match the stated distance at the document's walking speed (walk-time-distance-mismatch).
|
|
2
|
+
// The words come from the language package (walk-word, walk-rate, unit-length, unit-time, unit-equivalent-hedge); the speed
|
|
3
|
+
// used when the document states none comes from its profile (profiles/listing.yaml).
|
|
4
|
+
import { walkTimeMismatches, type WalkMark, type WalkRate, type WalkWords } from "../structure/walk-time.ts";
|
|
5
|
+
import { proseAndTablesOf } from "../table-text.ts";
|
|
6
|
+
import { quoteAt } from "./structure-tree.ts";
|
|
7
|
+
import type { Detector, Finding, ProseDocument } from "../plugin.ts";
|
|
8
|
+
|
|
9
|
+
const marksOf = (doc: ProseDocument, lexicon: string, group?: string): WalkMark[] =>
|
|
10
|
+
(doc.lexicons[lexicon] ?? [])
|
|
11
|
+
.filter((entry) => group === undefined || entry.group === group)
|
|
12
|
+
.map((entry) => ({ pattern: entry.pattern, position: entry.position, weight: entry.weight }));
|
|
13
|
+
|
|
14
|
+
const patternsOf = (doc: ProseDocument, lexicon: string, group?: string): string[] => marksOf(doc, lexicon, group).map((mark) => mark.pattern);
|
|
15
|
+
|
|
16
|
+
const walkWords = (doc: ProseDocument): WalkWords => ({
|
|
17
|
+
walks: marksOf(doc, "walk-word", "walk"),
|
|
18
|
+
gaps: patternsOf(doc, "walk-word", "gap"),
|
|
19
|
+
bounds: marksOf(doc, "walk-word", "bound"),
|
|
20
|
+
ranges: patternsOf(doc, "walk-word", "range"),
|
|
21
|
+
fillers: patternsOf(doc, "walk-word", "filler"),
|
|
22
|
+
lengths: marksOf(doc, "unit-length"),
|
|
23
|
+
times: marksOf(doc, "unit-time"),
|
|
24
|
+
hedges: patternsOf(doc, "unit-equivalent-hedge"),
|
|
25
|
+
perTimes: marksOf(doc, "walk-rate", "per"),
|
|
26
|
+
roundUps: patternsOf(doc, "walk-rate", "round-up"),
|
|
27
|
+
notRoundUps: patternsOf(doc, "walk-rate", "not-round-up"),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/** The speed the document's profile walks at when the document states none (a real-estate listing: 80 m a minute, rounded up). */
|
|
31
|
+
const profileRate = (doc: ProseDocument): WalkRate | undefined => {
|
|
32
|
+
const rate = doc.profile?.walkRate;
|
|
33
|
+
return rate === undefined ? undefined : { metresPerMinute: rate.metresPerMinute, rounding: rate.roundUp ? "up" : "any" };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** The prose with its tables put back, so code is not read; offsets are the source's. */
|
|
37
|
+
export const walkTimeDistanceMismatch: Detector = (doc): Finding[] =>
|
|
38
|
+
walkTimeMismatches(proseAndTablesOf(doc), walkWords(doc), profileRate(doc)).map((issue) => ({
|
|
39
|
+
rule: "walk-time-distance-mismatch",
|
|
40
|
+
severity: "warning",
|
|
41
|
+
line: 0,
|
|
42
|
+
column: 0,
|
|
43
|
+
quote: quoteAt(doc.source, issue.offset),
|
|
44
|
+
values: { ...issue.values, offset: issue.offset },
|
|
45
|
+
}));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Measured, Tolerance } from "./measures.ts";
|
|
2
|
+
|
|
3
|
+
const HALF = 0.5;
|
|
4
|
+
const DECIMAL_BASE = 10;
|
|
5
|
+
|
|
6
|
+
/** 目安の量(約1.2kg)が言える幅を、基準の単位で。書いた最後の桁の半分(約1.2kg は 1.15〜1.25 kg)。 */
|
|
7
|
+
const writtenHalfStep = (value: Measured): number => {
|
|
8
|
+
const step = HALF * DECIMAL_BASE ** -value.decimals;
|
|
9
|
+
return step * Math.max(...value.factors);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 目安と書いた量を含む二つを比べるときの、合うとみなす差。目安の量は書いた桁までしか言っていないので、その桁の半分までの差は合う。
|
|
14
|
+
* 量の種類ごとの差(tolerance)のほうが広ければ、そちら。
|
|
15
|
+
*/
|
|
16
|
+
export const approximateTolerance = (tolerance: Tolerance, approximate: readonly Measured[]): Tolerance => ({
|
|
17
|
+
relative: tolerance.relative,
|
|
18
|
+
absolute: Math.max(tolerance.absolute, ...approximate.map(writtenHalfStep)),
|
|
19
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { linesOf, type Line } from "../structure/lines.ts";
|
|
2
|
+
import { sameMeasure, sameUnit, type Measured } from "./measures.ts";
|
|
3
|
+
import { cellsOf, tablesOf, type Cell } from "./table-facts.ts";
|
|
4
|
+
import { withoutEdgeMarks } from "./trim-marks.ts";
|
|
5
|
+
import { toleranceOf } from "./unit-tolerance.ts";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 表に量を書いた品物(材料の表の「しょうゆ|大さじ1」)を、本文で違う単位の量と一緒に書いた所(しょうゆ30ml、30 ml of soy sauce)。
|
|
9
|
+
* 表の量は、行の最初の量(人数ごとの列があれば、手順が従う最初の列)。本文の量が表の量より多いときだけ言う。本文の名前は、量のすぐ前(空白一つまで)か、つなぐ語(の、of)と
|
|
10
|
+
* 冠詞(the)を挟んで量の後ろ。名前は語の切れ目で始まり終わる(「濃口しょうゆ」の しょうゆ は切れ目で始まるが、「強力粉」の 力粉 は始まらない)。
|
|
11
|
+
*/
|
|
12
|
+
export type ItemConflict = { readonly name: string; readonly measured: Measured; readonly other: Measured };
|
|
13
|
+
|
|
14
|
+
export type ItemWords = { readonly links: readonly string[]; readonly determiners: readonly string[] };
|
|
15
|
+
|
|
16
|
+
type ItemAmount = { readonly name: string; readonly key: string; readonly measured: Measured };
|
|
17
|
+
|
|
18
|
+
const LETTER = /\p{L}/u;
|
|
19
|
+
const SCRIPTS = [/\p{Script=Hiragana}/u, /[\p{Script=Katakana}ー]/u, /\p{Script=Han}/u, /\p{Script=Latin}/u];
|
|
20
|
+
const NAME_WINDOW = 80;
|
|
21
|
+
|
|
22
|
+
const keyOf = (text: string): string => text.normalize("NFKC").toLowerCase();
|
|
23
|
+
|
|
24
|
+
const scriptOf = (char: string): number => SCRIPTS.findIndex((script) => script.test(char));
|
|
25
|
+
|
|
26
|
+
/** 二つの字の間が語の切れ目か。字でない側があるか、字の種類(仮名、漢字、英字)が変わる所。英字どうしは続いている。 */
|
|
27
|
+
const isBoundary = (left: string, right: string): boolean =>
|
|
28
|
+
left === "" || right === "" || !LETTER.test(left) || !LETTER.test(right) || scriptOf(left) !== scriptOf(right);
|
|
29
|
+
|
|
30
|
+
const cellValue = (cell: Cell, source: string, measured: readonly Measured[]): Measured | undefined =>
|
|
31
|
+
measured.find((value) => value.start >= cell.start && value.end <= cell.end && source.slice(value.start, value.end) === withoutEdgeMarks(cell.text));
|
|
32
|
+
|
|
33
|
+
/** 表の行の見出しと、その行の最初の量。同じ名前の行が二つあれば最初の行。 */
|
|
34
|
+
const tableAmounts = (source: string, rows: readonly Line[], measured: readonly Measured[]): ItemAmount[] => {
|
|
35
|
+
const amounts = rows.flatMap((row): ItemAmount[] => {
|
|
36
|
+
const [first, ...rest] = cellsOf(row);
|
|
37
|
+
const name = first === undefined ? "" : withoutEdgeMarks(first.text);
|
|
38
|
+
const value = rest.map((cell) => cellValue(cell, source, measured)).find((found) => found !== undefined);
|
|
39
|
+
if (first === undefined || value === undefined || !LETTER.test(name) || cellValue(first, source, measured) !== undefined) return [];
|
|
40
|
+
return [{ name, key: keyOf(name), measured: value }];
|
|
41
|
+
});
|
|
42
|
+
return amounts.filter((amount, index) => amounts.findIndex((other) => other.key === amount.key) === index);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** 量のすぐ前に名前があるか(しょうゆ大さじ1、soy sauce 30 ml)。 */
|
|
46
|
+
const namedBefore = (source: string, value: Measured, key: string): boolean => {
|
|
47
|
+
const end = source.charAt(value.start - 1) === " " ? value.start - 1 : value.start;
|
|
48
|
+
const head = keyOf(source.slice(Math.max(0, end - NAME_WINDOW), end));
|
|
49
|
+
return head.endsWith(key) && isBoundary(head.charAt(head.length - key.length - 1), key.charAt(0));
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const skipSpace = (text: string): string => (text.startsWith(" ") ? text.slice(1) : text);
|
|
53
|
+
|
|
54
|
+
/** 頭の語を一つ落とす。英字の語は、後ろに空白が要る(of は offer の頭ではない)。 */
|
|
55
|
+
const dropWord = (text: string, words: readonly string[]): string | undefined => {
|
|
56
|
+
const word = words.map(keyOf).find((candidate) => text.startsWith(candidate) && isBoundary(candidate.slice(-1), text.charAt(candidate.length)));
|
|
57
|
+
return word === undefined ? undefined : skipSpace(text.slice(word.length));
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** つなぐ語を挟んで、量の後ろに名前があるか(30mlのしょうゆ、30 ml of the soy sauce)。 */
|
|
61
|
+
const namedAfter = (source: string, value: Measured, key: string, words: ItemWords): boolean => {
|
|
62
|
+
const linked = dropWord(skipSpace(keyOf(source.slice(value.end, value.end + NAME_WINDOW))), words.links);
|
|
63
|
+
if (linked === undefined) return false;
|
|
64
|
+
const rest = dropWord(linked, words.determiners) ?? linked;
|
|
65
|
+
return rest.startsWith(key) && isBoundary(key.slice(-1), rest.charAt(key.length));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** 本文の量が、表の量をどう換算しても超えるか。表より少ない量は、その一部を使う所(牛乳の半分を先に)かもしれない。 */
|
|
69
|
+
const exceeds = (value: Measured, listed: Measured): boolean =>
|
|
70
|
+
value.factors.every((a) => listed.factors.every((b) => (value.amount - value.zero) * a > (listed.amount - listed.zero) * b));
|
|
71
|
+
|
|
72
|
+
const inTable = (value: Measured, rows: readonly Line[]): boolean => rows.some((row) => value.start >= row.start && value.end <= row.start + row.text.length);
|
|
73
|
+
|
|
74
|
+
/** 本文の量のうち、表の同じ品物の量と単位が違い、換算すると合わないもの。 */
|
|
75
|
+
export const itemConflicts = (source: string, measured: readonly Measured[], words: ItemWords): ItemConflict[] => {
|
|
76
|
+
const rows = tablesOf(linesOf(source)).flatMap((table) => [table.header, ...table.rows]);
|
|
77
|
+
const amounts = tableAmounts(source, rows, measured);
|
|
78
|
+
if (amounts.length === 0) return [];
|
|
79
|
+
return measured
|
|
80
|
+
.filter((value) => !inTable(value, rows))
|
|
81
|
+
.flatMap((value): ItemConflict[] => {
|
|
82
|
+
const item = amounts.find(
|
|
83
|
+
({ key, measured: listed }) => listed.dimension === value.dimension && (namedBefore(source, value, key) || namedAfter(source, value, key, words)),
|
|
84
|
+
);
|
|
85
|
+
if (
|
|
86
|
+
item === undefined ||
|
|
87
|
+
sameUnit(item.measured, value) ||
|
|
88
|
+
sameMeasure(item.measured, value, toleranceOf(value.dimension)) ||
|
|
89
|
+
!exceeds(value, item.measured)
|
|
90
|
+
)
|
|
91
|
+
return [];
|
|
92
|
+
return [{ name: item.name, measured: value, other: item.measured }];
|
|
93
|
+
});
|
|
94
|
+
};
|