Summary
- A captured ref can fabricate a node when the callee returns without matching anything.
- Repro via CLI: on source returns the root node as .
- Suspected root cause: unconditionally writes even when the callee matched nothing.
Repro
Run:
{"x":{"kind":"program","text":"foo","span":[0, 3]}}
Observed output:
Trace also shows the callee taking the epsilon skip path and then the caller executing an epsilon capture:
_ObjWrap:
00 ε [Obj] 02
⬥ Obj
02 Trampoline 03
▶ (Q)
Q:
08 (A) 06 : 09
▶ (A)
A:
06 ε 12, 16
12 ◀ (A)
Q:
09 ε [Node Set(M0)] 11
⬥ Node
⬥ Set "x"
11 ◀ (Q)
_ObjWrap:
03 ε [EndObj] 05
⬥ EndObj
05 ◀ _ObjWrap ◼
{
"x": {
"kind": "program",
"text": "foo",
"span": [0, 3]
}
}
And the lowered bytecode contains:
Why this looks wrong
is a non-greedy optional, so is allowed to return without matching an .
In that case, should not fabricate a node that never matched.
Regardless of whether the intended behavior is null / absent / no match, returning the root node is incorrect.
Suspected root cause
In , does:
- set
- then restore tree depth
That seems correct when the callee actually matched something, but incorrect when the callee returned through a zero-match path (like a skipped optional).
A fix likely needs to distinguish:
- return after a real match
- return after a zero-width / skipped path
and avoid manufacturing in the latter case.
Summary
Repro
Run:
{"x":{"kind":"program","text":"foo","span":[0, 3]}}
Observed output:
Trace also shows the callee taking the epsilon skip path and then the caller executing an epsilon capture:
_ObjWrap:
00 ε [Obj] 02
⬥ Obj
02 Trampoline 03
▶ (Q)
Q:
08 (A) 06 : 09
▶ (A)
A:
06 ε 12, 16
12 ◀ (A)
Q:
09 ε [Node Set(M0)] 11
⬥ Node
⬥ Set "x"
11 ◀ (Q)
_ObjWrap:
03 ε [EndObj] 05
⬥ EndObj
05 ◀ _ObjWrap ◼
{
"x": {
"kind": "program",
"text": "foo",
"span": [0, 3]
}
}
And the lowered bytecode contains:
Why this looks wrong
is a non-greedy optional, so is allowed to return without matching an .
In that case, should not fabricate a node that never matched.
Regardless of whether the intended behavior is null / absent / no match, returning the root node is incorrect.
Suspected root cause
In , does:
That seems correct when the callee actually matched something, but incorrect when the callee returned through a zero-match path (like a skipped optional).
A fix likely needs to distinguish:
and avoid manufacturing in the latter case.