Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ fn main() -> Result<(), antlr4_runtime::AntlrError> {
}
```

Use `parse_with_parser` when you want the compact setup path and also need the
parser afterward for diagnostics or the owned token stream:

```rust
use antlr4_runtime::Parser;
use generated::json::{self, Json};
use generated::json_lexer::JsonLexer;

fn main() -> Result<(), antlr4_runtime::AntlrError> {
let output = json::parse_with_parser(r#"{"a":1}"#, JsonLexer::new, Json::json)?;
let syntax_errors = output.parser.number_of_syntax_errors();
let tree = output.result;
let tokens = output.parser.into_token_stream();

println!("{} errors across {} tokens", syntax_errors, tokens.tokens().len());
println!("{}", tree.text());
Ok(())
}
```

Or construct each layer explicitly when you need to set source names, parser
options, or custom error handling before invoking the entry rule:

Expand Down
20 changes: 20 additions & 0 deletions docs/kotlin-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ let tree = kotlin_parser::parse("fun main() {}", KotlinLexer::new, KotlinParser:
assert!(tree.text().contains("fun"));
```

Use `parse_with_parser` when a caller also needs parser state after the entry
rule, such as syntax diagnostics or the token stream:

```rust
use antlr4_runtime::Parser;
use generated::kotlin_lexer::KotlinLexer;
use generated::kotlin_parser::{self, KotlinParser};

let output =
kotlin_parser::parse_with_parser("fun main() {}", KotlinLexer::new, KotlinParser::kotlin_file)
.expect("entry rule parses");
let syntax_errors = output.parser.number_of_syntax_errors();
let tree = output.result;
let tokens = output.parser.into_token_stream();

assert_eq!(syntax_errors, 0);
assert!(tree.text().contains("fun"));
assert!(!tokens.tokens().is_empty());
```

The generated helper is additive. The explicit path is still available when the
caller needs to name the input source, adjust parser options, or attach custom
error handling before the entry rule:
Expand Down
72 changes: 66 additions & 6 deletions src/bin/antlr4-rust-gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7902,24 +7902,54 @@ fn render_parser_base_initialization(members: &[IntMemberTemplate]) -> String {
/// Renders the parser-module convenience that wires text input through the
/// caller-selected lexer, token stream, parser, and entry rule in one call.
fn render_parser_parse_convenience(type_name: &str) -> String {
let output_type_name = format!("{type_name}ParseOutput");
format!(
r#"/// Parses UTF-8 text by constructing the lexer, token stream, parser, and
r#"/// Result from [`parse_with_parser`].
///
/// Keeps the generated parser available after the entry rule runs so callers
/// can inspect diagnostics or recover the parser-owned token stream.
#[derive(Debug)]
pub struct {output_type_name}<R, L>
where
L: TokenSource,
{{
pub result: R,
pub parser: {type_name}<L>,
}}

/// Parses UTF-8 text by constructing the lexer, token stream, parser, and
/// caller-selected entry rule in one call.
///
/// Pass the generated lexer constructor and a parser entry rule, for example
/// `parse(src, MyGrammarLexer::new, {type_name}::file)`.
pub fn parse<L, R>(
///
/// Use [`parse_with_parser`] instead when the caller needs parser diagnostics
/// or the parser-owned token stream after the entry rule runs.
pub fn parse<L: TokenSource, R>(
input: impl AsRef<str>,
lexer: impl FnOnce(antlr4_runtime::InputStream) -> L,
entry: impl FnOnce(&mut {type_name}<L>) -> Result<R, antlr4_runtime::AntlrError>,
) -> Result<R, antlr4_runtime::AntlrError>
where
L: TokenSource,
{{
parse_with_parser(input, lexer, entry).map(|output| output.result)
}}

/// Parses UTF-8 text like [`parse`] while returning the parser after the entry
/// rule has run.
///
/// This keeps the compact generated setup path available for callers that also
/// need `Parser::number_of_syntax_errors()` or `{type_name}::into_token_stream()`.
pub fn parse_with_parser<L: TokenSource, R>(
input: impl AsRef<str>,
lexer: impl FnOnce(antlr4_runtime::InputStream) -> L,
entry: impl FnOnce(&mut {type_name}<L>) -> Result<R, antlr4_runtime::AntlrError>,
) -> Result<{output_type_name}<R, L>, antlr4_runtime::AntlrError>
{{
let lexer = lexer(antlr4_runtime::InputStream::new(input.as_ref()));
let tokens = CommonTokenStream::new(lexer);
let mut parser = {type_name}::new(tokens);
entry(&mut parser)
let result = entry(&mut parser)?;
Ok({output_type_name} {{ result, parser }})
}}"#
)
}
Expand Down Expand Up @@ -9250,13 +9280,43 @@ s : ;
let rendered =
render_parser("TParser", &minimal_parser_data(), None).expect("parser should render");

assert!(rendered.contains("pub fn parse<L, R>("));
assert!(rendered.contains("pub struct TParserParseOutput<R, L>"));
assert!(rendered.contains("pub result: R,"));
assert!(rendered.contains("pub parser: TParser<L>,"));
assert!(rendered.contains("pub fn parse<L: TokenSource, R>("));
assert!(rendered.contains("pub fn parse_with_parser<L: TokenSource, R>("));
assert!(
!rendered
.contains(") -> Result<R, antlr4_runtime::AntlrError>\nwhere\n L: TokenSource,")
);
assert!(!rendered.contains(
") -> Result<TParserParseOutput<R, L>, antlr4_runtime::AntlrError>\nwhere\n L: TokenSource,"
));
assert!(rendered.contains("lexer: impl FnOnce(antlr4_runtime::InputStream) -> L"));
assert!(rendered.contains("antlr4_runtime::InputStream::new(input.as_ref())"));
assert!(rendered.contains("let tokens = CommonTokenStream::new(lexer);"));
assert!(rendered.contains("let result = entry(&mut parser)?;"));
assert!(rendered.contains("Ok(TParserParseOutput { result, parser })"));
assert!(
rendered.contains("parse_with_parser(input, lexer, entry).map(|output| output.result)")
);
assert!(rendered.contains("pub fn new(input: CommonTokenStream<S>) -> Self"));
}

#[test]
fn generated_parse_output_name_does_not_collide_with_parser_type() {
let rendered = render_parser("ParseOutput", &minimal_parser_data(), None)
.expect("parser should render");

assert!(rendered.contains("pub struct ParseOutputParseOutput<R, L>"));
assert!(rendered.contains("pub parser: ParseOutput<L>,"));
assert!(
rendered
.contains(") -> Result<ParseOutputParseOutput<R, L>, antlr4_runtime::AntlrError>")
);
assert!(rendered.contains("Ok(ParseOutputParseOutput { result, parser })"));
}

#[test]
fn generated_parser_reports_lexer_errors_on_outer_success() {
let rendered =
Expand Down
Loading