microsoft / microsoft/TypeAgent

actionGrammar: optional rule-reference `<Rule>?` silently fails to match (inline optional group works)

Open
#2,461 0 comments 0 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.