import { COLLECTIVE_PERSON, FIRST_PERSON, SECOND_PERSON } from "#configuration/constants/tone.constants"; import { LONG_DASH, METRIC_UNITS, SEMICOLON } from "@ssot/govlab/shared/manifests/punctuation.manifest.ts"; import { countAny, digitMetricsOf, lowerWordsOf } from "#core/normalizers/word.normalizer"; import { longestOf, shapesOf } from "@ssot/govlab/shared/analyzers/sentence.analyzer.ts"; import { BANNED_TERMS } from "@ssot/govlab/shared/manifests/vocabulary.manifest.ts"; import { JOIN_CAP } from "@ssot/govlab/shared/manifests/sentence.manifest.ts"; import type { ToneMetrics } from "#types/tone.types"; import { isLabelledItem } from "#core/normalizers/sentence.normalizer"; const countPersons = function countPersons(words: readonly string[], markers: ReadonlySet): number { return words.filter((word) => markers.has(word)).length; }; const occurrences = function occurrences(text: string, needle: string): number { let count = 0; let from = text.indexOf(needle); while (from !== -1) { count += 1; from = text.indexOf(needle, from + needle.length); } return count; }; export const measureLiterals = function measureLiterals(module: string, literals: readonly string[]): ToneMetrics { const joined = literals.join(" "); const lower = joined.toLowerCase(); const words = lowerWordsOf(joined); const shapes = literals.flatMap(shapesOf); return { agentlessPassives: shapes.filter((shape) => shape.agentlessPassive).length, bannedTerms: countAny(lower, BANNED_TERMS), chainedSentences: shapes.filter((shape) => shape.joins > JOIN_CAP).length, collectivePerson: countPersons(words, COLLECTIVE_PERSON), digitMetrics: digitMetricsOf(lower, METRIC_UNITS), firstPerson: countPersons(words, FIRST_PERSON), labelledItems: literals.filter(isLabelledItem).length, literals: literals.length, longDashes: occurrences(joined, LONG_DASH), longestSentence: longestOf(shapes), module, secondPerson: countPersons(words, SECOND_PERSON), semicolons: occurrences(joined, SEMICOLON), sentences: shapes.length, words: words.length, }; };