Kotlin Grammar Smoke Build

August 3, 2026 ยท View on GitHub

This is the source-only path for building Rust modules from the ANTLR Kotlin grammar.

Inputs

  • Kotlin grammar from antlr/grammars-v4, directory kotlin/kotlin.
  • Keep the checkout and generated scratch files under the repository's ignored target/antlr-cleanroom/; cargo clean removes them.

Generate Rust Modules

GRAMMAR=target/antlr-cleanroom/grammars-v4/kotlin/kotlin

cargo run -p antlr-rust-codegen --bin antlr4-rust-gen -- \
  "$GRAMMAR/KotlinLexer.g4" \
  "$GRAMMAR/KotlinParser.g4" \
  --lib "$GRAMMAR" \
  --require-generated-parser \
  --out-dir target/antlr-cleanroom/kotlin-rust

UnicodeClasses.g4 is resolved through --lib; it does not need to be listed as another root.

This emits:

  • kotlin_lexer.rs
  • kotlin_parser.rs

The generated lexer caches its deserialized lexer ATN with OnceLock. The generated parser embeds a versioned packed parser ATN, validates it once without rebuilding an object graph, and delegates recognition to antlr4_runtime.

Choose the Kotlin Entry Rule

antlr4-rust-gen emits one public parser method for every grammar rule and lists those methods in the generated parser rustdoc. Choose the method that matches the Kotlin input shape rather than assuming the first rule is always right:

  • .kt compilation units use parser.kotlin_file().
  • .kts script-style input uses parser.script().

ANTLR recovery can produce a parse tree even when the wrong entry rule is used, but the tree will contain recovered error nodes and diagnostics. When adding a new Kotlin input form, confirm the entry rule against the upstream grammar and check parser diagnostics.

Smoke Crate

Create any Rust crate that depends on this runtime:

[dependencies]
antlr-rust-runtime = { path = "../path/to/runtime-crate" }

Replace the path with the relative path from the smoke crate to this checkout.

Then include the generated modules and parse a Kotlin sample:

use generated::kotlin_lexer::KotlinLexer;
use generated::kotlin_parser::{self, KotlinParser};

let tree = kotlin_parser::parse("fun main() {}", KotlinLexer::new, KotlinParser::kotlin_file)
    .expect("entry rule parses");
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:

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());

Use parse_stream to parse directly from a Rust reader while retaining the input's source name:

use std::fs::File;
use antlr4_runtime::InputStream;
use generated::kotlin_lexer::KotlinLexer;
use generated::kotlin_parser::{self, KotlinParser};

let path = std::path::Path::new("Main.kt");
let input = InputStream::from_reader_with_source_name(
    File::open(path).expect("Kotlin source should open"),
    path.display().to_string(),
)
.expect("Kotlin source should be UTF-8");
let parsed = kotlin_parser::parse_stream(input, KotlinLexer::new, KotlinParser::kotlin_file)
    .expect("entry rule parses");
assert!(parsed.tree().text().contains("fun"));

InputStream::from_reader also accepts stdin, sockets, cursors, and other std::io::Read implementations when no explicit source name is needed.

The generated helpers are additive. The explicit path remains available when the caller needs to adjust parser options or attach custom error handling before the entry rule:

use antlr4_runtime::{CommonTokenStream, InputStream};
use generated::kotlin_lexer::KotlinLexer;
use generated::kotlin_parser::KotlinParser;

let lexer = KotlinLexer::new(InputStream::new("fun main() {}"));
let tokens = CommonTokenStream::new(lexer);
let mut parser = KotlinParser::new(tokens);
let tree = parser.kotlin_file().expect("entry rule parses");
assert!(tree.text().contains("fun"));

Validated locally: the generated Kotlin lexer emits real tokens and the generated parser recognizes the parser.kotlin_file() entry rule for fun main() {}.