chaffjs 0.32.0 → 0.34.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/README.md +7 -0
- package/dist/api.d.ts +7 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js.map +1 -1
- package/dist/cli-text.d.ts.map +1 -1
- package/dist/cli-text.js +6 -4
- package/dist/cli-text.js.map +1 -1
- package/dist/commands/grade.d.ts.map +1 -1
- package/dist/commands/grade.js +66 -21
- package/dist/commands/grade.js.map +1 -1
- package/dist/company-kana-spelling.d.ts +6 -1
- package/dist/company-kana-spelling.d.ts.map +1 -1
- package/dist/company-kana-spelling.js +19 -2
- package/dist/company-kana-spelling.js.map +1 -1
- package/dist/company-name-ending.d.ts +7 -0
- package/dist/company-name-ending.d.ts.map +1 -0
- package/dist/company-name-ending.js +44 -0
- package/dist/company-name-ending.js.map +1 -0
- package/dist/company-names.d.ts +11 -3
- package/dist/company-names.d.ts.map +1 -1
- package/dist/company-names.js +27 -14
- package/dist/company-names.js.map +1 -1
- package/dist/cross-run.d.ts.map +1 -1
- package/dist/cross-run.js +1 -0
- package/dist/cross-run.js.map +1 -1
- package/dist/custom/no-rewrite.d.ts +3 -0
- package/dist/custom/no-rewrite.d.ts.map +1 -0
- package/dist/custom/no-rewrite.js +3 -0
- package/dist/custom/no-rewrite.js.map +1 -0
- package/dist/custom/parse.d.ts.map +1 -1
- package/dist/custom/parse.js +14 -2
- package/dist/custom/parse.js.map +1 -1
- package/dist/detectors/age-words.d.ts +5 -0
- package/dist/detectors/age-words.d.ts.map +1 -0
- package/dist/detectors/age-words.js +8 -0
- package/dist/detectors/age-words.js.map +1 -0
- package/dist/detectors/agreement-slip.d.ts.map +1 -1
- package/dist/detectors/agreement-slip.js +4 -0
- package/dist/detectors/agreement-slip.js.map +1 -1
- package/dist/detectors/annual-pay-mismatch.d.ts.map +1 -1
- package/dist/detectors/annual-pay-mismatch.js +2 -12
- package/dist/detectors/annual-pay-mismatch.js.map +1 -1
- package/dist/detectors/blank-structure.d.ts.map +1 -1
- package/dist/detectors/blank-structure.js +4 -3
- package/dist/detectors/blank-structure.js.map +1 -1
- package/dist/detectors/closing-balance-mismatch.d.ts.map +1 -1
- package/dist/detectors/closing-balance-mismatch.js +1 -0
- package/dist/detectors/closing-balance-mismatch.js.map +1 -1
- package/dist/detectors/code-fences.d.ts +5 -0
- package/dist/detectors/code-fences.d.ts.map +1 -1
- package/dist/detectors/code-fences.js +9 -0
- package/dist/detectors/code-fences.js.map +1 -1
- package/dist/detectors/condition-toki.d.ts +16 -0
- package/dist/detectors/condition-toki.d.ts.map +1 -0
- package/dist/detectors/condition-toki.js +42 -0
- package/dist/detectors/condition-toki.js.map +1 -0
- package/dist/detectors/conjugated-entry.d.ts +17 -0
- package/dist/detectors/conjugated-entry.d.ts.map +1 -0
- package/dist/detectors/conjugated-entry.js +50 -0
- package/dist/detectors/conjugated-entry.js.map +1 -0
- package/dist/detectors/content-morphemes.d.ts +2 -0
- package/dist/detectors/content-morphemes.d.ts.map +1 -1
- package/dist/detectors/content-morphemes.js +1 -1
- package/dist/detectors/content-morphemes.js.map +1 -1
- package/dist/detectors/cross-facts.d.ts.map +1 -1
- package/dist/detectors/cross-facts.js +2 -1
- package/dist/detectors/cross-facts.js.map +1 -1
- package/dist/detectors/custom.d.ts.map +1 -1
- package/dist/detectors/custom.js +8 -2
- package/dist/detectors/custom.js.map +1 -1
- package/dist/detectors/document-shape.d.ts.map +1 -1
- package/dist/detectors/document-shape.js +6 -0
- package/dist/detectors/document-shape.js.map +1 -1
- package/dist/detectors/due-date.d.ts.map +1 -1
- package/dist/detectors/due-date.js +12 -2
- package/dist/detectors/due-date.js.map +1 -1
- package/dist/detectors/fact-consistency.d.ts.map +1 -1
- package/dist/detectors/fact-consistency.js +12 -13
- package/dist/detectors/fact-consistency.js.map +1 -1
- package/dist/detectors/form-line.d.ts +9 -0
- package/dist/detectors/form-line.d.ts.map +1 -0
- package/dist/detectors/form-line.js +36 -0
- package/dist/detectors/form-line.js.map +1 -0
- package/dist/detectors/formula-amount.d.ts +3 -0
- package/dist/detectors/formula-amount.d.ts.map +1 -0
- package/dist/detectors/formula-amount.js +31 -0
- package/dist/detectors/formula-amount.js.map +1 -0
- package/dist/detectors/heading-claim.d.ts +14 -0
- package/dist/detectors/heading-claim.d.ts.map +1 -0
- package/dist/detectors/heading-claim.js +49 -0
- package/dist/detectors/heading-claim.js.map +1 -0
- package/dist/detectors/heading-echo.d.ts.map +1 -1
- package/dist/detectors/heading-echo.js +29 -1
- package/dist/detectors/heading-echo.js.map +1 -1
- package/dist/detectors/installment-total-mismatch.d.ts.map +1 -1
- package/dist/detectors/installment-total-mismatch.js +28 -19
- package/dist/detectors/installment-total-mismatch.js.map +1 -1
- package/dist/detectors/list-parallel.d.ts +5 -2
- package/dist/detectors/list-parallel.d.ts.map +1 -1
- package/dist/detectors/list-parallel.js +45 -7
- package/dist/detectors/list-parallel.js.map +1 -1
- package/dist/detectors/name-variant.d.ts.map +1 -1
- package/dist/detectors/name-variant.js +34 -7
- package/dist/detectors/name-variant.js.map +1 -1
- package/dist/detectors/number-unit.d.ts +10 -0
- package/dist/detectors/number-unit.d.ts.map +1 -1
- package/dist/detectors/number-unit.js +28 -8
- package/dist/detectors/number-unit.js.map +1 -1
- package/dist/detectors/orthographic-variant.d.ts.map +1 -1
- package/dist/detectors/orthographic-variant.js +4 -3
- package/dist/detectors/orthographic-variant.js.map +1 -1
- package/dist/detectors/orthography.d.ts.map +1 -1
- package/dist/detectors/orthography.js +16 -7
- package/dist/detectors/orthography.js.map +1 -1
- package/dist/detectors/per-unit-words.d.ts +7 -0
- package/dist/detectors/per-unit-words.d.ts.map +1 -0
- package/dist/detectors/per-unit-words.js +17 -0
- package/dist/detectors/per-unit-words.js.map +1 -0
- package/dist/detectors/period-multiple-mismatch.d.ts.map +1 -1
- package/dist/detectors/period-multiple-mismatch.js +2 -12
- package/dist/detectors/period-multiple-mismatch.js.map +1 -1
- package/dist/detectors/reference-number-variant.d.ts.map +1 -1
- package/dist/detectors/reference-number-variant.js +21 -3
- package/dist/detectors/reference-number-variant.js.map +1 -1
- package/dist/detectors/registry/condition-toki.d.ts +3 -0
- package/dist/detectors/registry/condition-toki.d.ts.map +1 -0
- package/dist/detectors/registry/condition-toki.js +3 -0
- package/dist/detectors/registry/condition-toki.js.map +1 -0
- package/dist/detectors/registry/formula-amount.d.ts +3 -0
- package/dist/detectors/registry/formula-amount.d.ts.map +1 -0
- package/dist/detectors/registry/formula-amount.js +3 -0
- package/dist/detectors/registry/formula-amount.js.map +1 -0
- package/dist/detectors/rent-multiple-mismatch.d.ts.map +1 -1
- package/dist/detectors/rent-multiple-mismatch.js +2 -12
- package/dist/detectors/rent-multiple-mismatch.js.map +1 -1
- package/dist/detectors/row-sentence-amounts.d.ts.map +1 -1
- package/dist/detectors/row-sentence-amounts.js +5 -10
- package/dist/detectors/row-sentence-amounts.js.map +1 -1
- package/dist/detectors/stated-amounts.d.ts +4 -0
- package/dist/detectors/stated-amounts.d.ts.map +1 -0
- package/dist/detectors/stated-amounts.js +12 -0
- package/dist/detectors/stated-amounts.js.map +1 -0
- package/dist/detectors/step-time-sum.d.ts.map +1 -1
- package/dist/detectors/step-time-sum.js +2 -1
- package/dist/detectors/step-time-sum.js.map +1 -1
- package/dist/detectors/structure-tree.d.ts +1 -1
- package/dist/detectors/structure-tree.d.ts.map +1 -1
- package/dist/detectors/structure-tree.js +7 -2
- package/dist/detectors/structure-tree.js.map +1 -1
- package/dist/detectors/team.d.ts.map +1 -1
- package/dist/detectors/team.js +10 -6
- package/dist/detectors/team.js.map +1 -1
- package/dist/detectors/time-order.d.ts.map +1 -1
- package/dist/detectors/time-order.js +17 -13
- package/dist/detectors/time-order.js.map +1 -1
- package/dist/detectors/unknown-word.d.ts +4 -1
- package/dist/detectors/unknown-word.d.ts.map +1 -1
- package/dist/detectors/unknown-word.js +18 -2
- package/dist/detectors/unknown-word.js.map +1 -1
- package/dist/detectors/use-interval.d.ts +1 -0
- package/dist/detectors/use-interval.d.ts.map +1 -1
- package/dist/detectors/use-interval.js +15 -10
- package/dist/detectors/use-interval.js.map +1 -1
- package/dist/detectors/use-maximum.d.ts.map +1 -1
- package/dist/detectors/use-maximum.js +3 -1
- package/dist/detectors/use-maximum.js.map +1 -1
- package/dist/eval.js +3 -3
- package/dist/eval.js.map +1 -1
- package/dist/extension/document-view.d.ts +2 -2
- package/dist/extension/document-view.d.ts.map +1 -1
- package/dist/extension/document-view.js +17 -8
- package/dist/extension/document-view.js.map +1 -1
- package/dist/extension/entry-genres.d.ts +5 -0
- package/dist/extension/entry-genres.d.ts.map +1 -0
- package/dist/extension/entry-genres.js +13 -0
- package/dist/extension/entry-genres.js.map +1 -0
- package/dist/extension/module-detector.d.ts.map +1 -1
- package/dist/extension/module-detector.js +4 -4
- package/dist/extension/module-detector.js.map +1 -1
- package/dist/extension/plugin-parse.d.ts +1 -1
- package/dist/extension/plugin-parse.d.ts.map +1 -1
- package/dist/extension/plugin-parse.js +24 -7
- package/dist/extension/plugin-parse.js.map +1 -1
- package/dist/extension/problem-text.d.ts.map +1 -1
- package/dist/extension/problem-text.js +2 -0
- package/dist/extension/problem-text.js.map +1 -1
- package/dist/facts/age-values.d.ts.map +1 -1
- package/dist/facts/age-values.js +20 -2
- package/dist/facts/age-values.js.map +1 -1
- package/dist/facts/column-units.d.ts +31 -0
- package/dist/facts/column-units.d.ts.map +1 -0
- package/dist/facts/column-units.js +37 -0
- package/dist/facts/column-units.js.map +1 -0
- package/dist/facts/fact-values.d.ts +4 -0
- package/dist/facts/fact-values.d.ts.map +1 -1
- package/dist/facts/fact-values.js +3 -1
- package/dist/facts/fact-values.js.map +1 -1
- package/dist/facts/labelled-facts.d.ts +10 -0
- package/dist/facts/labelled-facts.d.ts.map +1 -1
- package/dist/facts/labelled-facts.js +6 -1
- package/dist/facts/labelled-facts.js.map +1 -1
- package/dist/facts/per-unit.d.ts +36 -0
- package/dist/facts/per-unit.d.ts.map +1 -0
- package/dist/facts/per-unit.js +56 -0
- package/dist/facts/per-unit.js.map +1 -0
- package/dist/facts/row-sentence-amounts.d.ts +3 -12
- package/dist/facts/row-sentence-amounts.d.ts.map +1 -1
- package/dist/facts/row-sentence-amounts.js +1 -15
- package/dist/facts/row-sentence-amounts.js.map +1 -1
- package/dist/facts/table-facts.d.ts +1 -1
- package/dist/facts/table-facts.d.ts.map +1 -1
- package/dist/facts/table-facts.js +13 -9
- package/dist/facts/table-facts.js.map +1 -1
- package/dist/facts/term-values.d.ts +13 -1
- package/dist/facts/term-values.d.ts.map +1 -1
- package/dist/facts/term-values.js +71 -12
- package/dist/facts/term-values.js.map +1 -1
- package/dist/fix-plan/text.js +2 -2
- package/dist/fix-plan/text.js.map +1 -1
- package/dist/grade/baseline.d.ts +4 -2
- package/dist/grade/baseline.d.ts.map +1 -1
- package/dist/grade/baseline.js +13 -11
- package/dist/grade/baseline.js.map +1 -1
- package/dist/grade/expect-read.d.ts +16 -0
- package/dist/grade/expect-read.d.ts.map +1 -0
- package/dist/grade/expect-read.js +52 -0
- package/dist/grade/expect-read.js.map +1 -0
- package/dist/grade/expect.d.ts +50 -0
- package/dist/grade/expect.d.ts.map +1 -0
- package/dist/grade/expect.js +53 -0
- package/dist/grade/expect.js.map +1 -0
- package/dist/grade/grade-item.d.ts +1 -1
- package/dist/grade/grade-item.d.ts.map +1 -1
- package/dist/grade/grade-item.js +13 -3
- package/dist/grade/grade-item.js.map +1 -1
- package/dist/grade/item.d.ts +9 -1
- package/dist/grade/item.d.ts.map +1 -1
- package/dist/grade/item.js +32 -7
- package/dist/grade/item.js.map +1 -1
- package/dist/grade/result.d.ts +5 -0
- package/dist/grade/result.d.ts.map +1 -1
- package/dist/grade/results-read.d.ts.map +1 -1
- package/dist/grade/results-read.js +4 -2
- package/dist/grade/results-read.js.map +1 -1
- package/dist/grade/rubric-file.d.ts +23 -0
- package/dist/grade/rubric-file.d.ts.map +1 -0
- package/dist/grade/rubric-file.js +35 -0
- package/dist/grade/rubric-file.js.map +1 -0
- package/dist/grade/rubric.d.ts +2 -0
- package/dist/grade/rubric.d.ts.map +1 -1
- package/dist/grade/rubric.js +75 -47
- package/dist/grade/rubric.js.map +1 -1
- package/dist/grade/setup.d.ts +2 -1
- package/dist/grade/setup.d.ts.map +1 -1
- package/dist/grade/setup.js +2 -2
- package/dist/grade/setup.js.map +1 -1
- package/dist/grade/stamp.d.ts +2 -0
- package/dist/grade/stamp.d.ts.map +1 -1
- package/dist/grade/stamp.js +8 -2
- package/dist/grade/stamp.js.map +1 -1
- package/dist/grade/task-rubric.d.ts +16 -0
- package/dist/grade/task-rubric.d.ts.map +1 -0
- package/dist/grade/task-rubric.js +34 -0
- package/dist/grade/task-rubric.js.map +1 -0
- package/dist/grade/text.d.ts +5 -1
- package/dist/grade/text.d.ts.map +1 -1
- package/dist/grade/text.js +16 -4
- package/dist/grade/text.js.map +1 -1
- package/dist/lexicon-genres.d.ts +7 -0
- package/dist/lexicon-genres.d.ts.map +1 -0
- package/dist/lexicon-genres.js +8 -0
- package/dist/lexicon-genres.js.map +1 -0
- package/dist/name-char-reading.d.ts +5 -0
- package/dist/name-char-reading.d.ts.map +1 -1
- package/dist/name-char-reading.js +15 -0
- package/dist/name-char-reading.js.map +1 -1
- package/dist/name-end-letter.d.ts +19 -0
- package/dist/name-end-letter.d.ts.map +1 -0
- package/dist/name-end-letter.js +37 -0
- package/dist/name-end-letter.js.map +1 -0
- package/dist/name-neighbours.d.ts.map +1 -1
- package/dist/name-neighbours.js +2 -1
- package/dist/name-neighbours.js.map +1 -1
- package/dist/name-separator.d.ts +6 -0
- package/dist/name-separator.d.ts.map +1 -1
- package/dist/name-separator.js +29 -0
- package/dist/name-separator.js.map +1 -1
- package/dist/name-variants.d.ts +6 -3
- package/dist/name-variants.d.ts.map +1 -1
- package/dist/name-variants.js +37 -10
- package/dist/name-variants.js.map +1 -1
- package/dist/number-name.d.ts.map +1 -1
- package/dist/number-name.js +11 -2
- package/dist/number-name.js.map +1 -1
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/profile/cue-lines.d.ts +6 -0
- package/dist/profile/cue-lines.d.ts.map +1 -0
- package/dist/profile/cue-lines.js +13 -0
- package/dist/profile/cue-lines.js.map +1 -0
- package/dist/profile/parse.d.ts +1 -1
- package/dist/profile/parse.d.ts.map +1 -1
- package/dist/profile/parse.js +8 -2
- package/dist/profile/parse.js.map +1 -1
- package/dist/reference-numbers.d.ts +2 -0
- package/dist/reference-numbers.d.ts.map +1 -1
- package/dist/reference-numbers.js +1 -1
- package/dist/reference-numbers.js.map +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +1 -0
- package/dist/run.js.map +1 -1
- package/dist/spaced-person-name.d.ts.map +1 -1
- package/dist/spaced-person-name.js +26 -1
- package/dist/spaced-person-name.js.map +1 -1
- package/dist/spacing-code.d.ts.map +1 -1
- package/dist/spacing-code.js +10 -1
- package/dist/spacing-code.js.map +1 -1
- package/dist/spacing-minority.d.ts +7 -0
- package/dist/spacing-minority.d.ts.map +1 -1
- package/dist/spacing-minority.js +14 -0
- package/dist/spacing-minority.js.map +1 -1
- package/dist/structure/closing-balance.d.ts +9 -0
- package/dist/structure/closing-balance.d.ts.map +1 -1
- package/dist/structure/closing-balance.js +34 -7
- package/dist/structure/closing-balance.js.map +1 -1
- package/dist/structure/date-range.d.ts +3 -2
- package/dist/structure/date-range.d.ts.map +1 -1
- package/dist/structure/date-range.js +4 -4
- package/dist/structure/date-range.js.map +1 -1
- package/dist/structure/definition-use.d.ts.map +1 -1
- package/dist/structure/definition-use.js +20 -1
- package/dist/structure/definition-use.js.map +1 -1
- package/dist/structure/document-context.d.ts +2 -0
- package/dist/structure/document-context.d.ts.map +1 -0
- package/dist/structure/document-context.js +42 -0
- package/dist/structure/document-context.js.map +1 -0
- package/dist/structure/fenced-lines.d.ts +7 -0
- package/dist/structure/fenced-lines.d.ts.map +1 -0
- package/dist/structure/fenced-lines.js +27 -0
- package/dist/structure/fenced-lines.js.map +1 -0
- package/dist/structure/formula-amount.d.ts +38 -0
- package/dist/structure/formula-amount.d.ts.map +1 -0
- package/dist/structure/formula-amount.js +187 -0
- package/dist/structure/formula-amount.js.map +1 -0
- package/dist/structure/installment-fee.d.ts +52 -0
- package/dist/structure/installment-fee.d.ts.map +1 -0
- package/dist/structure/installment-fee.js +110 -0
- package/dist/structure/installment-fee.js.map +1 -0
- package/dist/structure/installment-total.d.ts +7 -0
- package/dist/structure/installment-total.d.ts.map +1 -1
- package/dist/structure/installment-total.js +6 -6
- package/dist/structure/installment-total.js.map +1 -1
- package/dist/structure/labelled-date-order.d.ts +31 -9
- package/dist/structure/labelled-date-order.d.ts.map +1 -1
- package/dist/structure/labelled-date-order.js +60 -15
- package/dist/structure/labelled-date-order.js.map +1 -1
- package/dist/structure/labelled-order.d.ts +7 -1
- package/dist/structure/labelled-order.d.ts.map +1 -1
- package/dist/structure/labelled-order.js +10 -3
- package/dist/structure/labelled-order.js.map +1 -1
- package/dist/structure/line-amount-label.d.ts.map +1 -1
- package/dist/structure/line-amount-label.js +2 -1
- package/dist/structure/line-amount-label.js.map +1 -1
- package/dist/structure/line-amount.d.ts +0 -5
- package/dist/structure/line-amount.d.ts.map +1 -1
- package/dist/structure/line-amount.js +1 -10
- package/dist/structure/line-amount.js.map +1 -1
- package/dist/structure/prose-total.d.ts +8 -0
- package/dist/structure/prose-total.d.ts.map +1 -1
- package/dist/structure/prose-total.js +70 -2
- package/dist/structure/prose-total.js.map +1 -1
- package/dist/structure/runs.d.ts.map +1 -1
- package/dist/structure/runs.js +9 -1
- package/dist/structure/runs.js.map +1 -1
- package/dist/structure/signed-date-line.d.ts +15 -0
- package/dist/structure/signed-date-line.d.ts.map +1 -0
- package/dist/structure/signed-date-line.js +68 -0
- package/dist/structure/signed-date-line.js.map +1 -0
- package/dist/structure/start-end-range.d.ts +30 -0
- package/dist/structure/start-end-range.d.ts.map +1 -0
- package/dist/structure/start-end-range.js +79 -0
- package/dist/structure/start-end-range.js.map +1 -0
- package/dist/structure/tax.d.ts.map +1 -1
- package/dist/structure/tax.js +5 -3
- package/dist/structure/tax.js.map +1 -1
- package/dist/structure/total-line.d.ts.map +1 -1
- package/dist/structure/total-line.js +10 -6
- package/dist/structure/total-line.js.map +1 -1
- package/dist/structure/total.d.ts +2 -0
- package/dist/structure/total.d.ts.map +1 -1
- package/dist/structure/total.js +34 -3
- package/dist/structure/total.js.map +1 -1
- package/dist/subject-codes.d.ts +21 -0
- package/dist/subject-codes.d.ts.map +1 -0
- package/dist/subject-codes.js +79 -0
- package/dist/subject-codes.js.map +1 -0
- package/dist/unlabelled-references.d.ts +6 -2
- package/dist/unlabelled-references.d.ts.map +1 -1
- package/dist/unlabelled-references.js +11 -10
- package/dist/unlabelled-references.js.map +1 -1
- package/genres.yaml +17 -0
- package/package.json +4 -4
- package/profiles/listing.yaml +1 -1
- package/profiles/property-sale.yaml +21 -0
- package/rules/closing-balance-mismatch.yaml +3 -3
- package/rules/date-range-reversed.yaml +4 -4
- package/rules/due-before-issue.yaml +9 -5
- package/rules/fact-conflict.yaml +2 -2
- package/rules/formula-amount-mismatch.yaml +67 -0
- package/rules/heading-echo.yaml +2 -2
- package/rules/installment-total-mismatch.yaml +9 -4
- package/rules/internal-jargon.yaml +2 -2
- package/rules/latin-spacing.yaml +4 -4
- package/rules/list-item-form-mix.yaml +1 -1
- package/rules/name-variant.yaml +5 -2
- package/rules/no-lead.yaml +2 -2
- package/rules/number-without-unit.yaml +2 -2
- package/rules/orthographic-variant.yaml +3 -3
- package/rules/reference-number-variant.yaml +3 -3
- package/rules/toki-as-condition.yaml +71 -0
- package/rules/total-mismatch.yaml +4 -4
- package/rules/unknown-word.yaml +2 -2
- package/rules/use-before-definition.yaml +2 -2
- package/src/api.ts +11 -2
- package/src/cli-text.ts +6 -4
- package/src/commands/grade.ts +75 -23
- package/src/company-kana-spelling.ts +22 -2
- package/src/company-name-ending.ts +50 -0
- package/src/company-names.ts +52 -17
- package/src/cross-run.ts +1 -0
- package/src/custom/no-rewrite.ts +2 -0
- package/src/custom/parse.ts +18 -2
- package/src/detectors/age-words.ts +12 -0
- package/src/detectors/agreement-slip.ts +3 -0
- package/src/detectors/annual-pay-mismatch.ts +3 -18
- package/src/detectors/blank-structure.ts +4 -3
- package/src/detectors/closing-balance-mismatch.ts +1 -0
- package/src/detectors/code-fences.ts +10 -0
- package/src/detectors/condition-toki.ts +60 -0
- package/src/detectors/conjugated-entry.ts +53 -0
- package/src/detectors/content-morphemes.ts +2 -1
- package/src/detectors/cross-facts.ts +2 -1
- package/src/detectors/custom.ts +9 -2
- package/src/detectors/document-shape.ts +8 -0
- package/src/detectors/due-date.ts +17 -7
- package/src/detectors/fact-consistency.ts +13 -17
- package/src/detectors/form-line.ts +47 -0
- package/src/detectors/formula-amount.ts +43 -0
- package/src/detectors/heading-claim.ts +76 -0
- package/src/detectors/heading-echo.ts +32 -1
- package/src/detectors/installment-total-mismatch.ts +35 -24
- package/src/detectors/list-parallel.ts +46 -7
- package/src/detectors/name-variant.ts +45 -8
- package/src/detectors/number-unit.ts +35 -9
- package/src/detectors/orthographic-variant.ts +5 -3
- package/src/detectors/orthography.ts +17 -8
- package/src/detectors/per-unit-words.ts +24 -0
- package/src/detectors/period-multiple-mismatch.ts +2 -18
- package/src/detectors/reference-number-variant.ts +27 -5
- package/src/detectors/registry/condition-toki.ts +4 -0
- package/src/detectors/registry/formula-amount.ts +4 -0
- package/src/detectors/rent-multiple-mismatch.ts +2 -18
- package/src/detectors/row-sentence-amounts.ts +5 -20
- package/src/detectors/stated-amounts.ts +20 -0
- package/src/detectors/step-time-sum.ts +2 -1
- package/src/detectors/structure-tree.ts +7 -2
- package/src/detectors/team.ts +13 -7
- package/src/detectors/time-order.ts +6 -3
- package/src/detectors/unknown-word.ts +20 -2
- package/src/detectors/use-interval.ts +8 -4
- package/src/detectors/use-maximum.ts +3 -1
- package/src/eval.ts +3 -3
- package/src/extension/document-view.ts +18 -9
- package/src/extension/entry-genres.ts +15 -0
- package/src/extension/module-detector.ts +9 -4
- package/src/extension/plugin-parse.ts +29 -7
- package/src/extension/problem-text.ts +4 -0
- package/src/facts/age-values.ts +24 -2
- package/src/facts/column-units.ts +67 -0
- package/src/facts/fact-values.ts +11 -1
- package/src/facts/labelled-facts.ts +16 -2
- package/src/facts/per-unit.ts +105 -0
- package/src/facts/row-sentence-amounts.ts +4 -26
- package/src/facts/table-facts.ts +7 -4
- package/src/facts/term-values.ts +94 -13
- package/src/fix-plan/text.ts +2 -2
- package/src/grade/baseline.ts +20 -12
- package/src/grade/expect-read.ts +65 -0
- package/src/grade/expect.ts +74 -0
- package/src/grade/grade-item.ts +14 -3
- package/src/grade/item.ts +48 -8
- package/src/grade/result.ts +5 -0
- package/src/grade/results-read.ts +7 -2
- package/src/grade/rubric-file.ts +46 -0
- package/src/grade/rubric.ts +77 -47
- package/src/grade/setup.ts +3 -3
- package/src/grade/stamp.ts +10 -2
- package/src/grade/task-rubric.ts +39 -0
- package/src/grade/text.ts +27 -5
- package/src/lexicon-genres.ts +11 -0
- package/src/name-char-reading.ts +16 -0
- package/src/name-end-letter.ts +52 -0
- package/src/name-neighbours.ts +2 -1
- package/src/name-separator.ts +32 -0
- package/src/name-variants.ts +54 -13
- package/src/number-name.ts +12 -3
- package/src/plugin.ts +6 -0
- package/src/profile/cue-lines.ts +14 -0
- package/src/profile/parse.ts +11 -2
- package/src/reference-numbers.ts +1 -1
- package/src/run.ts +1 -0
- package/src/spaced-person-name.ts +27 -2
- package/src/spacing-code.ts +13 -2
- package/src/spacing-minority.ts +17 -0
- package/src/structure/closing-balance.ts +46 -8
- package/src/structure/date-range.ts +4 -3
- package/src/structure/definition-use.ts +24 -1
- package/src/structure/document-context.ts +46 -0
- package/src/structure/fenced-lines.ts +28 -0
- package/src/structure/formula-amount.ts +236 -0
- package/src/structure/installment-fee.ts +201 -0
- package/src/structure/installment-total.ts +6 -6
- package/src/structure/labelled-date-order.ts +105 -21
- package/src/structure/labelled-order.ts +21 -10
- package/src/structure/line-amount-label.ts +2 -1
- package/src/structure/line-amount.ts +1 -11
- package/src/structure/prose-total.ts +81 -2
- package/src/structure/runs.ts +10 -1
- package/src/structure/signed-date-line.ts +78 -0
- package/src/structure/start-end-range.ts +120 -0
- package/src/structure/tax.ts +6 -3
- package/src/structure/total-line.ts +12 -6
- package/src/structure/total.ts +42 -3
- package/src/subject-codes.ts +105 -0
- package/src/unlabelled-references.ts +24 -16
package/src/facts/term-values.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { sameValue, type FactValue } from "./fact-values.ts";
|
|
|
2
2
|
import type { Fact } from "./labelled-facts.ts";
|
|
3
3
|
import type { TermWord } from "./document-terms.ts";
|
|
4
4
|
import { withoutEdgeMarks } from "./trim-marks.ts";
|
|
5
|
+
import { qualifiedKeyOf, type QualifierWords } from "./qualified-labels.ts";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* 文書全体で一つの値を持つ項目(保証期間、warranty period)を主語にした文の期間。名前付きの値の読み(labelled-facts)は、区切りの
|
|
@@ -24,6 +25,14 @@ export type TermValueWords = {
|
|
|
24
25
|
/** 起算の句の印(から は値の前の句の終わり、from は値の後ろの句の頭)。 */
|
|
25
26
|
readonly starts: readonly PlacedWord[];
|
|
26
27
|
readonly valueEnds: readonly string[];
|
|
28
|
+
/** 名前のすぐ後ろで、その値を何に使ったかを言う句の頭(used for the)。後ろの句ごと落としても同じ項目。 */
|
|
29
|
+
readonly restating?: readonly string[];
|
|
30
|
+
/** 名前と値を囲む型(を*として)。* が値。型の値は、期間でなくても読む。 */
|
|
31
|
+
readonly frames?: readonly string[];
|
|
32
|
+
/** 文の値に条件を付ける語(場合、if)。型の前の句にあれば読まない。 */
|
|
33
|
+
readonly conditions?: readonly string[];
|
|
34
|
+
/** 名前に条件を付ける型(*の、for *)。 */
|
|
35
|
+
readonly qualifiers?: QualifierWords;
|
|
27
36
|
};
|
|
28
37
|
|
|
29
38
|
const folded = (text: string): string => text.normalize("NFKC").toLowerCase().replace(/\s+/gu, " ").trim();
|
|
@@ -73,12 +82,35 @@ const withoutSelf = (key: string, words: TermValueWords): string => {
|
|
|
73
82
|
return (self.position === "before" ? key.slice(length) : key.slice(0, key.length - length)).trim();
|
|
74
83
|
};
|
|
75
84
|
|
|
76
|
-
/**
|
|
77
|
-
|
|
85
|
+
/** 語彙表の項目の形。語彙表の語は条件の付いた名前の形(扶養親族の数 は 数 (扶養親族))なので、名前もその形にして比べる。 */
|
|
86
|
+
const termOf = (key: string, words: TermValueWords): string | undefined => {
|
|
87
|
+
const qualified = words.qualifiers === undefined ? key : qualifiedKeyOf(key, words.qualifiers);
|
|
88
|
+
return words.terms.map((word) => folded(word.pattern)).find((pattern) => pattern === key || pattern === qualified);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** 何に使ったかを言う句から後ろを落とした名前(number of dependants used for the allowance の number of dependants)。 */
|
|
92
|
+
const withoutRestating = (key: string, words: TermValueWords): string | undefined =>
|
|
93
|
+
(words.restating ?? [])
|
|
94
|
+
.flatMap((word) => {
|
|
95
|
+
const mark = ` ${folded(word)}`;
|
|
96
|
+
const at = key.indexOf(mark);
|
|
97
|
+
const next = key.charAt(at + mark.length);
|
|
98
|
+
return at > 0 && (next === "" || next === " ") ? [key.slice(0, at)] : [];
|
|
99
|
+
})
|
|
100
|
+
.find((head) => termOf(head, words) !== undefined);
|
|
101
|
+
|
|
102
|
+
/** 名前が語彙表の項目なら、その語の形。restated: 何に使ったかを言う句を落として、項目になった。 */
|
|
103
|
+
const termKeyWith = (label: string, words: TermValueWords): { readonly key: string; readonly restated: boolean } | undefined => {
|
|
78
104
|
const key = withoutSelf(withoutDeterminer(folded(withoutEdgeMarks(label)), words), words);
|
|
79
|
-
|
|
105
|
+
const term = termOf(key, words);
|
|
106
|
+
if (term !== undefined) return { key: term, restated: false };
|
|
107
|
+
const restated = withoutRestating(key, words);
|
|
108
|
+
return restated === undefined ? undefined : { key: restated, restated: true };
|
|
80
109
|
};
|
|
81
110
|
|
|
111
|
+
/** 名前が語彙表の項目なら、その語の形。 */
|
|
112
|
+
export const termKeyOf = (label: string, words: TermValueWords): string | undefined => termKeyWith(label, words)?.key;
|
|
113
|
+
|
|
82
114
|
type Span = { readonly start: number; readonly end: number };
|
|
83
115
|
|
|
84
116
|
const valuesIn = (values: readonly FactValue[], span: Span): FactValue[] => values.filter((value) => span.start <= value.start && value.end <= span.end);
|
|
@@ -91,7 +123,7 @@ const separatorsIn = (text: string, separator: string): number[] =>
|
|
|
91
123
|
.map((match) => match.index)
|
|
92
124
|
.filter((index) => !isLatin(separator) || (text.charAt(index - 1) === " " && [" ", ""].includes(text.charAt(index + separator.length))));
|
|
93
125
|
|
|
94
|
-
type Subject = { readonly key: string; readonly label: string; readonly lead: Span | undefined; readonly restStart: number };
|
|
126
|
+
type Subject = { readonly key: string; readonly label: string; readonly lead: Span | undefined; readonly restStart: number; readonly restated: boolean };
|
|
95
127
|
|
|
96
128
|
/** 文の頭から区切りまでの主語。読点の前は条件の句。 */
|
|
97
129
|
const subjectOf = (source: string, from: number, to: number, words: TermValueWords): Subject | undefined => {
|
|
@@ -101,9 +133,10 @@ const subjectOf = (source: string, from: number, to: number, words: TermValueWor
|
|
|
101
133
|
const head = clause.slice(0, index);
|
|
102
134
|
const comma = [...head.matchAll(CLAUSE_COMMAS)].at(-1)?.index;
|
|
103
135
|
const label = withoutEdgeMarks(comma === undefined ? head : head.slice(comma + 1));
|
|
104
|
-
const
|
|
105
|
-
if (
|
|
106
|
-
|
|
136
|
+
const term = termKeyWith(label, words);
|
|
137
|
+
if (term === undefined) continue;
|
|
138
|
+
const lead = comma === undefined ? undefined : { start: from, end: from + comma };
|
|
139
|
+
return { ...term, label, lead, restStart: from + index + separator.length };
|
|
107
140
|
}
|
|
108
141
|
}
|
|
109
142
|
return undefined;
|
|
@@ -170,10 +203,10 @@ const isWorkedExample = (lead: Span, starts: readonly Span[], values: readonly F
|
|
|
170
203
|
return starts.some((span) => valuesIn(values, span).some((value) => value.kind === "date" && sameValue(date, value)));
|
|
171
204
|
};
|
|
172
205
|
|
|
173
|
-
/** 主語と区切りの後ろの期間(保証期間は、お買い上げ日から24か月です)。 */
|
|
174
|
-
const subjectFact = (source: string, value: FactValue, values: readonly FactValue[], words: TermValueWords): Fact | undefined => {
|
|
206
|
+
/** 主語と区切りの後ろの期間(保証期間は、お買い上げ日から24か月です)。restatedOnly: 何に使ったかを言う句の付いた主語だけ。 */
|
|
207
|
+
const subjectFact = (source: string, value: FactValue, values: readonly FactValue[], words: TermValueWords, restatedOnly = false): Fact | undefined => {
|
|
175
208
|
const subject = subjectOf(source, clauseStartOf(source, value.start), value.start, words);
|
|
176
|
-
if (subject === undefined) return undefined;
|
|
209
|
+
if (subject === undefined || (restatedOnly && !subject.restated)) return undefined;
|
|
177
210
|
const before = startBeforeValue(source, { start: subject.restStart, end: value.start }, values, words);
|
|
178
211
|
if (before === undefined) return undefined;
|
|
179
212
|
const after = startAfterValue(source, value.end, values, words);
|
|
@@ -226,11 +259,59 @@ const unitNamedFact = (source: string, value: FactValue, words: TermValueWords):
|
|
|
226
259
|
|
|
227
260
|
const DURATION_UNITS = new Set(["day", "week", "month", "year"]);
|
|
228
261
|
|
|
229
|
-
/**
|
|
262
|
+
/** 型の前と後ろの字(を*として の を と として)。 */
|
|
263
|
+
const frameParts = (frame: string): readonly [string, string] | undefined => {
|
|
264
|
+
const at = frame.indexOf("*");
|
|
265
|
+
return at <= 0 || at === frame.length - 1 ? undefined : [frame.slice(0, at), frame.slice(at + 1)];
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
/** 型の前の句(扶養控除は、)が、値も条件の語も含まないか。条件の句(登録された場合、)の後ろの値は、その場合の値。 */
|
|
269
|
+
const isPlainLead = (source: string, lead: Span, values: readonly FactValue[], words: TermValueWords): boolean => {
|
|
270
|
+
const written = folded(source.slice(lead.start, lead.end));
|
|
271
|
+
return valuesIn(values, lead).length === 0 && !(words.conditions ?? []).some((word) => written.includes(folded(word)));
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
/** 型の名前(扶養親族の数を2人として の 扶養親族の数)。名前は読点の後ろから。 */
|
|
275
|
+
const framedFact = (
|
|
276
|
+
source: string,
|
|
277
|
+
value: FactValue,
|
|
278
|
+
values: readonly FactValue[],
|
|
279
|
+
words: TermValueWords,
|
|
280
|
+
[open, close]: readonly [string, string],
|
|
281
|
+
): Fact | undefined => {
|
|
282
|
+
const from = clauseStartOf(source, value.start);
|
|
283
|
+
const labelEnd = value.start - open.length;
|
|
284
|
+
if (labelEnd <= from || source.slice(labelEnd, value.start) !== open || !source.startsWith(close, value.end)) return undefined;
|
|
285
|
+
const head = source.slice(from, labelEnd);
|
|
286
|
+
const comma = [...head.matchAll(CLAUSE_COMMAS)].at(-1)?.index ?? -1;
|
|
287
|
+
const label = withoutEdgeMarks(head.slice(comma + 1));
|
|
288
|
+
const key = termKeyOf(label, words);
|
|
289
|
+
if (key === undefined || !isPlainLead(source, { start: from, end: from + Math.max(comma, 0) }, values, words)) return undefined;
|
|
290
|
+
return { label, key, value };
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/** 名前と値を囲む型の値(扶養控除は、扶養親族の数を2人として計算しています)。 */
|
|
294
|
+
const frameFact = (source: string, value: FactValue, values: readonly FactValue[], words: TermValueWords): Fact | undefined =>
|
|
295
|
+
(words.frames ?? [])
|
|
296
|
+
.flatMap((frame) => {
|
|
297
|
+
const parts = frameParts(frame);
|
|
298
|
+
const fact = parts === undefined ? undefined : framedFact(source, value, values, words, parts);
|
|
299
|
+
return fact === undefined ? [] : [fact];
|
|
300
|
+
})
|
|
301
|
+
.at(0);
|
|
302
|
+
|
|
303
|
+
const durationFact = (source: string, value: FactValue, values: readonly FactValue[], words: TermValueWords): Fact | undefined =>
|
|
304
|
+
bracketFact(source, value, words) ?? subjectFact(source, value, values, words) ?? unitNamedFact(source, value, words);
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 語彙表の項目を主語にした文の、期間の値。期間でない数は、何に使ったかを言う句の付いた主語(the number of dependants used for the
|
|
308
|
+
* allowance is 2)か、名前と値を囲む型(扶養親族の数を2人として)のときだけ。values は文書の中の順の値(日付を含む)。
|
|
309
|
+
*/
|
|
230
310
|
export const termValues = (source: string, values: readonly FactValue[], words: TermValueWords): Fact[] =>
|
|
231
311
|
values
|
|
232
|
-
.filter((value) => value.kind === "quantity"
|
|
312
|
+
.filter((value) => value.kind === "quantity")
|
|
233
313
|
.flatMap((value) => {
|
|
234
|
-
const
|
|
314
|
+
const read = DURATION_UNITS.has(value.unit) ? durationFact(source, value, values, words) : subjectFact(source, value, values, words, true);
|
|
315
|
+
const fact = read ?? frameFact(source, value, values, words);
|
|
235
316
|
return fact === undefined ? [] : [fact];
|
|
236
317
|
});
|
package/src/fix-plan/text.ts
CHANGED
|
@@ -109,7 +109,7 @@ export const FIX_PLAN_TEXT: Texts<FixPlanText> = {
|
|
|
109
109
|
spots: "見つけた箇所",
|
|
110
110
|
at: (line) => `${String(line)} 行目`,
|
|
111
111
|
notRunHeading: "動かなかったルール",
|
|
112
|
-
notExperimental: "
|
|
112
|
+
notExperimental: "試験中のルールは動かしていません。--experimental を付けると見ます。",
|
|
113
113
|
checksHeading: "直したあとの確かめ",
|
|
114
114
|
saveAs: (path) => `書き直したものを ${path} に保存して、次を実行します。`,
|
|
115
115
|
checksNote:
|
|
@@ -174,7 +174,7 @@ export const FIX_PLAN_TEXT: Texts<FixPlanText> = {
|
|
|
174
174
|
spots: "Spots",
|
|
175
175
|
at: (line) => `line ${String(line)}`,
|
|
176
176
|
notRunHeading: "Rules that did not run",
|
|
177
|
-
notExperimental: "The experimental rules did not run
|
|
177
|
+
notExperimental: "The experimental rules did not run. Add --experimental to run them.",
|
|
178
178
|
checksHeading: "Check after rewriting",
|
|
179
179
|
saveAs: (path) => `Save the rewrite as ${path} and run:`,
|
|
180
180
|
checksNote:
|
package/src/grade/baseline.ts
CHANGED
|
@@ -60,13 +60,22 @@ type Pair = { readonly before: GradeResult; readonly after: GradeResult };
|
|
|
60
60
|
|
|
61
61
|
const countIn = (result: GradeResult, rule: string): number => result.findings.filter((finding) => finding.rule === rule).length;
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
/** The rules the rubric names for one output: with rubric tasks, its task's rules over the top level's. */
|
|
64
|
+
export type RubricRulesOf = (result: GradeResult) => ReadonlySet<string>;
|
|
65
|
+
|
|
66
|
+
/** A rule's movement, and the outputs it rose in whose own rubric names it: only those are a regression. */
|
|
67
|
+
type Moved = { readonly movement: RuleMovement; readonly regressedIn: readonly string[] };
|
|
68
|
+
|
|
69
|
+
const movementsOf = (pairs: readonly Pair[], rubricRulesOf: RubricRulesOf): Moved[] =>
|
|
64
70
|
rateRows([pairs.map((pair) => pair.before), pairs.map((pair) => pair.after)]).flatMap(({ rule, unit, rates: [before = 0, after = 0] }) => {
|
|
65
71
|
const inUnit = pairs.filter((pair) => pair.after.size.unit === unit);
|
|
66
|
-
const
|
|
72
|
+
const increased = inUnit.filter((pair) => countIn(pair.after, rule) > countIn(pair.before, rule));
|
|
73
|
+
const increasedIn = increased.map((pair) => resultName(pair.after));
|
|
67
74
|
const decreasedIn = inUnit.filter((pair) => countIn(pair.after, rule) < countIn(pair.before, rule)).map((pair) => resultName(pair.after));
|
|
68
75
|
if (increasedIn.length === 0 && decreasedIn.length === 0 && before === after) return [];
|
|
69
|
-
|
|
76
|
+
const graded = (pair: Pair): boolean => rubricRulesOf(pair.after).has(rule);
|
|
77
|
+
const regressedIn = increased.filter(graded).map((pair) => resultName(pair.after));
|
|
78
|
+
return [{ movement: { rule, unit, before, after, increasedIn, decreasedIn, inRubric: inUnit.some(graded) }, regressedIn }];
|
|
70
79
|
});
|
|
71
80
|
|
|
72
81
|
const factKey = (fact: GradeFact): string => `${fact.kind}\n${fact.key}`;
|
|
@@ -100,11 +109,9 @@ const penaltyOf = (results: readonly GradeResult[]): number => results.reduce((s
|
|
|
100
109
|
|
|
101
110
|
const scored = (pairs: readonly Pair[]): boolean => pairs.some((pair) => pair.after.score !== undefined);
|
|
102
111
|
|
|
103
|
-
const regressionsOf = (newlyFailed: readonly string[],
|
|
112
|
+
const regressionsOf = (newlyFailed: readonly string[], moved: readonly Moved[], penalty: Comparison["penalty"]): string[] => [
|
|
104
113
|
...newlyFailed.map((id) => `${id}: passed, now fails`),
|
|
105
|
-
...
|
|
106
|
-
.filter((movement) => movement.inRubric && movement.increasedIn.length > 0)
|
|
107
|
-
.map((movement) => `rules.${movement.rule}: more findings in ${movement.increasedIn.join(", ")}`),
|
|
114
|
+
...moved.filter((entry) => entry.regressedIn.length > 0).map((entry) => `rules.${entry.movement.rule}: more findings in ${entry.regressedIn.join(", ")}`),
|
|
108
115
|
...(penalty !== undefined && penalty.after > penalty.before ? [`score.penalty ${String(penalty.before)} → ${String(penalty.after)}`] : []),
|
|
109
116
|
];
|
|
110
117
|
|
|
@@ -112,9 +119,10 @@ const pairRead = (pair: Pair): boolean => sameReading([pair.before, pair.after])
|
|
|
112
119
|
|
|
113
120
|
/**
|
|
114
121
|
* `after` compared with `before`, output by output: an output is paired with the earlier one of its id and variant.
|
|
115
|
-
* `rubricRules`: the rules the rubric names, whose increase is a regression.
|
|
122
|
+
* `rubricRules`: the rules the rubric names, whose increase is a regression; per output when the rubric has tasks.
|
|
116
123
|
*/
|
|
117
|
-
export const compareRuns = (before: readonly GradeResult[], after: readonly GradeResult[], rubricRules: ReadonlySet<string>): Comparison => {
|
|
124
|
+
export const compareRuns = (before: readonly GradeResult[], after: readonly GradeResult[], rubricRules: ReadonlySet<string> | RubricRulesOf): Comparison => {
|
|
125
|
+
const rubricRulesOf: RubricRulesOf = typeof rubricRules === "function" ? rubricRules : () => rubricRules;
|
|
118
126
|
const earlier = new Map(before.map((result) => [resultKey(result), result]));
|
|
119
127
|
const keys = new Set(after.map(resultKey));
|
|
120
128
|
const matched = after.flatMap((result) => {
|
|
@@ -122,7 +130,7 @@ export const compareRuns = (before: readonly GradeResult[], after: readonly Grad
|
|
|
122
130
|
return match === undefined ? [] : [{ before: match, after: result }];
|
|
123
131
|
});
|
|
124
132
|
const pairs = matched.filter(pairRead);
|
|
125
|
-
const
|
|
133
|
+
const moved = movementsOf(pairs, rubricRulesOf);
|
|
126
134
|
const newlyFailed = pairs.filter((pair) => pair.before.pass && !pair.after.pass).map((pair) => resultName(pair.after));
|
|
127
135
|
const penalty = scored(pairs) ? { before: penaltyOf(pairs.map((pair) => pair.before)), after: penaltyOf(pairs.map((pair) => pair.after)) } : undefined;
|
|
128
136
|
return {
|
|
@@ -130,11 +138,11 @@ export const compareRuns = (before: readonly GradeResult[], after: readonly Grad
|
|
|
130
138
|
onlyBefore: before.filter((result) => !keys.has(resultKey(result))).map(resultName),
|
|
131
139
|
onlyAfter: after.filter((result) => !earlier.has(resultKey(result))).map(resultName),
|
|
132
140
|
readOtherwise: matched.filter((pair) => !pairRead(pair)).map((pair) => resultName(pair.after)),
|
|
133
|
-
rules,
|
|
141
|
+
rules: moved.map((entry) => entry.movement),
|
|
134
142
|
newlyFailed,
|
|
135
143
|
newlyPassed: pairs.filter((pair) => !pair.before.pass && pair.after.pass).map((pair) => resultName(pair.after)),
|
|
136
144
|
items: pairs.map(itemChange).filter((change) => change.dropped.length + change.added.length + change.citations.length > 0),
|
|
137
145
|
...(penalty === undefined ? {} : { penalty }),
|
|
138
|
-
regressions: regressionsOf(newlyFailed,
|
|
146
|
+
regressions: regressionsOf(newlyFailed, moved, penalty),
|
|
139
147
|
};
|
|
140
148
|
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { documentLanguage } from "../check-source.ts";
|
|
2
|
+
import { factsOf } from "../commands/compare.ts";
|
|
3
|
+
import { documentFromSource } from "../commands/read-document.ts";
|
|
4
|
+
import type { Compared } from "../compare/outcome.ts";
|
|
5
|
+
import { resolveGenre } from "../resolve-genre.ts";
|
|
6
|
+
import { OUTPUT_PATH, type ItemReading } from "./checks.ts";
|
|
7
|
+
import { EXPECT_LISTS, entryPath, expectAgainst, factOfEntry, type GradeExpect, type GradeExpectResult, type ReadEntry } from "./expect.ts";
|
|
8
|
+
import type { GradeItem, ItemProblem } from "./item.ts";
|
|
9
|
+
|
|
10
|
+
// An item's `expect` entries read as compare reads a document, in the output's language and genre, so an entry and
|
|
11
|
+
// the output spell one fact alike.
|
|
12
|
+
|
|
13
|
+
const ENTRY_PATH = "expect.md";
|
|
14
|
+
|
|
15
|
+
type EntryRead = { readonly entry: ReadEntry } | { readonly problem: Omit<ItemProblem, "line"> };
|
|
16
|
+
|
|
17
|
+
const readEntry = async (list: ReadEntry["list"], index: number, entry: string, reading: ItemReading & { readonly language: string }): Promise<EntryRead> => {
|
|
18
|
+
const prose = await documentFromSource(ENTRY_PATH, entry, { language: reading.language, genre: reading.genre }, reading.config, true);
|
|
19
|
+
const facts = factsOf(ENTRY_PATH, prose, reading.config.names ?? []);
|
|
20
|
+
const found = factOfEntry(entry, facts.extraction.atoms);
|
|
21
|
+
if ("unread" in found)
|
|
22
|
+
return { problem: { kind: found.unread === "no-fact" ? "expect-no-fact" : "expect-several-facts", detail: `${entryPath(list, index)} ${entry}` } };
|
|
23
|
+
return { entry: { list, index, entry, read: { path: ENTRY_PATH, extraction: { ...facts.extraction, atoms: [found.fact] } } } };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const readEntries = (expect: GradeExpect, reading: ItemReading & { readonly language: string }): Promise<EntryRead[]> =>
|
|
27
|
+
Promise.all(EXPECT_LISTS.flatMap((list) => expect[list].map((entry, index) => readEntry(list, index, entry, reading))));
|
|
28
|
+
|
|
29
|
+
/** The output's own facts, read as `chaff compare` reads the document after. */
|
|
30
|
+
const outputFacts = async (output: string, reading: ItemReading & { readonly language: string }): Promise<Compared> => {
|
|
31
|
+
const prose = await documentFromSource(OUTPUT_PATH, output, { language: reading.language, genre: reading.genre }, reading.config, true);
|
|
32
|
+
return factsOf(OUTPUT_PATH, prose, reading.config.names ?? []);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** The language and genre the output is graded in, as checkSource chooses them. */
|
|
36
|
+
const itemReading = (item: GradeItem, setup: { readonly config: ItemReading["config"]; readonly genre?: string | undefined }) => ({
|
|
37
|
+
config: setup.config,
|
|
38
|
+
language: item.language ?? documentLanguage(OUTPUT_PATH, item.output, setup.config),
|
|
39
|
+
genre: resolveGenre(OUTPUT_PATH, item.output, setup.config, item.genre ?? setup.genre).genre,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/** Each entry chaff cannot read as exactly one fact, on the item's line: the run stops before grading (exit 2). */
|
|
43
|
+
export const expectProblems = async (
|
|
44
|
+
item: GradeItem,
|
|
45
|
+
line: number,
|
|
46
|
+
setup: { readonly config: ItemReading["config"]; readonly genre?: string | undefined },
|
|
47
|
+
): Promise<ItemProblem[]> => {
|
|
48
|
+
if (item.expect === undefined) return [];
|
|
49
|
+
const read = await readEntries(item.expect, itemReading(item, setup));
|
|
50
|
+
return read.flatMap((entry) => ("problem" in entry ? [{ ...entry.problem, line }] : []));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The output against the item's expect, read in the language and genre it was graded in. Throws for an entry that is
|
|
55
|
+
* not one fact: `chaff grade` refuses such an item before grading (expectProblems), so none reaches here.
|
|
56
|
+
*/
|
|
57
|
+
export const expectOfItem = async (item: GradeItem, reading: ItemReading & { readonly language: string }): Promise<GradeExpectResult | undefined> => {
|
|
58
|
+
if (item.expect === undefined) return undefined;
|
|
59
|
+
const read = await readEntries(item.expect, reading);
|
|
60
|
+
const entries = read.flatMap((entry) => {
|
|
61
|
+
if ("problem" in entry) throw new Error(`${item.id}: ${entry.problem.detail ?? ""} is not one fact`);
|
|
62
|
+
return [entry.entry];
|
|
63
|
+
});
|
|
64
|
+
return expectAgainst(entries, await outputFacts(item.output, reading));
|
|
65
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Atom } from "../compare/atom.ts";
|
|
2
|
+
import { outcomeOf, type Compared } from "../compare/outcome.ts";
|
|
3
|
+
|
|
4
|
+
// An item's known answer (`expect`): facts the output must state and facts it must not. Pure: the shape of the field,
|
|
5
|
+
// which one fact an entry states, and whether the output states it, decided as `chaff compare --distinct` decides it.
|
|
6
|
+
|
|
7
|
+
export type GradeExpect = { readonly facts: readonly string[]; readonly absent: readonly string[] };
|
|
8
|
+
|
|
9
|
+
export const EXPECT_LISTS = ["facts", "absent"] as const;
|
|
10
|
+
|
|
11
|
+
export type ExpectList = (typeof EXPECT_LISTS)[number];
|
|
12
|
+
|
|
13
|
+
const isExpectList = (key: string): key is ExpectList => EXPECT_LISTS.some((list) => list === key);
|
|
14
|
+
|
|
15
|
+
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
|
+
|
|
17
|
+
const isEntryList = (value: unknown): value is string[] => Array.isArray(value) && value.every((entry) => typeof entry === "string" && entry.trim() !== "");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The field as written, or the path of its first wrong part. A key other than facts and absent is wrong, not ignored:
|
|
21
|
+
* a misspelt list would check nothing and pass every output. A missing list is an empty one.
|
|
22
|
+
*/
|
|
23
|
+
export const expectOf = (raw: unknown): { readonly expect: GradeExpect | undefined } | { readonly wrong: string } => {
|
|
24
|
+
if (raw === undefined) return { expect: undefined };
|
|
25
|
+
if (!isRecord(raw)) return { wrong: "expect" };
|
|
26
|
+
const unknown = Object.keys(raw).find((key) => !isExpectList(key));
|
|
27
|
+
if (unknown !== undefined) return { wrong: `expect.${unknown}` };
|
|
28
|
+
const wrongList = EXPECT_LISTS.find((list) => raw[list] !== undefined && !isEntryList(raw[list]));
|
|
29
|
+
if (wrongList !== undefined) return { wrong: `expect.${wrongList}` };
|
|
30
|
+
const listOf = (list: ExpectList): readonly string[] => {
|
|
31
|
+
const value = raw[list];
|
|
32
|
+
return isEntryList(value) ? value : [];
|
|
33
|
+
};
|
|
34
|
+
return { expect: { facts: listOf("facts"), absent: listOf("absent") } };
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** The one fact an entry states, or why it states none or several. */
|
|
38
|
+
export type EntryFact = { readonly fact: Atom } | { readonly unread: "no-fact" | "several-facts" };
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The fact an entry states. Several facts are one when exactly one of them is the whole entry: a quotation 「12,000円」
|
|
42
|
+
* is read as the quotation and the number inside it, and the entry means the quotation.
|
|
43
|
+
*/
|
|
44
|
+
export const factOfEntry = (entry: string, atoms: readonly Atom[]): EntryFact => {
|
|
45
|
+
const [only] = atoms;
|
|
46
|
+
if (atoms.length === 1 && only !== undefined) return { fact: only };
|
|
47
|
+
if (atoms.length === 0) return { unread: "no-fact" };
|
|
48
|
+
const whole = atoms.filter((atom) => atom.text === entry.trim());
|
|
49
|
+
const [wholeOnly] = whole;
|
|
50
|
+
return whole.length === 1 && wholeOnly !== undefined ? { fact: wholeOnly } : { unread: "several-facts" };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Where an entry is in the item: `expect.facts[0]`. */
|
|
54
|
+
export const entryPath = (list: ExpectList, index: number): string => `expect.${list}[${String(index)}]`;
|
|
55
|
+
|
|
56
|
+
/** One entry as read: its list, its place, its text, and its document's facts with only the entry's fact kept. */
|
|
57
|
+
export type ReadEntry = { readonly list: ExpectList; readonly index: number; readonly entry: string; readonly read: Compared };
|
|
58
|
+
|
|
59
|
+
/** An entry that states one fact, compared as compare compares two documents: the output states it at least once. */
|
|
60
|
+
export const statesFact = (entry: Compared, output: Compared): boolean => outcomeOf(entry, output, undefined, "distinct").dropped.length === 0;
|
|
61
|
+
|
|
62
|
+
/** What the output was checked against: how many entries, the facts it does not state, and the absent ones it does. */
|
|
63
|
+
export type GradeExpectResult = { readonly checked: number; readonly missing: readonly string[]; readonly present: readonly string[] };
|
|
64
|
+
|
|
65
|
+
/** Each entry against the output, by the entry's text as written in the item. */
|
|
66
|
+
export const expectAgainst = (entries: readonly ReadEntry[], output: Compared): GradeExpectResult => ({
|
|
67
|
+
checked: entries.length,
|
|
68
|
+
missing: entries.filter((entry) => entry.list === "facts" && !statesFact(entry.read, output)).map((entry) => entry.entry),
|
|
69
|
+
present: entries.filter((entry) => entry.list === "absent" && statesFact(entry.read, output)).map((entry) => entry.entry),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/** The failure reasons: `expect.missing 12,000円`, `expect.present 15,000円`. Absent when the item had no expect. */
|
|
73
|
+
export const expectFailures = (result: GradeExpectResult | undefined): string[] =>
|
|
74
|
+
result === undefined ? [] : [...result.missing.map((entry) => `expect.missing ${entry}`), ...result.present.map((entry) => `expect.present ${entry}`)];
|
package/src/grade/grade-item.ts
CHANGED
|
@@ -10,11 +10,14 @@ import { ratesOf, sizeOf, type OutputSize } from "./rates.ts";
|
|
|
10
10
|
import type { GradeCitations, GradeFacts, GradeFinding, GradeResult, GradeScore, NotRunEntry, Stamp } from "./result.ts";
|
|
11
11
|
import type { Rubric } from "./rubric.ts";
|
|
12
12
|
import { rubricVerdict } from "./rubric-verdict.ts";
|
|
13
|
+
import { rubricForTask } from "./task-rubric.ts";
|
|
13
14
|
import type { GradeSettings } from "./stamp.ts";
|
|
14
15
|
import { GRADE_TEXT } from "./text.ts";
|
|
15
16
|
import { aiScoreOfDocument } from "../ai-score/of-document.ts";
|
|
16
17
|
import { gradeAiScoreOf } from "./ai-score.ts";
|
|
17
|
-
import { defaultVerdict, type Verdict } from "./verdict.ts";
|
|
18
|
+
import { defaultVerdict, verdictOf, type Verdict } from "./verdict.ts";
|
|
19
|
+
import { expectFailures, type GradeExpectResult } from "./expect.ts";
|
|
20
|
+
import { expectOfItem } from "./expect-read.ts";
|
|
18
21
|
|
|
19
22
|
/** One run's settings, its `grade:` rubric when chaff.yaml has one, and its stamp, which every result carries. */
|
|
20
23
|
export type GradeSetup = GradeSettings & { readonly stamp: Stamp; readonly rubric?: Rubric | undefined };
|
|
@@ -34,6 +37,7 @@ type Checked = {
|
|
|
34
37
|
readonly facts: GradeFacts | null;
|
|
35
38
|
readonly cited: CitationsRead | undefined;
|
|
36
39
|
readonly contexts: ContextsRead | undefined;
|
|
40
|
+
readonly expect: GradeExpectResult | undefined;
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
const checkItem = async (item: GradeItem, setup: GradeSetup): Promise<Checked> => {
|
|
@@ -50,6 +54,7 @@ const checkItem = async (item: GradeItem, setup: GradeSetup): Promise<Checked> =
|
|
|
50
54
|
facts: item.reference === undefined ? null : await factsAgainst(texts, reading, allowedBy(setup.rubric)),
|
|
51
55
|
cited: item.citations === undefined ? undefined : await citationsAgainst(item, item.citations, reading, ui),
|
|
52
56
|
contexts: await contextsOf(item, check.language, reading, setup.rubric),
|
|
57
|
+
expect: await expectOfItem(item, { ...reading, language: check.language }),
|
|
53
58
|
};
|
|
54
59
|
};
|
|
55
60
|
|
|
@@ -97,12 +102,17 @@ const judge = (item: GradeItem, checked: Checked, setup: GradeSetup): { readonly
|
|
|
97
102
|
};
|
|
98
103
|
|
|
99
104
|
/** Grades one output (spec §29.3): its findings and their rates, the facts against its reference, its quotations, and pass or fail. */
|
|
100
|
-
export const gradeItem = async (item: GradeItem,
|
|
105
|
+
export const gradeItem = async (item: GradeItem, runSetup: GradeSetup): Promise<GradeResult> => {
|
|
106
|
+
const setup = item.task === undefined ? runSetup : { ...runSetup, rubric: rubricForTask(runSetup.rubric, item.task) };
|
|
101
107
|
const checked = await checkItem(item, setup);
|
|
102
|
-
const
|
|
108
|
+
const judged = judge(item, checked, setup);
|
|
109
|
+
const { score } = judged;
|
|
110
|
+
// expect is the item's own answer, so it fails the output with or without a `grade:` rubric.
|
|
111
|
+
const verdict = verdictOf([...judged.verdict.failedBecause, ...expectFailures(checked.expect)]);
|
|
103
112
|
return {
|
|
104
113
|
id: item.id,
|
|
105
114
|
...(item.variant === undefined ? {} : { variant: item.variant }),
|
|
115
|
+
...(item.task === undefined ? {} : { task: item.task }),
|
|
106
116
|
language: checked.check.language,
|
|
107
117
|
genre: checked.check.genre.genre,
|
|
108
118
|
size: checked.size,
|
|
@@ -115,6 +125,7 @@ export const gradeItem = async (item: GradeItem, setup: GradeSetup): Promise<Gra
|
|
|
115
125
|
facts: checked.facts,
|
|
116
126
|
citations: citationsOf(checked),
|
|
117
127
|
...(checked.contexts === undefined ? {} : { contexts: checked.contexts.contexts }),
|
|
128
|
+
...(checked.expect === undefined ? {} : { expect: checked.expect }),
|
|
118
129
|
...(score === undefined ? {} : { score }),
|
|
119
130
|
aiScore: gradeAiScoreOf(aiScoreOfDocument(checked.check.doc, checked.check.rules, checked.check.genre.genre)),
|
|
120
131
|
pass: verdict.pass,
|
package/src/grade/item.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// One line of `chaff grade`'s input: a model's output and what to check it against. Pure: reads text, returns items
|
|
2
2
|
// or what is wrong with them, so a harness that writes the file can be told which line to fix.
|
|
3
|
+
import { expectOf, type GradeExpect } from "./expect.ts";
|
|
3
4
|
import { resultKey } from "./result-name.ts";
|
|
4
5
|
|
|
5
6
|
export type GradeCitation = { readonly source: string; readonly address: string; readonly quote: string };
|
|
@@ -18,10 +19,14 @@ export type GradeItem = {
|
|
|
18
19
|
readonly contexts?: readonly string[] | undefined;
|
|
19
20
|
/** The prompt, model or setting that produced this output. Outputs of several variants are compared on the same ids. */
|
|
20
21
|
readonly variant?: string | undefined;
|
|
22
|
+
/** The rubric task this item is graded under (`grade.tasks.<name>`). Undefined: the top-level rubric. */
|
|
23
|
+
readonly task?: string | undefined;
|
|
24
|
+
/** Facts the output must state and facts it must not: a question's known answer. Undefined when not given. */
|
|
25
|
+
readonly expect?: GradeExpect | undefined;
|
|
21
26
|
};
|
|
22
27
|
|
|
23
28
|
/** The fields an item already has a meaning for: none of them can name a variant. */
|
|
24
|
-
export const ITEM_FIELDS: readonly string[] = ["id", "output", "reference", "sources", "citations", "contexts", "language", "genre"];
|
|
29
|
+
export const ITEM_FIELDS: readonly string[] = ["id", "output", "reference", "sources", "citations", "contexts", "language", "genre", "expect", "task"];
|
|
25
30
|
|
|
26
31
|
/** Which field names an output's variant, and whether every line must have it (the key was given on the command line). */
|
|
27
32
|
export type VariantField = { readonly key: string; readonly required: boolean };
|
|
@@ -44,13 +49,22 @@ export type ItemProblemKind =
|
|
|
44
49
|
| "which-source"
|
|
45
50
|
| "unknown-language"
|
|
46
51
|
| "unknown-genre"
|
|
52
|
+
| "not-expect"
|
|
53
|
+
| "expect-no-fact"
|
|
54
|
+
| "expect-several-facts"
|
|
55
|
+
| "unknown-task"
|
|
47
56
|
| "empty";
|
|
48
57
|
|
|
49
58
|
/** Why a line cannot be graded. `detail`: the field, the value or the parser's message; `first`: the line an id was first on. */
|
|
50
59
|
export type ItemProblem = { readonly kind: ItemProblemKind; readonly line: number; readonly detail?: string; readonly first?: number };
|
|
51
60
|
|
|
52
61
|
/** What an item may name: the languages chaff can load and the genres it knows. */
|
|
53
|
-
export type ItemVocabulary = {
|
|
62
|
+
export type ItemVocabulary = {
|
|
63
|
+
readonly isLanguage: (language: string) => boolean;
|
|
64
|
+
readonly genres: readonly string[];
|
|
65
|
+
/** The tasks the rubric defines. None given: an item naming a task is refused, as there is no rubric for it. */
|
|
66
|
+
readonly tasks?: readonly string[] | undefined;
|
|
67
|
+
};
|
|
54
68
|
|
|
55
69
|
const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
56
70
|
|
|
@@ -129,6 +143,27 @@ const variantOf = (raw: Record<string, unknown>, key: string, line: number): Par
|
|
|
129
143
|
return typeof value === "string" && value !== "" ? { value } : { problem: { kind: "not-string", line, detail: key } };
|
|
130
144
|
};
|
|
131
145
|
|
|
146
|
+
/** The item's task: a non-empty string the rubric defines, or none. Never a silent fall back to the top-level rubric. */
|
|
147
|
+
const taskOf = (raw: unknown, vocabulary: ItemVocabulary, line: number): Parsed<string | undefined> => {
|
|
148
|
+
if (raw === undefined) return { value: undefined };
|
|
149
|
+
if (typeof raw !== "string" || raw === "") return { problem: { kind: "not-string", line, detail: "task" } };
|
|
150
|
+
return (vocabulary.tasks ?? []).includes(raw) ? { value: raw } : { problem: { kind: "unknown-task", line, detail: raw } };
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/** The item's variant and task, each written only when given, checked in that order. */
|
|
154
|
+
const labelsOf = (
|
|
155
|
+
raw: Record<string, unknown>,
|
|
156
|
+
line: number,
|
|
157
|
+
vocabulary: ItemVocabulary,
|
|
158
|
+
variantKey: string,
|
|
159
|
+
): Parsed<{ readonly variant?: string; readonly task?: string }> => {
|
|
160
|
+
const variant = variantOf(raw, variantKey, line);
|
|
161
|
+
if ("problem" in variant) return variant;
|
|
162
|
+
const task = taskOf(raw["task"], vocabulary, line);
|
|
163
|
+
if ("problem" in task) return task;
|
|
164
|
+
return { value: { ...(variant.value === undefined ? {} : { variant: variant.value }), ...(task.value === undefined ? {} : { task: task.value }) } };
|
|
165
|
+
};
|
|
166
|
+
|
|
132
167
|
const itemOf = (raw: Record<string, unknown>, line: number, vocabulary: ItemVocabulary, variantKey: string): Parsed<GradeItem> => {
|
|
133
168
|
const id = raw["id"];
|
|
134
169
|
if (typeof id !== "string" || id === "") return { problem: { kind: "no-id", line } };
|
|
@@ -138,17 +173,22 @@ const itemOf = (raw: Record<string, unknown>, line: number, vocabulary: ItemVoca
|
|
|
138
173
|
if ("problem" in fields) return fields;
|
|
139
174
|
const unknown = vocabularyProblem(fields.value, vocabulary, line);
|
|
140
175
|
if (unknown !== undefined) return { problem: unknown };
|
|
141
|
-
const
|
|
142
|
-
if ("problem" in
|
|
176
|
+
const labels = labelsOf(raw, line, vocabulary, variantKey);
|
|
177
|
+
if ("problem" in labels) return labels;
|
|
143
178
|
const sources = sourcesOf(raw["sources"], line);
|
|
144
179
|
if ("problem" in sources) return sources;
|
|
145
180
|
const citations = citationsOf(raw["citations"], sources.value, line);
|
|
146
181
|
if ("problem" in citations) return citations;
|
|
147
182
|
const contexts = contextsOf(raw["contexts"], line);
|
|
148
183
|
if ("problem" in contexts) return contexts;
|
|
184
|
+
const expect = expectOf(raw["expect"]);
|
|
185
|
+
if ("wrong" in expect) return { problem: { kind: "not-expect", line, detail: expect.wrong } };
|
|
149
186
|
const passages = contexts.value === undefined ? {} : { contexts: contexts.value };
|
|
150
|
-
const
|
|
151
|
-
|
|
187
|
+
const expected = expect.expect === undefined ? {} : { expect: expect.expect };
|
|
188
|
+
const { variant, task } = labels.value;
|
|
189
|
+
const labelled = variant === undefined ? {} : { variant };
|
|
190
|
+
const tasked = task === undefined ? {} : { task };
|
|
191
|
+
return { value: { id, output, ...fields.value, sources: sources.value, citations: citations.value, ...passages, ...labelled, ...expected, ...tasked } };
|
|
152
192
|
};
|
|
153
193
|
|
|
154
194
|
/** One item from a value already parsed, as `grade()` receives it; `line` is 0 when it came from no file. */
|
|
@@ -192,7 +232,7 @@ export const parseItems = (
|
|
|
192
232
|
text: string,
|
|
193
233
|
vocabulary: ItemVocabulary,
|
|
194
234
|
variantField: VariantField = DEFAULT_VARIANT_FIELD,
|
|
195
|
-
): { readonly items: readonly GradeItem[] } | { readonly problems: readonly ItemProblem[] } => {
|
|
235
|
+
): { readonly items: readonly GradeItem[]; readonly lines: readonly number[] } | { readonly problems: readonly ItemProblem[] } => {
|
|
196
236
|
const lines = text.split(/\r?\n/u).map((body, index) => ({ body, line: index + 1 }));
|
|
197
237
|
const parsed = lines
|
|
198
238
|
.filter(({ body }) => body.trim() !== "")
|
|
@@ -204,5 +244,5 @@ export const parseItems = (
|
|
|
204
244
|
...unlabelled(read, variantField),
|
|
205
245
|
];
|
|
206
246
|
if (problems.length > 0) return { problems: problems.toSorted((left, right) => left.line - right.line) };
|
|
207
|
-
return read.length === 0 ? { problems: [{ kind: "empty", line: 0 }] } : { items: read.map((entry) => entry.item) };
|
|
247
|
+
return read.length === 0 ? { problems: [{ kind: "empty", line: 0 }] } : { items: read.map((entry) => entry.item), lines: read.map((entry) => entry.line) };
|
|
208
248
|
};
|
package/src/grade/result.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { CitationStatus } from "../structure/cite.ts";
|
|
|
3
3
|
import type { Severity } from "../plugin.ts";
|
|
4
4
|
import type { OutputSize } from "./rates.ts";
|
|
5
5
|
import type { GradeAiScore } from "./ai-score.ts";
|
|
6
|
+
import type { GradeExpectResult } from "./expect.ts";
|
|
6
7
|
|
|
7
8
|
// The shape of one line of `chaff grade --out`, and of what `grade()` returns. Spec §29.3.
|
|
8
9
|
|
|
@@ -62,6 +63,8 @@ export type GradeResult = {
|
|
|
62
63
|
readonly id: string;
|
|
63
64
|
/** The item's variant label, when it had one. */
|
|
64
65
|
readonly variant?: string | undefined;
|
|
66
|
+
/** The rubric task the output was graded under, when the item named one. */
|
|
67
|
+
readonly task?: string | undefined;
|
|
65
68
|
readonly language: string;
|
|
66
69
|
readonly genre: string;
|
|
67
70
|
readonly size: OutputSize;
|
|
@@ -74,6 +77,8 @@ export type GradeResult = {
|
|
|
74
77
|
readonly citations: GradeCitations | null;
|
|
75
78
|
/** Only when the item gave contexts, so results of items without them keep their shape. */
|
|
76
79
|
readonly contexts?: GradeContexts | undefined;
|
|
80
|
+
/** Only when the item gave expect: the entries checked, the facts not stated and the absent ones stated. */
|
|
81
|
+
readonly expect?: GradeExpectResult | undefined;
|
|
77
82
|
/** Only with a `grade:` rubric. */
|
|
78
83
|
readonly score?: GradeScore | undefined;
|
|
79
84
|
/** The AI-likeness quick score. Absent only in results written before it existed. Never part of pass or fail. */
|
|
@@ -72,8 +72,13 @@ const isRates = (value: unknown): boolean => isRecord(value) && Object.values(va
|
|
|
72
72
|
/** The id, and the variant when the output had one. */
|
|
73
73
|
const isLabel = (value: Record<string, unknown>): boolean => isString(value["id"]) && (value["variant"] === undefined || isString(value["variant"]));
|
|
74
74
|
|
|
75
|
-
/**
|
|
76
|
-
const
|
|
75
|
+
/** Absent when the item gave no expect. */
|
|
76
|
+
const isExpect = (value: unknown): boolean =>
|
|
77
|
+
value === undefined || (isRecord(value) && isNumber(value["checked"]) && isArrayOf(value["missing"], isString) && isArrayOf(value["present"], isString));
|
|
78
|
+
|
|
79
|
+
/** The parts a result carries only sometimes: contexts, expect, a rubric's score, the quick score. Each is checked when present. */
|
|
80
|
+
const hasOptionalParts = (value: Record<string, unknown>): boolean =>
|
|
81
|
+
isContexts(value["contexts"]) && isExpect(value["expect"]) && isScore(value["score"]) && isAiScore(value["aiScore"]);
|
|
77
82
|
|
|
78
83
|
/** A line `--out` writes, every field checked: a line that only looks like one would be compared as if it had been graded. */
|
|
79
84
|
export const isGradeResult = (value: unknown): value is GradeResult =>
|