microsoft / microsoft/TypeAgent
actionGrammar: optional rule-reference `<Rule>?` silently fails to match (inline optional group works)
- Dominant language
- TypeScript
- Stars
- 740
- Forks
- 106
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 84
Description
## Summary
In `packages/actionGrammar`, an **optional rule-reference** `?` causes the matcher to silently fail: the NFA consumes all input tokens but never reaches an accepting state, so `matchNFA` returns `matched: false`. The equivalent **inline optional group** `(... )?` works correctly, as does a **required** (non-optional) rule-reference. So the defect is specific to the combination *optional + rule-reference*.
## Minimal repro
```js
import { loadGrammarRulesNoThrow, compileGrammarToNFA, matchNFA, registerBuiltInEntities } from "action-grammar";
registerBuiltInEntities();
function run(label, text) {
const errs = [];
const g = loadGrammarRulesNoThrow("r.agr", text, errs);
const nfa = compileGrammarToNFA(g, "r");
const toks = "add a to the b playlist".split(" ");
const r = matchNFA(nfa, toks);
console.log(label.padEnd(22), "matched:", r.matched, "consumed:", r.tokensConsumed, "/", toks.length);
}
// FAILS — optional rule-reference
run("optional rule-ref", ` = add $(x:wildcard) to ? $(y:wildcard) playlist -> { actionName:"a", parameters:{ x, y } };
= the | my ;`);
// WORKS — inline optional group
run("inline optional group", ` = add $(x:wildcard) to (the | my)? $(y:wildcard) playlist -> { actionName:"a", parameters:{ x, y } };`);
// WORKS — required rule-reference
run("required rule-ref", ` = add $(x:wildcard) to $(y:wildcard) playlist -> { actionName:"a", parameters:{ x, y } };
= the | my ;`);
```
### Output
```
optional rule-ref matched: false consumed: 6 / 6
inline optional group matched: true consumed: 6 / 6
required rule-ref matched: true consumed: 6 / 6
```
## Expected
All three grammars should match `add a to the b playlist` (with `Owner` matching `the`). The optional rule-reference form should behave identically to the inline optional group.
## Observed
The optional rule-reference form fails: the input is fully consumed (`consumed: 6 / 6`) but `matched` is `false` and `actionValue` is `undefined` — i.e. the path reaches the end of input but no accepting state, suggesting the optionality (epsilon skip) of a `?` reference is not wired into the NFA accept path the way an inline `(...)?` group is.
## Workaround
Replace `?` with an inline optional group `( a | b )?`, or make the reference required.
## Notes
- Affects the NFA interpreter path (`matchNFA` / `nfaInterpreter.ts`); not yet checked against the DFA path.
- Found while building `examples/snipsBench` (a SNIPS slot-filling benchmark over action-grammar); the hand-authored grammars hit this and switched to inline optional groups.
## Environment
- `packages/actionGrammar` @ current `main`
- Node 22+, the engine's default `matchNFA` path
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with packages/actionGrammar/nfaInterpreter.ts and the matchNFA and compileGrammarToNFA entry points. Run the minimal reproduction from the issue, then compare optional rule-reference handling with the working inline optional group and required reference cases. Done means the optional rule-reference matches the input and produces the expected action value without breaking the existing cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- compilers, testing-qa
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100