
LR(1) parser generator implementing the lalrpop grammar and architecture, offering a configuration API to process .lalrpop grammars, Cargo-style conventions and code generation.
lalrpop-kotlin is a 100% pure Kotlin Multiplatform LR(1) and LALR(1) parser generator framework designed around grammar readability, expressiveness, and actionable diagnostics. It is a faithful 1:1 transliteration port of the Rust lalrpop/lalrpop crate (v0.23.1), paired with the companion lalrpop-util-kotlin runtime.
.lalrpop Grammars: Write expressive context-free grammars with parameterized macros (such as Comma<Id>), conditional nonterminal subsets (Expr<"all"> vs. Expr<"if">), built-in repetition and optional operators (*, +, ?), precedence/associativity annotations (#[precedence], #[assoc]), compact default actions, and nonterminal type inference.#[LALR] support), deterministic state/item ordering, and rich conflict diagnostics rendered with ASCII parse trees via ascii-canvas.FileText) and Unicode scalar boundaries (Tokenizer, CharIndices, Escape), ensuring multi-line error highlights (reportParseError), comments, string/regex literals, and supplementary Astral Unicode (U+10000..U+10FFFF) match upstream byte-for-byte.lalrpop-kotlin's own .lalrpop grammar parser (LrGrammar.kt) runs natively in Kotlin on lalrpop-util-kotlin, alongside packed ShortArray table generation (ACTION, EOF_ACTION, GOTO) verified cell-for-cell against real-world grammars such as Starlark.btree-kotlin, itertools-kotlin, km-io, logos-kotlin, rand-kotlin, regex-kotlin, regex-syntax-kotlin, lalrpop-util-kotlin)—zero JVM-only java.* lock-in—plus native Swift Export (Lalrpop SPM package & XCFramework).Add lalrpop-kotlin (and lalrpop-util-kotlin when driving generated parsers at runtime) to your commonMain dependencies in build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.kotlinmania:lalrpop-kotlin:0.2.0")
implementation("io.github.kotlinmania:lalrpop-util-kotlin:0.1.1")
}
}
}dependencies {
implementation("io.github.kotlinmania:lalrpop-kotlin:0.2.0")
}[versions]
lalrpop = "0.2.0"
lalrpopUtil = "0.1.1"
[libraries]
lalrpop = { module = "io.github.kotlinmania:lalrpop-kotlin", version.ref = "lalrpop" }
lalrpop-util = { module = "io.github.kotlinmania:lalrpop-util-kotlin", version.ref = "lalrpopUtil" }<dependency>
<groupId>io.github.kotlinmania</groupId>
<artifactId>lalrpop-kotlin-jvm</artifactId>
<version>0.2.0</version>
</dependency>Build the Swift Export SPM package and XCFramework locally:
./gradlew embedSwiftExportForXcodeThen import the exported Lalrpop module in Swift (macOS 15+, Swift 6):
import LalrpopUse io.github.kotlinmania.lalrpop.api.Configuration to configure input/output directories, conditional #[cfg(feature = "...")] flags, diagnostic verbosity, report generation, and build caching:
import io.github.kotlinmania.lalrpop.api.Configuration
import io.github.kotlinmania.lalrpop.api.processRoot
import io.github.kotlinmania.lalrpop.api.processSrc
fun generateParsers() {
Configuration.new()
.setInDir("src/main/grammars")
.setOutDir("build/generated/lalrpop")
.useColorsIfTty()
.logInfo()
.emitComments(true)
.emitReport(true)
.setMacroRecursionLimit(200)
.setFeatures(listOf("ast-spans"))
.forceBuild(false)
.process()
}
// Or use the convenience entry points for standard directory layouts:
fun generateFromSrc() = processSrc()
fun generateFromRoot() = processRoot()lalrpop-kotlin parses standard .lalrpop grammar files with macros, precedence tiers, and typed productions:
grammar;
pub Expr: i32 = {
#[precedence(level="1")]
Term,
#[precedence(level="2")] #[assoc(side="left")]
<l:Expr> "+" <r:Expr> => l + r,
<l:Expr> "-" <r:Expr> => l - r,
};
Term: i32 = {
Num,
"(" <Expr> ")",
};
Num: i32 = <s:r"[0-9]+"> => i32::from_str(s).unwrap();
For full .lalrpop syntax tutorials, macros, lexer customization, error recovery (!), and conditional compilation, see The LALRPOP Book and the Cheat Sheet.
Upstream Rust (lalrpop) |
Kotlin Multiplatform (lalrpop-kotlin) |
|---|---|
lalrpop::Configuration::new() |
Configuration.new() (io.github.kotlinmania.lalrpop.api) |
cfg.set_in_dir(dir) / cfg.set_out_dir(dir)
|
cfg.setInDir(dir) / cfg.setOutDir(dir)
|
cfg.generate_in_source_tree() |
cfg.generateInSourceTree() |
cfg.use_cargo_dir_conventions() |
cfg.useCargoDirConventions() |
cfg.force_build(true) |
cfg.forceBuild(true) |
cfg.emit_comments(true) / cfg.emit_report(true)
|
cfg.emitComments(true) / cfg.emitReport(true)
|
cfg.set_features(["feat"]) |
cfg.setFeatures(listOf("feat")) |
cfg.process() / cfg.process_file(path)
|
cfg.process() / cfg.processFile(path)
|
lalrpop::process_root() / lalrpop::process_src()
|
processRoot() / processSrc()
|
lalrpop_util runtime crate |
io.github.kotlinmania:lalrpop-util-kotlin |
On Apple platforms, lalrpop-kotlin exports its configuration and diagnostic enums directly to Swift 6:
import Lalrpop
let config = api.Configuration.Companion.shared.new()
.useColorsIfTty()
.logInfo()
.setMacroRecursionLimit(val: 200)
.emitComments(val: true)
.emitReport(val: true)lalrpop-kotlin builds and validates across all 17 supported 64-bit and Web KotlinMania targets:
| Platform Family | Gradle Targets | Notes |
|---|---|---|
| JVM | jvm |
JDK 21 toolchain |
| Android | androidLibrary |
minSdk = 24, compileSdk = 34
|
| Android Native |
androidNativeArm64, androidNativeX64
|
64-bit Android NDK targets |
| macOS | macosArm64 |
Apple Silicon + Swift 6 Export (Lalrpop SPM & XCFramework) |
| iOS |
iosArm64, iosSimulatorArm64
|
Static frameworks + XCFramework |
| watchOS |
watchosDeviceArm64, watchosSimulatorArm64
|
64-bit Apple Watch device & simulator |
| tvOS |
tvosArm64, tvosSimulatorArm64
|
Apple TV device & simulator |
| Linux |
linuxX64, linuxArm64
|
Native Linux binaries |
| Windows | mingwX64 |
Native 64-bit Windows binaries |
| JavaScript | js(IR) |
Browser & Node.js |
| WebAssembly |
wasmJs, wasmWasi
|
Browser, Node.js, and WASI runtimes |
# Run all host unit tests and the local Swift 6 Export test harness
./gradlew test
# Run the full 17-target multiplatform build gate, static analysis (detekt + ktlint), and cross-target validation
./gradlew buildSee AGENTS.md and SWIFT.md for workspace porting conventions, port-lint provenance headers, and Swift Export guidelines.
lalrpop-kotlin is a faithful 1:1 Kotlin Multiplatform transliteration of the upstream Rust lalrpop parser generator framework (v0.23.1).
All credit for the original compiler architecture, LR(1) lane-table construction, grammar macro system, type inference, error recovery design, documentation, and test suites belongs to the upstream Rust creators, maintainers, and contributors. We extend our sincere thanks and appreciation to:
lalrpop/lalrpop, ena, and ascii-canvas, whose vision made LR(1) parser generation genuinely ergonomic and approachable.docs.rs): https://docs.rs/lalrpop/latest/lalrpop/
This repository brings their work natively to the Kotlin Multiplatform and Swift ecosystems. Questions or bug reports concerning upstream LALRPOP grammar semantics belong to lalrpop/lalrpop; issues specific to the Kotlin Multiplatform port or Swift Export integration belong in KotlinMania/lalrpop-kotlin.
lalrpop-kotlin is dual-licensed under the same terms as upstream lalrpop/lalrpop, at your option:
LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0
LICENSE-MIT or https://opensource.org/licenses/MIT
Original Rust work: Copyright (c) 2015–2025 The LALRPOP Project Developers.
Kotlin Multiplatform port: Copyright (c) 2026 Sydney Renee and The Solace Project.
lalrpop-kotlin is a 100% pure Kotlin Multiplatform LR(1) and LALR(1) parser generator framework designed around grammar readability, expressiveness, and actionable diagnostics. It is a faithful 1:1 transliteration port of the Rust lalrpop/lalrpop crate (v0.23.1), paired with the companion lalrpop-util-kotlin runtime.
.lalrpop Grammars: Write expressive context-free grammars with parameterized macros (such as Comma<Id>), conditional nonterminal subsets (Expr<"all"> vs. Expr<"if">), built-in repetition and optional operators (*, +, ?), precedence/associativity annotations (#[precedence], #[assoc]), compact default actions, and nonterminal type inference.#[LALR] support), deterministic state/item ordering, and rich conflict diagnostics rendered with ASCII parse trees via ascii-canvas.FileText) and Unicode scalar boundaries (Tokenizer, CharIndices, Escape), ensuring multi-line error highlights (reportParseError), comments, string/regex literals, and supplementary Astral Unicode (U+10000..U+10FFFF) match upstream byte-for-byte.lalrpop-kotlin's own .lalrpop grammar parser (LrGrammar.kt) runs natively in Kotlin on lalrpop-util-kotlin, alongside packed ShortArray table generation (ACTION, EOF_ACTION, GOTO) verified cell-for-cell against real-world grammars such as Starlark.btree-kotlin, itertools-kotlin, km-io, logos-kotlin, rand-kotlin, regex-kotlin, regex-syntax-kotlin, lalrpop-util-kotlin)—zero JVM-only java.* lock-in—plus native Swift Export (Lalrpop SPM package & XCFramework).Add lalrpop-kotlin (and lalrpop-util-kotlin when driving generated parsers at runtime) to your commonMain dependencies in build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.kotlinmania:lalrpop-kotlin:0.2.0")
implementation("io.github.kotlinmania:lalrpop-util-kotlin:0.1.1")
}
}
}dependencies {
implementation("io.github.kotlinmania:lalrpop-kotlin:0.2.0")
}[versions]
lalrpop = "0.2.0"
lalrpopUtil = "0.1.1"
[libraries]
lalrpop = { module = "io.github.kotlinmania:lalrpop-kotlin", version.ref = "lalrpop" }
lalrpop-util = { module = "io.github.kotlinmania:lalrpop-util-kotlin", version.ref = "lalrpopUtil" }<dependency>
<groupId>io.github.kotlinmania</groupId>
<artifactId>lalrpop-kotlin-jvm</artifactId>
<version>0.2.0</version>
</dependency>Build the Swift Export SPM package and XCFramework locally:
./gradlew embedSwiftExportForXcodeThen import the exported Lalrpop module in Swift (macOS 15+, Swift 6):
import LalrpopUse io.github.kotlinmania.lalrpop.api.Configuration to configure input/output directories, conditional #[cfg(feature = "...")] flags, diagnostic verbosity, report generation, and build caching:
import io.github.kotlinmania.lalrpop.api.Configuration
import io.github.kotlinmania.lalrpop.api.processRoot
import io.github.kotlinmania.lalrpop.api.processSrc
fun generateParsers() {
Configuration.new()
.setInDir("src/main/grammars")
.setOutDir("build/generated/lalrpop")
.useColorsIfTty()
.logInfo()
.emitComments(true)
.emitReport(true)
.setMacroRecursionLimit(200)
.setFeatures(listOf("ast-spans"))
.forceBuild(false)
.process()
}
// Or use the convenience entry points for standard directory layouts:
fun generateFromSrc() = processSrc()
fun generateFromRoot() = processRoot()lalrpop-kotlin parses standard .lalrpop grammar files with macros, precedence tiers, and typed productions:
grammar;
pub Expr: i32 = {
#[precedence(level="1")]
Term,
#[precedence(level="2")] #[assoc(side="left")]
<l:Expr> "+" <r:Expr> => l + r,
<l:Expr> "-" <r:Expr> => l - r,
};
Term: i32 = {
Num,
"(" <Expr> ")",
};
Num: i32 = <s:r"[0-9]+"> => i32::from_str(s).unwrap();
For full .lalrpop syntax tutorials, macros, lexer customization, error recovery (!), and conditional compilation, see The LALRPOP Book and the Cheat Sheet.
Upstream Rust (lalrpop) |
Kotlin Multiplatform (lalrpop-kotlin) |
|---|---|
lalrpop::Configuration::new() |
Configuration.new() (io.github.kotlinmania.lalrpop.api) |
cfg.set_in_dir(dir) / cfg.set_out_dir(dir)
|
cfg.setInDir(dir) / cfg.setOutDir(dir)
|
cfg.generate_in_source_tree() |
cfg.generateInSourceTree() |
cfg.use_cargo_dir_conventions() |
cfg.useCargoDirConventions() |
cfg.force_build(true) |
cfg.forceBuild(true) |
cfg.emit_comments(true) / cfg.emit_report(true)
|
cfg.emitComments(true) / cfg.emitReport(true)
|
cfg.set_features(["feat"]) |
cfg.setFeatures(listOf("feat")) |
cfg.process() / cfg.process_file(path)
|
cfg.process() / cfg.processFile(path)
|
lalrpop::process_root() / lalrpop::process_src()
|
processRoot() / processSrc()
|
lalrpop_util runtime crate |
io.github.kotlinmania:lalrpop-util-kotlin |
On Apple platforms, lalrpop-kotlin exports its configuration and diagnostic enums directly to Swift 6:
import Lalrpop
let config = api.Configuration.Companion.shared.new()
.useColorsIfTty()
.logInfo()
.setMacroRecursionLimit(val: 200)
.emitComments(val: true)
.emitReport(val: true)lalrpop-kotlin builds and validates across all 17 supported 64-bit and Web KotlinMania targets:
| Platform Family | Gradle Targets | Notes |
|---|---|---|
| JVM | jvm |
JDK 21 toolchain |
| Android | androidLibrary |
minSdk = 24, compileSdk = 34
|
| Android Native |
androidNativeArm64, androidNativeX64
|
64-bit Android NDK targets |
| macOS | macosArm64 |
Apple Silicon + Swift 6 Export (Lalrpop SPM & XCFramework) |
| iOS |
iosArm64, iosSimulatorArm64
|
Static frameworks + XCFramework |
| watchOS |
watchosDeviceArm64, watchosSimulatorArm64
|
64-bit Apple Watch device & simulator |
| tvOS |
tvosArm64, tvosSimulatorArm64
|
Apple TV device & simulator |
| Linux |
linuxX64, linuxArm64
|
Native Linux binaries |
| Windows | mingwX64 |
Native 64-bit Windows binaries |
| JavaScript | js(IR) |
Browser & Node.js |
| WebAssembly |
wasmJs, wasmWasi
|
Browser, Node.js, and WASI runtimes |
# Run all host unit tests and the local Swift 6 Export test harness
./gradlew test
# Run the full 17-target multiplatform build gate, static analysis (detekt + ktlint), and cross-target validation
./gradlew buildSee AGENTS.md and SWIFT.md for workspace porting conventions, port-lint provenance headers, and Swift Export guidelines.
lalrpop-kotlin is a faithful 1:1 Kotlin Multiplatform transliteration of the upstream Rust lalrpop parser generator framework (v0.23.1).
All credit for the original compiler architecture, LR(1) lane-table construction, grammar macro system, type inference, error recovery design, documentation, and test suites belongs to the upstream Rust creators, maintainers, and contributors. We extend our sincere thanks and appreciation to:
lalrpop/lalrpop, ena, and ascii-canvas, whose vision made LR(1) parser generation genuinely ergonomic and approachable.docs.rs): https://docs.rs/lalrpop/latest/lalrpop/
This repository brings their work natively to the Kotlin Multiplatform and Swift ecosystems. Questions or bug reports concerning upstream LALRPOP grammar semantics belong to lalrpop/lalrpop; issues specific to the Kotlin Multiplatform port or Swift Export integration belong in KotlinMania/lalrpop-kotlin.
lalrpop-kotlin is dual-licensed under the same terms as upstream lalrpop/lalrpop, at your option:
LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0
LICENSE-MIT or https://opensource.org/licenses/MIT
Original Rust work: Copyright (c) 2015–2025 The LALRPOP Project Developers.
Kotlin Multiplatform port: Copyright (c) 2026 Sydney Renee and The Solace Project.