import { WORD_STOPS } from "#configuration/constants/tone.constants"; export const wordsOf = function wordsOf(text: string): string[] { const words: string[] = []; let current = ""; for (const char of text) { if (WORD_STOPS.has(char)) { if (current.length > 0) { words.push(current); } current = ""; } else { current += char; } } if (current.length > 0) { words.push(current); } return words; }; export const lowerWordsOf = function lowerWordsOf(text: string): string[] { return wordsOf(text).map((word) => word.toLowerCase()); }; const isBoundary = function isBoundary(char: string): boolean { return char.length === 0 || WORD_STOPS.has(char); }; export const countTerm = function countTerm(lowerText: string, term: string): number { let count = 0; let from = lowerText.indexOf(term); while (from !== -1) { const before = from === 0 ? "" : lowerText.charAt(from - 1); const after = lowerText.charAt(from + term.length); if (isBoundary(before) && isBoundary(after)) { count += 1; } from = lowerText.indexOf(term, from + term.length); } return count; }; export const countAny = function countAny(lowerText: string, terms: readonly string[]): number { return terms.reduce((sum, term) => sum + countTerm(lowerText, term), 0); }; const isDigit = function isDigit(char: string): boolean { return char >= "0" && char <= "9"; }; const isNumeric = function isNumeric(stem: string): boolean { if (stem.length === 0) { return false; } for (const char of stem) { if (!isDigit(char) && char !== ".") { return false; } } return true; }; export const isDigitMetric = function isDigitMetric(word: string, units: readonly string[]): boolean { const unit = units.find((held) => word.endsWith(held) && word.length > held.length); return unit !== undefined && isNumeric(word.slice(0, -unit.length)); }; export const digitMetricsOf = function digitMetricsOf(lowerText: string, units: readonly string[]): number { return lowerWordsOf(lowerText).filter((word) => isDigitMetric(word, units)).length; };