Skip to content

Parser robustness: guard against Earley chart blow-up with an opt-in timeout#5

Merged
christiankissig merged 2 commits into
masterfrom
parser-robustness
Jun 2, 2026
Merged

Parser robustness: guard against Earley chart blow-up with an opt-in timeout#5
christiankissig merged 2 commits into
masterfrom
parser-robustness

Conversation

@christiankissig

Copy link
Copy Markdown
Owner

Investigation

Profiled the ~15–20% "timeout" bucket from the coverage sweeps. Findings:

  • Root cause: the parse hangs in lark/parsers/earley.py:predict_and_complete — the Earley chart computation, pure Python. RSS climbs as the chart grows. It is not a regex/C wedge and not one pathological construct.
  • Terminating, not infinite: e.g. Karatsuba_Runtime_Lemmas.thy (8 KB) finishes in 11.7 s. Parse time grows super-linearly with total file size (5.4 KB→4.4 s, 6.8 KB→7.9 s, 7.8 KB→11.7 s) — global grammar ambiguity, not a single line.
  • Scale & correlation: on a 200-file sample, 31 (~15%) exceed 15 s; strongly size-correlated (wedge files median 33 KB vs slow-but-finishing 10 KB).
  • Lexer swap not viable: contextual is unsupported with earley; basic can't tokenize the grammar (it relies on the dynamic lexer's context sensitivity).
  • SIGALRM interrupts the loop cleanly (verified: aborts at 3.0 s) because the hot loop is pure Python.

The proper performance fix is reducing grammar ambiguity (a larger, separate effort). This PR delivers the robustness fix so a pathological file fails cleanly instead of hanging the caller.

Changes

  • parse(text, parser=None, timeout=None) — new opt-in timeout (seconds). On exceed, raises ParsingError("parsing timed out after Ns"). Default None = unchanged behaviour.
  • --timeout/-t CLI flag.
  • Implementation uses SIGALRM via setitimer; it applies only on the main thread of a Unix process (documented), is skipped elsewhere, and always restores the prior handler/timer.
  • Drive-by: ParsingError._source_hint() no longer leaks the literal "source_hint" placeholder into location-less messages.

Testing

  • Full suite: 135 passed (6 new timeout tests using a fake slow parser, so they're deterministic and fast — no dependency on a real wedge). Handler-restoration and off-main-thread skip are covered.
  • E2E: cli … --timeout 3 on a wedge file exits cleanly in ~3.4 s with Error: parsing timed out after 3.0s; normal files unaffected.
  • ruff check / ruff format / isort clean.

Follow-up (not in this PR)

Reduce Earley ambiguity for an actual speed-up — candidate sources: overlapping name/embedded/term regexes and the many optional/comment_block*/marker? slots that fan out the chart.

🤖 Generated with Claude Code

christiankissig and others added 2 commits June 2, 2026 14:05
ParsingError._source_hint() returned the literal string "source_hint"
when no source location was available, which got appended verbatim to
the error message (e.g. "...timed out after 2.0ssource_hint"). Return an
empty string instead so location-less errors read cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Earley parser's chart grows super-linearly with input size, so a
large/ambiguous theory file can take minutes in predict_and_complete and
hang the caller (~15% of the AFP corpus exceeds 15s, correlating with
file size). The parse loop is pure Python, so SIGALRM interrupts it
cleanly.

Add a `timeout` parameter to parse() (and a `--timeout/-t` CLI flag) that
aborts with a ParsingError when exceeded, leaving normal parsing
unchanged by default. The limit uses SIGALRM and so applies only on the
main thread of a Unix process; elsewhere it is silently skipped. The
SIGALRM handler and timer are always restored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@christiankissig christiankissig merged commit 87fc2e5 into master Jun 2, 2026
6 checks passed
@christiankissig christiankissig deleted the parser-robustness branch June 2, 2026 13:14
christiankissig added a commit that referenced this pull request Jul 7, 2026
Parser robustness: guard against Earley chart blow-up with an opt-in timeout
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant