39 lines
1.9 KiB
TypeScript
39 lines
1.9 KiB
TypeScript
import { parseFreeformFactV3 } from './truthtrance-freeform-facts-v3';
|
|
import { parseFreeformFactV4 } from './truthtrance-freeform-facts-v4';
|
|
import type { TruthFact } from './truthtrance';
|
|
|
|
type Compound = Extract<TruthFact, { kind: 'and' | 'or' }>;
|
|
|
|
/**
|
|
* A new, deliberately narrow grammar: two existing current-fact atoms joined
|
|
* by one unambiguous AND/OR. Older parser versions and their saved receipts
|
|
* remain unchanged. Call only for fact-scoped text and show the interpretation.
|
|
*/
|
|
export function parseTruthFreeformFactsV5(text: string): Compound | null {
|
|
if (typeof text !== 'string' || text.length > 500) return null;
|
|
const input = text.trim();
|
|
// A complete old interpretation, including a card name containing "and",
|
|
// retains its original meaning rather than being reinterpreted by V5.
|
|
if (parseFreeformFactV3(input) || parseFreeformFactV4(input)) return null;
|
|
const question = /^do[ \t]+you[ \t]+have[ \t]+(.+?)[ \t]*\??$/i.exec(input);
|
|
if (!question) return null;
|
|
const body = question[1];
|
|
const atom = (part: string): TruthFact | null => {
|
|
const value = part.trim();
|
|
if (!value || /[?\r\n]/.test(value)) return null;
|
|
const full = /^do[ \t]+you[ \t]+(?:have|not[ \t]+have)[ \t]+/i.test(value)
|
|
? value : /^not[ \t]+have[ \t]+/i.test(value) ? `Do you ${value}`
|
|
: /^have[ \t]+/i.test(value) ? `Do you ${value}` : `Do you have ${value}`;
|
|
const fact = parseFreeformFactV3(full) ?? parseFreeformFactV4(full);
|
|
return fact && fact.kind !== 'and' && fact.kind !== 'or' ? fact : null;
|
|
};
|
|
const matches: Compound[] = [];
|
|
for (const connector of body.matchAll(/[ \t]+(and|or)[ \t]+/gi)) {
|
|
const index = connector.index ?? 0;
|
|
const left = atom(body.slice(0, index));
|
|
const right = atom(body.slice(index + connector[0].length));
|
|
if (left && right) matches.push({ kind: connector[1].toLowerCase() as 'and' | 'or', terms: [left, right] });
|
|
}
|
|
return matches.length === 1 ? matches[0] : null;
|
|
}
|