29 lines
1.6 KiB
TypeScript
29 lines
1.6 KiB
TypeScript
import { TRUTH_CARD_NAMES } from './truthtrance-card-names';
|
|
import { parseFreeformFactV3 } from './truthtrance-freeform-facts-v3';
|
|
import type { CardCountFact } from './truthtrance-card-count';
|
|
import type { HandInventoryFact } from './truthtrance-hand-inventory';
|
|
|
|
/**
|
|
* Recognize only new, unambiguous present-tense negation forms. Earlier
|
|
* parsers remain unchanged for previously stamped questions. The caller
|
|
* limits interpretation to fact-scoped questions and displays the result.
|
|
*/
|
|
export function parseFreeformFactV4(text: string): CardCountFact | HandInventoryFact | null {
|
|
if (typeof text !== 'string' || text.length > 500) return null;
|
|
const match = /^do[ \t]+you[ \t]+(?:have[ \t]+no|not[ \t]+have[ \t]+(a|an|any))[ \t]+(.+?)[ \t]*\??$/i.exec(text.trim());
|
|
if (!match) return null;
|
|
const subject = match[2].trim();
|
|
// Reuse v3's bounded primary-role vocabulary and zero-count semantics.
|
|
const category = parseFreeformFactV3(`Do you have exactly 0 ${subject}`);
|
|
const inventory = category?.kind === 'handInventory' ? category : null;
|
|
const nameInput = subject.replace(/[ \t]+in[ \t]+your[ \t]+hand$/i, '').trim();
|
|
if (inventory && /^(a|an)$/i.test(match[1] ?? '') && /s$/i.test(nameInput)) return null;
|
|
const names = TRUTH_CARD_NAMES.filter(name => name.toLowerCase() === nameInput.toLowerCase());
|
|
// Refuse if the words could mean both a named face and a category.
|
|
if ((inventory && names.length > 0) || names.length > 1) return null;
|
|
if (inventory) return inventory;
|
|
return names.length === 1
|
|
? { kind: 'handCount', name: names[0], compare: 'eq', value: 0 }
|
|
: null;
|
|
}
|