Skip to content

VM captures root node when a captured ref returns via non-greedy optional skip #380

@zharinov

Description

@zharinov

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions