Skip to content

fix: restore fast Java parsing with typed contexts - #175

Merged
tinovyatkin merged 4 commits into
mainfrom
fix/issue-174-fast-context-alts
Jul 23, 2026
Merged

fix: restore fast Java parsing with typed contexts#175
tinovyatkin merged 4 commits into
mainfrom
fix/issue-174-fast-context-alts

Conversation

@tinovyatkin

@tinovyatkin tinovyatkin commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Fixes #174

Summary

  • Keep the fast recognizer eligible when generated parsers request private context-alternative metadata.
  • Carry selected alternatives and left-recursive boundaries in the deferred tree rope, preserving typed base/operator context dispatch.
  • Generate the Rust Java benchmark from the untouched pinned JavaParser.g4; Python and Go retain their portable rewrite.
  • Add snapshots for predicate-aware alternative tracking plus the issue minimal Java method-body benchmark fixture.
  • Version benchmark semantics per result so CI skips only incompatible old/new methodology pairs and resumes Java comparisons after this CR lands.

Root cause

Typed traversal generation made Java request track_context_alt_numbers: true. The runtime treated that private tree metadata as incompatible with fast recognition, routing Java expression rules through the exponentially slower general recognizer. The generated parser ATN is unchanged; the regression was the runtime eligibility gate introduced with typed traversal.

The existing Java benchmark did not expose that route because it removed JavaParserBase and rewrote both semantic-predicate sites before Rust generation. The Rust lane now keeps the grammar unchanged, retaining those predicate coordinates and the interpreted-rule shape used by downstream consumers.

Impact

On class C { int m() { return 1; } }, current main takes about 5.36 seconds under the corrected benchmark and then stack-overflows on the first larger Java fixture. This branch parses the minimal fixture in 0.08-0.19 ms and completes every existing Java fixture. Rust/Go parse-tree dumps remain byte-identical for all Java fixtures.

Validation

  • cargo clippy --locked --all-targets --all-features -- -D warnings
  • cargo test --locked --all-targets --all-features
  • python3 -m unittest tools/parse-bench/test_run.py (13 tests)
  • Java Rust benchmark with untouched grammar: all 5 parse fixtures complete
  • Java Rust/Go AST parity: all 5 fixtures match byte-for-byte
  • Negative control against origin/main: 5.36 seconds for the 50-byte issue fixture, then stack overflow on the first larger Java fixture
  • Full 357-case ANTLR runtime testsuite on the implementation commit
  • Failed-CI artifact replay: 4 incompatible Java variant pairs skipped; all 8 unchanged Rust pairs passed the 1.15x regression gate

Summary by CodeRabbit

  • New Features

    • Parse trees and CST output now preserve alternative-number information, including nested, repeated, and left-recursive rules.
    • Left-recursive contexts and labeled operator nodes retain their metadata during fast parsing.
  • Bug Fixes

    • Improved fast-path parsing and predicate handling when alternative tracking is enabled.
  • Benchmarking

    • Benchmark reports now distinguish grammar variants and skip comparisons when variants differ.
    • Added coverage for Java parsing performance and runtime-specific grammar preparation.

Copilot AI review requested due to automatic review settings July 23, 2026 15:48

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6159277f-d6ab-4acf-b085-65b0ad4ce3fd

📥 Commits

Reviewing files that changed from the base of the PR and between 0cd0083 and 533793c.

