Skip to content

feat(runtime): expose generated parser bail/error-strategy configuration publicly #358

Description

@tinovyatkin

Summary

The runtime already implements bail-on-first-error behavior:

  • BailErrorStrategy;
  • BaseParser::set_bail_on_error(bool);
  • BaseParser::bail_on_error().

Generated parser users cannot configure it through the normal public generated
API because the generated parser's BaseParser field is private. The only
generated set_error_handler(BailErrorStrategy) helper is a private
compatibility method emitted for embedded test/action bodies:

embedded_parser_facades.

Expose the existing behavior as a normal public generated-parser capability.

The sdf-labs/antlr4 target exposes arbitrary boxed error strategies through
with_strategy and set_error_strategy, but its unsafe delegate and
per-operation dynamic dispatch should not be copied merely for API similarity.
The immediate useful surface is the already implemented default-versus-bail
choice.

Current limitation

An external caller cannot write:

let mut parser = MyParser::new(tokens);
parser.set_error_handler(BailErrorStrategy::new());
let tree = parser.file()?;

or:

parser.set_bail_on_error(true);

without a grammar-specific embedded member/helper or exposing runtime internals.

Constructor-aware parse drivers added in #349 allow callers to choose
MyParser::with_typed_hooks, but the constructor closure still cannot reach the
private parser base to enable bail behavior.

Goal

Add a public, grammar-independent generated-parser facade for bail behavior.

A minimal API could be:

impl<L, H> MyParser<L, H> {
    pub fn set_bail_on_error(&mut self, enabled: bool);
    pub fn bail_on_error(&self) -> bool;

    // Optional ANTLR-shaped convenience:
    pub fn set_error_handler(&mut self, strategy: BailErrorStrategy);
}

Prefer placing the implementation in
__antlr4_rust_parser_facade! so compatible older generated macro invocations
gain the additive API from a newer runtime without adding per-grammar
boilerplate.

If a general custom ErrorStrategy abstraction is desired later, evaluate it
separately against:

  • default-path branch/dynamic-dispatch overhead;
  • ownership and borrowing without unsafe self-referential delegates;
  • interaction with generated direct rules and interpreted fallback;
  • recovery diagnostics, listeners, and parse-tree construction.

Required behavior

  • Default construction retains the current recovering strategy.
  • Bail mode aborts at the first syntax error instead of attempting inline or
    rule-level recovery.
  • The offending token and normal error listener notification remain available.
  • Fatal semantic/runtime failures retain their existing classification.
  • Define whether reset() preserves the configured strategy, matching the
    project's recognizer-reuse contract.
  • Parser reuse and set_token_stream must not silently revert the choice.
  • The disabled/default path must add no measurable parser hot-path overhead
    beyond the existing bail_on_error branch.

Acceptance criteria

  • A normally generated parser exposes public bail configuration without
    embedded actions, custom members, or private-field access.
  • The API works through a parser constructor passed to
    parse_with_parser_constructor and
    parse_stream_with_parser_constructor.
  • Tests distinguish recovering and bail behavior on single-token
    insertion, single-token deletion, no-viable-alternative, and nested-rule
    failures.
  • Error listeners receive the first bail diagnostic with the correct
    offending token and position.
  • Reset/reuse semantics are documented and tested.
  • Generated and interpreted rule paths abort consistently.
  • Default-mode performance remains neutral.
  • The generated-code API revision changes only if the emitted-source/runtime
    contract actually requires it.

Related

Non-goals

  • Copying sdf-labs' unsafe ErrorStrategyDelegate.
  • Designing arbitrary grammar-specific recovery strategies in the first patch.
  • Exposing the generated parser's private BaseParser field.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions