docs(mystery): spec for engine prereqs (verbs, disambiguation, ending UI) #1
@@ -231,3 +231,40 @@ describe('parser — pronouns', () => {
|
|||||||
expect(result.kind).toBe('unknown')
|
expect(result.kind).toBe('unknown')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('stop-word stripping', () => {
|
||||||
|
const ctx: ParserContext = {
|
||||||
|
knownItems: ['lamp'],
|
||||||
|
knownEncounters: [],
|
||||||
|
visibleNouns: [{ id: 'lamp', aliases: ['lamp', 'oil lamp'] }],
|
||||||
|
inventoryItemIds: [],
|
||||||
|
lastNoun: null,
|
||||||
|
awaitingDisambiguation: null,
|
||||||
|
}
|
||||||
|
|
||||||
|
it('strips a leading "at" from the noun phrase', () => {
|
||||||
|
const cmd = parse('look at lamp', ctx)
|
||||||
|
expect(cmd).toEqual({
|
||||||
|
kind: 'verb-target',
|
||||||
|
verb: 'look',
|
||||||
|
target: { canonical: 'lamp', raw: 'lamp' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('strips a leading "the"', () => {
|
||||||
|
const cmd = parse('examine the lamp', ctx)
|
||||||
|
expect(cmd.kind).toBe('verb-target')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('strips "a" and "an"', () => {
|
||||||
|
expect(parse('take a lamp', ctx).kind).toBe('verb-target')
|
||||||
|
expect(parse('take an oil lamp', ctx).kind).toBe('verb-target')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not strip stop-words mid-phrase', () => {
|
||||||
|
// 'oil lamp' is the alias; 'oil at lamp' is not. Stop-words only strip from
|
||||||
|
// the head of the noun phrase, not anywhere in the middle.
|
||||||
|
const cmd = parse('take oil lamp', ctx)
|
||||||
|
expect(cmd.kind).toBe('verb-target')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ const VERB_ONLY_VERBS = new Set<string>(['look', 'inventory', 'wait'])
|
|||||||
/** Two-word verb prefixes (e.g. "pick up X"). */
|
/** Two-word verb prefixes (e.g. "pick up X"). */
|
||||||
const TWO_WORD_VERBS = ['pick up']
|
const TWO_WORD_VERBS = ['pick up']
|
||||||
|
|
||||||
|
/** Leading stop-words stripped from the noun phrase before matching. */
|
||||||
|
const STOP_WORDS = new Set(['at', 'the', 'a', 'an'])
|
||||||
|
|
||||||
function tokenize(input: string): string[] {
|
function tokenize(input: string): string[] {
|
||||||
return input.trim().toLowerCase().split(/\s+/).filter(Boolean)
|
return input.trim().toLowerCase().split(/\s+/).filter(Boolean)
|
||||||
}
|
}
|
||||||
@@ -126,6 +129,11 @@ export function parse(rawInput: string, ctx: ParserContext): ParsedCommand {
|
|||||||
return { kind: 'unknown', raw: trimmed, reason: 'unknown-verb' }
|
return { kind: 'unknown', raw: trimmed, reason: 'unknown-verb' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Strip leading stop-words from the noun phrase (e.g. "at", "the", "a", "an").
|
||||||
|
while (rest.length > 0 && STOP_WORDS.has(rest[0]!)) {
|
||||||
|
rest = rest.slice(1)
|
||||||
|
}
|
||||||
|
|
||||||
if (rest.length === 0) {
|
if (rest.length === 0) {
|
||||||
if (VERB_ONLY_VERBS.has(verb)) {
|
if (VERB_ONLY_VERBS.has(verb)) {
|
||||||
return { kind: 'verb-only', verb: verb as 'look' | 'inventory' | 'wait' }
|
return { kind: 'verb-only', verb: verb as 'look' | 'inventory' | 'wait' }
|
||||||
|
|||||||
Reference in New Issue
Block a user