⛔ Files ignored due to path filters (1)
  • src/snapshots/antlr4_runtime__parser__tests__fast_recognizer_preserves_labeled_left_recursive_operator_context.snap is excluded by !**/*.snap
📒 Files selected for processing (7)
  • src/parser.rs
  • tools/parse-bench/README.md
  • tools/parse-bench/compare.py
  • tools/parse-bench/fixtures/java/issue-174-return-expression.java
  • tools/parse-bench/fixtures/manifest.json
  • tools/parse-bench/run.py
  • tools/parse-bench/test_run.py

📝 Walkthrough

Walkthrough

The fast recognizer now propagates alternative metadata through deferred trees and CST construction. Benchmark tooling labels runtime grammar variants, adds a Java parse fixture, and compares only matching baseline/current variants.

Changes

Parser alternative tracking

Layer / File(s) Summary
Tracking configuration and tree wiring
src/parser.rs
Parser entrypoints and CST helpers propagate public and context alternative-number tracking into rule and implicit-token contexts.
Deferred alternatives and recursive boundaries
src/parser.rs
Deferred alternatives and left-recursive boundaries preserve, restore, and patch alternative state during materialization.
Fast recognition, repetition, and retry flow
src/parser.rs
Fast recognition returns alternative numbers and records them across transitions, repetitions, boundaries, and retry selection.
Alternative metadata regression coverage
src/parser.rs
Tests cover deep materialization, left-recursive context metadata, and private-context predicate recognition.

Benchmark grammar and comparison variants

Layer / File(s) Summary
Runtime grammar preparation and fixture coverage
tools/parse-bench/run.py, tools/parse-bench/fixtures/*
Runtime grammar preparation is centralized, Rust Java parsing retains its predicate variant, and a Java return-expression fixture is registered.
Variant-aware benchmark comparison
tools/parse-bench/compare.py
Comparison skips and reports baseline/current pairs with mismatched benchmark variants.
Grammar and comparison test coverage
tools/parse-bench/test_run.py
Tests validate grammar preparation, variant selection, mismatch handling, empty comparisons, unrelated results, and regression thresholds.
Benchmark variant documentation
tools/parse-bench/README.md
Documentation describes runtime-specific Java grammar handling and benchmark variant comparison behavior.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: restoring fast Java parsing with typed contexts.
Linked Issues check ✅ Passed The changes address the Java method-body regression by preserving fast recognition and typed contexts, matching issue #174.
Out of Scope Changes check ✅ Passed The benchmark, fixture, README, and parser updates all support the stated Java parsing fix and variant-aware comparison.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-174-fast-context-alts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

Copy/Paste Detection

Found 14 duplication(s) across 1 changed Rust file(s) (threshold: 100 tokens).

Show duplications

Found a 27 line (145 tokens) duplication in the following files:

  • Starting at line 15358 of src/parser.rs
  • Starting at line 15491 of src/parser.rs
    fn generated_match_token_recovers_missing_token_from_context_follow() {
        let atn = generated_match_recovery_atn();
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new(
                [None, Some("'X'"), Some("'Y'")],
                [None, Some("X"), Some("Y")],
                [None::<&str>, None, None],
            ),
        );
        let mut parser = BaseParser::new(
            CommonTokenStream::new(Source {
                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
                index: 0,
            }),
            data,
        );
        parser.rule_context_stack = vec![
            RuleContextFrame {
                rule_index: 0,
                invoking_state: 0,
            },
            RuleContextFrame {
                rule_index: 1,
                invoking_state: 1,
            },
        ];
```rust

---

Found a 27 line (127 tokens) duplication in the following files:
* Starting at line 14316 of src/parser.rs
* Starting at line 14388 of src/parser.rs

```rust
        let mut atn = ParserAtnBuilder::new(2);
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(0))
                .expect("state")
                .index(),
            0
        );
        assert_eq!(
            atn.add_state(AtnStateKind::BlockStart, Some(0))
                .expect("state")
                .index(),
            1
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            2
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            3
        );
        assert_eq!(
            atn.add_state(AtnStateKind::BlockEnd, Some(0))

Found a 22 line (125 tokens) duplication in the following files:

  • Starting at line 14342 of src/parser.rs
  • Starting at line 14414 of src/parser.rs
            atn.add_state(AtnStateKind::BlockEnd, Some(0))
                .expect("state")
                .index(),
            4
        );
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStop, Some(0))
                .expect("state")
                .index(),
            5
        );
        atn.set_rule_to_start_state(vec![0])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![5])
            .expect("rule stop states");
        atn.add_decision_state(1).expect("decision state");
        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
            .expect("transition");
        atn.add_transition(
            1,
            ParserTransitionSpec::Atom {
                target: 2,
```rust

---

Found a 34 line (119 tokens) duplication in the following files:
* Starting at line 9748 of src/parser.rs
* Starting at line 9823 of src/parser.rs

```rust
                        outcomes.extend(
                            self.recognize_state(
                                atn,
                                RecognizeRequest {
                                    state_number: *target,
                                    stop_state,
                                    index,
                                    rule_start_index,
                                    decision_start_index: next_decision_start_index,
                                    init_action_rules,
                                    predicates,
                                    semantics,
                                    rule_args,
                                    member_actions,
                                    return_actions,
                                    local_int_arg,
                                    member_values: member_values.clone(),
                                    return_values: return_values.clone(),
                                    rule_alt_number: next_alt_number,
                                    track_alt_numbers,
                                    consumed_eof,
                                    committed_decision: transition_committed,
                                    precedence,
                                    depth: depth + 1,
                                    recovery_symbols: epsilon_recovery_symbols.clone(),
                                    recovery_state: epsilon_recovery_state,
                                },
                                visiting,
                                memo,
                                expected,
                            )
                            .into_iter()
                            .map(|mut outcome| {
                                prepend_decision(&mut outcome, decision);

Found a 15 line (113 tokens) duplication in the following files:

  • Starting at line 15416 of src/parser.rs
  • Starting at line 15692 of src/parser.rs
    fn generated_match_token_counts_single_token_deletion_recovery() {
        let atn = generated_match_recovery_atn();
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new(
                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
                [None, Some("X"), Some("Y"), Some("Z")],
                [None::<&str>, None, None, None],
            ),
        );
        let mut parser = BaseParser::new(
            CommonTokenStream::new(Source {
                tokens: vec![
                    TestToken::new(3).with_text("z"),
                    TestToken::new(2).with_text("y"),
```rust

---

Found a 12 line (112 tokens) duplication in the following files:
* Starting at line 13297 of src/parser.rs
* Starting at line 13380 of src/parser.rs

```rust
        let mut atn = ParserAtnBuilder::new(1);
        for (state, kind, rule) in [
            (0, AtnStateKind::RuleStart, 0),
            (1, AtnStateKind::StarLoopEntry, 0),
            (2, AtnStateKind::Basic, 0), // ops hub
            (3, AtnStateKind::Basic, 0), // shift prec
            (4, AtnStateKind::Basic, 0), // shift first >
            (5, AtnStateKind::Basic, 0), // shift second >
            (6, AtnStateKind::Basic, 0), // rel prec
            (7, AtnStateKind::Basic, 0), // rel >
            (8, AtnStateKind::LoopEnd, 0),
            (9, AtnStateKind::RuleStop, 0),

Found a 22 line (112 tokens) duplication in the following files:

  • Starting at line 14753 of src/parser.rs
  • Starting at line 14954 of src/parser.rs
    fn predicate_after_token_atn() -> Atn {
        let mut atn = ParserAtnBuilder::new(2);
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(0))
                .expect("state")
                .index(),
            0
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            1
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            2
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
```rust

---

Found a 22 line (111 tokens) duplication in the following files:
* Starting at line 13119 of src/parser.rs
* Starting at line 14753 of src/parser.rs
* Starting at line 14954 of src/parser.rs

```rust
    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
        let mut atn = ParserAtnBuilder::new(2);
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(0))
                .expect("state")
                .index(),
            0
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            1
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            2
        );
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(1))

Found a 14 line (110 tokens) duplication in the following files:

  • Starting at line 15087 of src/parser.rs
  • Starting at line 17843 of src/parser.rs
    fn parser_matches_token_and_reports_mismatch() {
        let source = Source {
            tokens: vec![
                TestToken::new(1).with_text("x"),
                TestToken::eof("parser-test", 1, 1, 1),
            ],
            index: 0,
        };
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
        );
        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
        let matched = parser.match_token(1).expect("token 1 should match");
```rust

---

Found a 13 line (109 tokens) duplication in the following files:
* Starting at line 15087 of src/parser.rs
* Starting at line 17868 of src/parser.rs

```rust
    fn parser_matches_token_and_reports_mismatch() {
        let source = Source {
            tokens: vec![
                TestToken::new(1).with_text("x"),
                TestToken::eof("parser-test", 1, 1, 1),
            ],
            index: 0,
        };
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
        );
        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);

Found a 22 line (108 tokens) duplication in the following files:

  • Starting at line 7543 of src/parser.rs
  • Starting at line 7934 of src/parser.rs
    ) -> Option<RecognizeOutcome> {
        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
        let mut next_index = error_index;
        loop {
            let symbol = self.token_type_at(next_index);
            if sync_symbols.contains(&symbol) {
                if next_index == error_index {
                    return None;
                }
                break;
            }
            if symbol == TOKEN_EOF {
                break;
            }
            let after = self.consume_index(next_index, symbol);
            if after == next_index {
                break;
            }
            next_index = after;
        }
        let mut nodes = NodeSeqId::EMPTY;
```rust

---

Found a 15 line (108 tokens) duplication in the following files:
* Starting at line 18134 of src/parser.rs
* Starting at line 18158 of src/parser.rs

```rust
    fn outcome_ties_keep_later_non_recursive_alternative() {
        let arena = RecognitionArena::default();
        let first = RecognizeOutcome {
            index: 1,
            consumed_eof: false,
            alt_number: 0,
            member_values: BTreeMap::new(),
            return_values: BTreeMap::new(),
            diagnostics: DiagnosticSeqId::EMPTY,
            decisions: Vec::new(),
            actions: vec![ParserAction::new(1, 0, 0, None)],
            nodes: NodeSeqId::EMPTY,
        };
        let second = RecognizeOutcome {
            actions: vec![ParserAction::new(2, 0, 0, None)],

Found a 16 line (105 tokens) duplication in the following files:

  • Starting at line 6686 of src/parser.rs
  • Starting at line 7302 of src/parser.rs
        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
        })?;
        let stop_state = atn
            .rule_to_stop_state()
            .get(rule_index)
            .filter(|state| *state != usize::MAX)
            .ok_or_else(|| {
                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
            })?;

        let start_index = self.current_visible_index();
        self.clear_prediction_diagnostics();
        self.reset_per_parse_caches();
        self.reset_recognition_arena();
        let caller_follow_state = self.pending_invoking_follow_state(atn);
```rust

---

Found a 13 line (100 tokens) duplication in the following files:
* Starting at line 6414 of src/parser.rs
* Starting at line 6438 of src/parser.rs

```rust
        let mut expected = BTreeSet::new();
        for index in (1..self.rule_context_stack.len()).rev() {
            let invoking_state = self.rule_context_stack[index].invoking_state;
            let Ok(state_number) = usize::try_from(invoking_state) else {
                continue;
            };
            let Some(Transition::Rule { follow_state, .. }) = atn
                .state(state_number)
                .and_then(|state| state.transitions().first())
                .map(ParserTransition::data)
            else {
                continue;
            };

@claude

claude Bot commented Jul 23, 2026

Copy link
Copy Markdown

Claude finished @tinovyatkin's task in 14m 44s —— View job


Code review — PR #175

Verdict: no blocking issues found. I ran an independent review (2 bug-focused passes + 2 CLAUDE.md-compliance passes over src/parser.rs, the three new snapshots, and tools/parse-bench/). All passes converged, and I independently re-verified that both previously-flagged Codex findings are genuinely fixed on the current commit (533793c).

Review checklist
  • Gather PR context (diff vs origin/main)
  • Analyze src/parser.rs alternative-number tracking changes
  • Analyze tools/parse-bench/ benchmark + comparison changes
  • Verify correctness of the two prior Codex findings' fixes
  • CLAUDE.md compliance audit (src + parse-bench)
  • Post consolidated review

✅ Previously-flagged issues — both verified fixed

1. Operator alt markers past LR boundaries (Codex P2 on parser.rs:9155) — fixed. The transition now defers the selected alternative first, then prepends the left-recursive boundary. I traced the full deferred rope for a + b through the test ATN: it materializes to [Alt(primary), token, Boundary, Alt(operator), op, right], and the suffix-first walk in materialize_fast_deferred_nodes assigns the operator alt to the outer context and the primary alt to the wrapped operand — no overwrite. 1+2+3 (two boundaries) and a bare primary trace correctly too. The new fast_recognizer_preserves_labeled_left_recursive_operator_context snapshot pins exactly this.

2. Variant-only comparison should skip, not fail (Codex P2 on compare.py:152) — fixed. When every matched pair differs only by benchmark_variant, they land in variant_mismatches and the compared == 0 guard treats it as an intentional skip (exit 0). A genuinely unrelated baseline (no matching keys) leaves variant_mismatches empty and still exits 1 without --allow-empty. A real same-variant regression still populates regression_failures and returns 1. Covered by the new test_compare_skips_only_changed_benchmark_variants / test_compare_rejects_unrelated_result_sets / test_compare_enforces_threshold_for_matching_variants.

✅ CLAUDE.md compliance — clean

  • New tests use named external insta::assert_debug_snapshot! matching the three sibling files under src/snapshots/ — house style.
  • The enclosing mod tests (src/parser.rs:12484) already carries #[allow(clippy::disallowed_methods)]; the new tests are nested inside it, so the insta rule is satisfied.
  • No unordered HashMap/HashSet is snapshotted — the multi-entry snapshots come from descendants() (a Vec-backed pre-order walk), and the third is a plain tuple. Determinism rule respected.
  • Only the three serde-free insta macros are used.
  • Python parse-bench files: the root CLAUDE.md's Rust-specific rules don't scope to them, and the new benchmark_variant JSON field / skip behavior is documented in tools/parse-bench/README.md.

🟡 One non-blocking observation (could not confirm as a defect)

In the repetition path, the enter/exit loop-decision alternative is appended (concat_deferred_nodes(path.deferred_nodes, alternative)) rather than prepended as on the main transition path — parser.rs:8206-8214 and 8272-8279. In principle a single-alt rule wrapping a */+ loop could pick up a non-zero context_alt_number from the loop's internal decision where ANTLR would report 0. I could not confirm any observable wrong result: it's gated on fast_track_alt_numbers, overwritten by any outer decision marker, and not consulted for dispatch on unlabeled single-alt rules — so this is an awareness note, not a blocker. A defensive snapshot over a single-alt rule containing a loop would settle whether the internal number ever leaks. Investigate →

