The compiler ships as a standalone binary that loads the embedded BNF grammar, builds a parser, and runs a suite of RPG IV snippet examples to demonstrate the grammar in action:
```rust-langrpg/README.md
cargo run --bin demo
```
You will see output similar to:
```rust-langrpg/README.md
=== RPG IV Free-Format Parser ===
[grammar] Loaded successfully.
[parser] Built successfully (all non-terminals resolved).
The RPG IV free-format grammar is encoded in BNF notation in `src/rpg.bnf` and embedded at compile time with `include_str!`. At startup the compiler parses the grammar with the [`bnf`](https://docs.rs/bnf/latest/bnf/) crate to build a `GrammarParser`. Each source file is validated against the top-level `<program>` rule before any further processing. This stage acts as a gate: malformed source is rejected early with a clear parse error.
### Stage 2 — Lowering to a typed AST (`src/lower.rs`)
The BNF parser only validates structure; it does not produce a typed tree suitable for code generation. A hand-written tokenizer and recursive-descent parser in `lower.rs` converts the raw source text into the typed `Program` AST defined in `src/ast.rs`.
The AST covers the full language surface that the compiler handles:
Unrecognised constructs produce `Statement::Unimplemented` or placeholder declaration variants rather than hard errors, so the compiler continues to lower the parts it understands.
The typed `Program` is handed to the code generator, which uses [`inkwell`](https://crates.io/crates/inkwell) (safe Rust bindings to LLVM 21) to build an LLVM IR module:
- Each `DCL-PROC … END-PROC` becomes an LLVM function.
- An exported procedure named `main` (or the first exported procedure) is wrapped in a C `main()` entry point so the resulting binary is directly executable.
-`DCL-S` standalone variables are allocated as `alloca` stack slots inside their owning function, or as LLVM global variables for module-scope declarations.
- String literals are stored as null-terminated byte arrays in `.rodata`.
-`DSPLY expr;` is lowered to a call to `rpg_dsply(ptr, len)` (or `rpg_dsply_i64` / `rpg_dsply_f64` for numeric types) provided by the runtime library.
- Control-flow constructs (`IF`, `DOW`, `DOU`, `FOR`, `SELECT`) are lowered to LLVM basic blocks and conditional / unconditional branches.
-`LEAVE` / `ITER` are lowered to `br` to the loop-exit / loop-header block respectively, tracked via a `FnState` per function.
The module is then compiled to a native `.o` object file for the host target via LLVM's target machine API, with optional optimisation passes (`-O0` through `-O3`).
### Stage 4 — Linking
The object file is linked into a standalone ELF executable by invoking the system C compiler (`cc`). The executable is linked against `librpgrt.so`.
### Runtime library (`rpgrt/`)
`rpgrt` is a separate Cargo crate built as a `cdylib`, producing `librpgrt.so`. It is written in Rust and exports a C ABI used by compiled RPG programs:
| Symbol | Signature | Purpose |
|--------|-----------|---------|
| `rpg_dsply` | `(ptr: *const u8, len: i64)` | Display a fixed-length `CHAR` field (trims trailing spaces) |
| `rpg_dsply_cstr` | `(ptr: *const c_char)` | Display a null-terminated C string |
| `rpg_dsply_i64` | `(n: i64)` | Display a signed 64-bit integer |