Skip to content

Parser menhir to handmade#68

Merged
davesnx merged 12 commits into
mainfrom
cursor/parser-menhir-to-handmade-4410
Feb 11, 2026
Merged

Parser menhir to handmade#68
davesnx merged 12 commits into
mainfrom
cursor/parser-menhir-to-handmade-4410

Conversation

@davesnx

@davesnx davesnx commented Feb 6, 2026

Copy link
Copy Markdown
Owner

Rewrite the parser from Menhir to a handmade recursive descent parser to improve performance, error messages, and simplify the code.

cursoragent and others added 10 commits February 6, 2026 08:12
- Remove Parser.mly (Menhir grammar) and menhirLib dependency
- Add Parser.ml: handmade recursive descent / Pratt parser
- Update Core.ml to call Parser.program directly
- Update dune build to remove menhir stanza
- Update dune-project to remove menhir dependency
- All 701 unit tests pass, CLI cram tests pass
- Minor improvement: error pointers now span full expressions

Co-authored-by: David Sancho <davesnx@users.noreply.github.com>
- Add benchmarks/bench_parser.ml: measures parse-only performance
  across 22 queries of varying complexity (100k iterations each)
- Add bench-parser Makefile target
- Clean up inlined error handling in Parser.ml zero-arg function case
- Average parse time: ~2.7 microseconds per query

Co-authored-by: David Sancho <davesnx@users.noreply.github.com>
@davesnx
davesnx marked this pull request as ready for review February 11, 2026 16:35
@davesnx
davesnx requested a review from Copilot February 11, 2026 16:36
Repository owner deleted a comment from cursor Bot Feb 11, 2026

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

Repository owner deleted a comment from cursor Bot Feb 11, 2026
Repository owner deleted a comment from cursor Bot Feb 11, 2026
Repository owner deleted a comment from Copilot AI Feb 11, 2026
Repository owner deleted a comment from Copilot AI Feb 11, 2026
Repository owner deleted a comment from Copilot AI Feb 11, 2026
Repository owner deleted a comment from Copilot AI Feb 11, 2026
Repository owner deleted a comment from Copilot AI Feb 11, 2026
Repository owner deleted a comment from Copilot AI Feb 11, 2026
@davesnx
davesnx merged commit 12bbb0c into main Feb 11, 2026
5 checks passed
@davesnx
davesnx deleted the cursor/parser-menhir-to-handmade-4410 branch February 11, 2026 18:40

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Comment thread source/Parser.ml
match (peek stream : Lexer.token) with
| FN -> parse_fn_def stream
| TRY -> parse_try stream
| _ -> parse_pipe_expr stream

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try-catch result not composable with pipe operators

High Severity

parse_fn_or_expr returns the result of parse_try directly without passing it through parse_pipe_expr. In the old Menhir grammar, try-catch was a production inside sequence_expr alongside PIPE and COMMA, so try a catch b | c correctly produced Pipe(Try(a, Some b, None), c). The new parser fails to parse this because the | c is left unconsumed. This affects all contexts using parse_sequence_expr: top-level programs, array constructions like [try .foo catch null | .bar], function arguments, if-then-else branches, etc.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment thread source/Parser.ml

and parse_pipe_expr stream =
let left = parse_comma_expr stream in
parse_pipe_right stream left

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pipe-level operators not composable with trailing comma

Medium Severity

parse_pipe_expr calls parse_comma_expr then parse_pipe_right, but the result of parse_pipe_right is never fed back through comma handling. In the old Menhir grammar, COMMA had higher precedence than PIPE/ALTERNATIVE/UPDATE_ASSIGN, so a ?? b, c correctly produced Comma(Alternative(a, b), c). In the new parser, after parse_pipe_right produces Alternative(a, b), the trailing , is unconsumed, causing a parse error. Affects patterns like .foo |= . + 1, .bar |= . + 2 and a ?? "default", b.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment thread source/Parser.ml
match (peek stream : Lexer.token) with
| FN -> parse_fn_def stream
| TRY -> parse_try stream
| _ -> parse_pipe_expr stream

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try-catch result not composable with pipe operators

High Severity

parse_fn_or_expr returns the result of parse_try directly without passing it through parse_pipe_expr. In the old Menhir grammar, try-catch was a production inside sequence_expr alongside PIPE and COMMA, so try a catch b | c correctly produced Pipe(Try(a, Some b, None), c). The new parser fails to parse this because the | c is left unconsumed. This affects all contexts using parse_sequence_expr: top-level programs, array constructions like [try .foo catch null | .bar], function arguments, if-then-else branches, etc.

Additional Locations (1)

Fix in Cursor Fix in Web

Comment thread source/Parser.ml

and parse_pipe_expr stream =
let left = parse_comma_expr stream in
parse_pipe_right stream left

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pipe-level operators not composable with trailing comma

Medium Severity

parse_pipe_expr calls parse_comma_expr then parse_pipe_right, but the result of parse_pipe_right is never fed back through comma handling. In the old Menhir grammar, COMMA had higher precedence than PIPE/ALTERNATIVE/UPDATE_ASSIGN, so a ?? b, c correctly produced Comma(Alternative(a, b), c). In the new parser, after parse_pipe_right produces Alternative(a, b), the trailing , is unconsumed, causing a parse error. Affects patterns like .foo |= . + 1, .bar |= . + 2 and a ?? "default", b.

Additional Locations (1)

Fix in Cursor Fix in Web

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.

3 participants