ℹ️ Informational

Codecov reports 92.07% patch coverage (28 uncovered lines in parser.rs). Not a gate here, but worth a glance to confirm the uncovered lines are the error/recovery arms rather than a new materialization branch.

Nice, surgical fix — the eligibility gate was the right thing to relax, and the deferred-rope ordering is carefully done.
· branch fix/issue-174-fast-context-alts

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

📊 Source Code Metrics (this PR vs main)

File Cyclomatic Cognitive Functions LLOC MI
src/parser.rs 2099 (main: 2067) 🔴 1400 (main: 1367) 🔴 654 (main: 645) 🔴 4583 (main: 4482) 🔴 0 ⚪

Generated by mehen v1.6.0 — the code quality watcher.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 0cd00830b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.06799% with 28 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/parser.rs 92.06% 28 Missing ⚠️

📢 Thoughts on this report? Let us know!

@tinovyatkin tinovyatkin changed the title [codex] restore fast Java parsing with typed contexts fix: restore fast Java parsing with typed contexts Jul 23, 2026
@tinovyatkin
tinovyatkin marked this pull request as ready for review July 23, 2026 16:31
Copilot AI review requested due to automatic review settings July 23, 2026 16:36

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@tinovyatkin
tinovyatkin marked this pull request as draft July 23, 2026 16:37
@tinovyatkin
tinovyatkin marked this pull request as ready for review July 23, 2026 16:37
@tinovyatkin
tinovyatkin marked this pull request as draft July 23, 2026 16:40
@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45d9d7245c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/parser.rs
Comment thread tools/parse-bench/compare.py
@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 533793c411

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@tinovyatkin
tinovyatkin marked this pull request as ready for review July 23, 2026 17:35
@tinovyatkin
tinovyatkin merged commit 865ac8f into main Jul 23, 2026
15 of 16 checks passed
@tinovyatkin
tinovyatkin deleted the fix/issue-174-fast-context-alts branch July 23, 2026 17:59
@ophiarch ophiarch Bot mentioned this pull request Jul 23, 2026
tinovyatkin added a commit that referenced this pull request Jul 26, 2026
…uting change

The untouched-JavaParser.g4 lane previously ran the two predicate-bearing
rules (and, via the caller cascade, most of the grammar) through the ATN
interpreter. With untranslated predicates now lowering as generatable
templates, that lane routes through generated rule bodies — a methodology
change, not a runtime regression, and the per-fixture deltas are mixed
by design (mojang 6.4->3.2 ms, google-closure 1.9->0.9 ms, but
bazel-sky-value-retriever 10.4->19.0 ms on the CI runner, where the
generated walker loses to the warmed interpreter DFA for that fixture's
decision mix; the same shape exists on main between the portable and
interpreted lanes).

Bump JAVA_RUST_PREDICATE_VARIANT to v2 so the comparator skips the
mismatched-variant rows and re-arms the 1.15x Java regression gate once
both reports carry v2 — the same reset the v1 tag performed for the
legacy-to-predicate transition in #175.
tinovyatkin added a commit that referenced this pull request Jul 26, 2026
…known templates (#218)

* perf(codegen): lower untranslated parser predicates as generatable Unknown templates

An untranslated predicate body (e.g. a bare this.IsNotIdentifierAssign()
helper call) previously produced no PredicateTemplate, leaving its
coordinate out of the generated set. compile_generated_parser_transition
then refused to compile the containing rule, and with
require_generated_callees active the drop cascaded through
drop_rules_calling_disabled_rules to every calling rule — the untouched
grammars-v4 JavaParser.g4 kept only 15 of 129 generated rule bodies and
routed everything else through the interpreter, 5-6x slower than the
predicate-stripped portable grammar (issue #209).

Lower such coordinates as a new PredicateTemplate::Unknown instead,
mirroring UnknownWithFailMessage: SemIR PExpr::Hook(0), so evaluation
keeps the documented hook -> unknown-policy chain. Typed/closure hooks
stay consulted, --sem-unknown dispositions and --require-full-semantics
behave unchanged, the manifest still reports disposition assume-true
with template null, and a dispose="error" coordinate override still
lowers to no SemIR entry.

Untouched JavaParser.g4 now generates all 129 rule dispatch bodies;
parse times match the lit-true ({ true }?) build within noise:
mojang-data-result.java 14.7 -> 3.1 ms, google-closure-property.java
4.1 -> 0.9 ms (portable baseline 2.2 / 0.6 ms; the small residual is
allow_semantic_context adaptive prediction at the two predicate-bearing
decisions).

Fixes #209

* test(codegen): snapshot the untranslated-predicate template collection

Swap the hand-written assert_eq! in
untranslated_parser_predicate_keeps_generated_rule for a named insta
snapshot per the repository snapshot guidance (pinning a collection's
full contents is a value test, not a property test).

Addresses the Codex review comment on PR #218.

* ci(parse-bench): bump the Java Rust benchmark variant for the #209 routing change

The untouched-JavaParser.g4 lane previously ran the two predicate-bearing
rules (and, via the caller cascade, most of the grammar) through the ATN
interpreter. With untranslated predicates now lowering as generatable
templates, that lane routes through generated rule bodies — a methodology
change, not a runtime regression, and the per-fixture deltas are mixed
by design (mojang 6.4->3.2 ms, google-closure 1.9->0.9 ms, but
bazel-sky-value-retriever 10.4->19.0 ms on the CI runner, where the
generated walker loses to the warmed interpreter DFA for that fixture's
decision mix; the same shape exists on main between the portable and
interpreted lanes).

Bump JAVA_RUST_PREDICATE_VARIANT to v2 so the comparator skips the
mismatched-variant rows and re-arms the 1.15x Java regression gate once
both reports carry v2 — the same reset the v1 tag performed for the
legacy-to-predicate transition in #175.
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.

v0.15.0: Java method-body expression parsing ~2000× slower than pre-release (ALL(*) blowup)

2 participants