Document id: lang-forge-examples-v1
Status: active
Last updated: 2026-07-10
Owner: Project maintainers
Scope: Examples and fixture guide for LangForge
Each runnable example has the same broad shape:
*.lf grammar file
-> lang-forge generate
-> generated scanner/parser under generated/ or Generated/
-> handwritten reducer or adapter outside generated/
-> command-line demo under cmd/
-> binary and logs under dist/
Only generated/ or Generated/ is produced by LangForge. Directories such as
cmd, semantics, .NET project files, and adapter files outside generated
folders are normal source code. They are written by the example author and may
import the generated package.
Several Go files start with //go:build langforge_generated. That is a Go
build tag. It tells Go to compile those files only after the example Makefile
has generated the local scanner/parser package and passes
-tags langforge_generated.
For a fuller beginner guide, including what {go: add} means, read
Generated Code And Semantics.
For reusable per-language parser facade, reducer, library, dependency
injection, and multi-parser layouts, read
Handwritten Integration Guide.
The examples accept a LANG_FORGE Makefile override. That command can be the
source checkout, a standalone binary, an installed binary, or a Docker image.
For the reusable Makefile and Docker patterns, read
Invocation And Layout Patterns.
Examples also default LANG_FORGE_VERBOSITY to 1, so make run, make test, and make generate show the major LangForge stages while the specs are
validated and generated. Set it to 0 for quiet script output, or raise it
while debugging a grammar:
make -C examples/go/calc run LANG_FORGE_VERBOSITY=0
make -C examples/go/calc generate LANG_FORGE_VERBOSITY=2
make -C examples/parser-algorithms test LANG_FORGE_VERBOSITY=3make examples-parity runs two fast source checks. The spec parity checker
compares equivalent .lf files after normalizing target-specific directives.
The action-manifest parity checker builds the same contracts written to
langforge.actions.json and compares action labels, RHS labels, portable typed
semantic roles, typed/untyped status, and recovery reporting contracts. When a
target-specific difference is intentional, document it in
examples/manifest-parity.allowlist.json
with the reported family, target, path, and a short reason.
The Go examples need Go and make. The C# examples need the .NET 10.0 SDK
because their projects target net10.0. The C examples need GCC or another
C11-capable compiler for build/run targets; set CC=clang or another compiler
when needed. The C++ examples need g++, clang++, or another C++17-capable
compiler; set CXX=clang++ or another compiler when needed. The full matrix is
in Requirements.
The main example is:
It defines:
- a number/operator lexer;
- an expression grammar with precedence encoded through nonterminals;
- Go package name
calc; - generated output under
examples/go/calc/generated, created on demand.
Run:
make -C examples/go/calc runThe runnable demo reads examples/go/calc/input.calc,
regenerates the scanner/parser, builds dist/calc-demo, parses through
NewReaderScanner, evaluates the expression through generated reduction
hooks, and writes the same report to
examples/go/calc/dist/calc-demo.log. It also tokenizes afterward to print a
teaching token stream in the report.
The calc family is the compact cross-target demonstration of streamed scanner
input. Go uses io.Reader, C# uses TextReader/Stream, C uses a read
callback with *_stream_scanner, and C++ uses InputStreamScanner over
std::istream. Token-list parsing remains in the examples for tests,
debugging, tests, and token inspection.
The calc example also demonstrates reducer-mode handwritten package references:
calc.lf declares a %semantic go import and the demo wires that package as
the generated parser reducer.
In calc, {go: add} and {go: subtract} are labels. They are not arithmetic
code generated by LangForge. The handwritten reducer in
examples/go/calc/semantics/reducer.go
uses generated semantic action constants and ReducerMap to map those labels
to arithmetic behavior.
The sample expression is:
1 + 2 * (3 - 4.5)
The example also supports direct generated-code testing:
make -C examples/go/calc testGenerated artifacts under examples/go/calc/generated and examples/go/calc/dist
are ignored by Git. Use make -C examples/go/calc clean to remove them.
C# examples live under:
- examples/csharp/calc/calc.lf
- examples/csharp/datakeeper/datakeeper.lf
- examples/csharp/draw/draw.lf
- examples/csharp/parser-recovery/recovery.lf
- examples/csharp/vehicle-report/vehicle.lf
They mirror the Go grammar shapes but use %target csharp, C# namespaces, and
{csharp: ...} action labels:
Expr : Expr Plus Term {csharp: add}
Run the C# family:
make -C examples/csharp/calc run
make -C examples/csharp/datakeeper run
make -C examples/csharp/draw run
make -C examples/csharp/parser-recovery run
make -C examples/csharp/vehicle-report runTest:
make -C examples/csharp/calc test
make -C examples/csharp/datakeeper test
make -C examples/csharp/draw test
make -C examples/csharp/parser-recovery test
make -C examples/csharp/vehicle-report testEach Makefile validates the spec, generates C# scanner/parser files under
Generated/ with .g.cs filenames, builds with .NET 10, runs a handwritten
C# reducer, and writes a local report under dist/.
The C# DRAW example writes dist/sample-csharp.png and a render report. It is
the C# mirror of the Go DRAW PNG workflow, with handwritten C# AST and PNG code
outside Generated/.
For a reusable compiler-style C# starter, see
examples/templates/csharp/layered-compiler.
It hides generated parser details behind IMiniCompilerParser, returns a
domain ParseResult<ProgramNode>, maps generated typed reducer contexts in
Semantics/, and keeps Program.cs as a thin demo.
The C++ examples live under:
- examples/cpp/calc/calc.lf
- examples/cpp/datakeeper/datakeeper.lf
- examples/cpp/draw/draw.lf
- examples/cpp/parser-recovery/recovery.lf
- examples/cpp/vehicle-report/vehicle.lf
They use %target cpp, C++ namespace packages, and {cpp: ...} action labels:
Expr : Expr Plus Term {cpp: add}
Run it:
make -C examples/cpp/calc run
make -C examples/cpp/datakeeper run
make -C examples/cpp/draw run
make -C examples/cpp/parser-recovery run
make -C examples/cpp/vehicle-report runTest it:
make -C examples/cpp/calc test
make -C examples/cpp/datakeeper test
make -C examples/cpp/draw test
make -C examples/cpp/parser-recovery test
make -C examples/cpp/vehicle-report testThe handwritten main.cpp files use generated SemanticAction enum values and
ReducerMap instead of embedding semantics in generated parser code. That
mirrors the preferred C++ backend style from ADR-0014: generated tables stay
static and deterministic, while handwritten semantics are ordinary C++ functions
and lambdas keyed by action IDs.
For a modern C++ starter that hides generated APIs behind a domain parser
facade, uses direct typed reducer handlers, documents ownership with
std::unique_ptr and std::variant, and includes CMake integration, see
examples/templates/cpp/layered-compiler.
C examples live under:
- examples/c/calc/calc.lf
- examples/c/datakeeper/datakeeper.lf
- examples/c/draw/draw.lf
- examples/c/parser-recovery/recovery.lf
- examples/c/vehicle-report/vehicle.lf
They mirror the same language scenarios with %target c, C-oriented
%package prefixes, and {c: ...} action labels. LangForge generates
tokens.h, scanner.h, scanner.c, parser.h, and parser.c under each
example's generated/ directory.
Run the C family:
make -C examples/c/calc run
make -C examples/c/datakeeper run
make -C examples/c/draw run
make -C examples/c/parser-recovery run
make -C examples/c/vehicle-report runTest:
make -C examples/c/calc test
make -C examples/c/datakeeper test
make -C examples/c/draw test
make -C examples/c/parser-recovery test
make -C examples/c/vehicle-report testThe C Makefiles validate and generate without requiring a C compiler. Build and
run steps are skipped with a clear message if CC is unavailable. When a C
compiler is present, the examples compile the generated sources together with
handwritten main.c reducers and the shared
examples/c/common helper module.
The handwritten C examples include generated/parser.h directly instead of
relying on the Makefile include path to find parser.h. The generated header
is still the single source of truth for parser/scanner types; the explicit path
just makes IDE code navigation work after make -C examples/c/... generate.
The C DRAW example writes an actual PNG file through a handwritten C AST and interpreter. It mirrors the Go/C# DRAW flow while using a tiny local RGB/PNG helper instead of external image libraries.
The examples are meant to be read in increasing complexity:
| Step | Example | What it teaches |
|---|---|---|
| 0 | examples/templates/*/mini-compiler |
Copyable scanner/parser/compiler starter shape with AST, reducer, stack-code lowering, runtime, diagnostics, and tests |
| 1 | examples/templates/*/library-dsl |
Reusable library architecture with domain model, typed reducer, parser facade, diagnostics formatter, thin demo, and smoke tests |
| 2 | examples/templates/csharp/layered-compiler |
Modern C# layered compiler architecture with Ast/, Semantics/, Parsing/, IMiniCompilerParser, domain results, and DI-friendly semantic policies |
| 3 | examples/templates/cpp/layered-compiler |
Modern C++17 layered compiler architecture with public headers, source-based parsing, direct typed reducers, move-only AST ownership, and CMake |
| 4 | examples/go/calc |
Small combined .lf, generated scanner/parser, reducer-backed expression value, simple CLI report |
| 5 | examples/{go,csharp,c,cpp}/parser-recovery |
Reserved error productions, synchronization, expected-token aliases, multiple diagnostics, result cleanup/inspection, and progress guarantees |
| 6 | examples/parser-algorithms |
LR parser algorithm differences and expected SLR conflict |
| 7 | examples/go/datakeeper |
DSL syntax, generated reduction hooks, AST adapter, stack-machine lowering, mock execution |
| 8 | examples/go/draw |
DSL syntax, generated reduction hooks, interpreter, reusable blocks, rendered output |
| 9 | examples/go/vehicle-report |
Exercise-style Flex/Bison migration shape, generated reductions, AST, XML-like report |
| 10 | examples/csharp/* |
The same example set generated for C# with .g.cs output, reducer enums, .NET build/run checks, and mock reports |
| 11 | examples/c/* |
The same example set generated for C with conventional .h/.c files, reducer function pointers, and C-friendly reports/artifacts |
| 12 | examples/cpp/* |
The same example set generated for C++17 with enum class actions, ReducerMap, and static table lookup |
| 13 | examples/benchmarks |
Optional scanner/parser performance examples for throughput, allocation, source parsing, lexeme slices, reducer dispatch, and recovery overhead |
| 14 | testdata/ucdt |
Legacy split .l/.y inspiration fixtures and regression checks |
For starter-project guidance rather than a demo tour, read
Example Template Guide. It explains the
examples/templates folders, shared fixtures under examples/testdata, typed
reducer helpers, parser facade templates, and reusable fragments under
examples/mk.
See Learning Path for the guided route through these examples.
Benchmark examples live under
examples/benchmarks. They are not part of
make examples-test or make ci.
Run the Go benchmark suite:
make examples-benchmarks
make examples-benchmarks-go BENCH_COUNT=5 BENCH_TIME=2s
make examples-benchmarks-csharp CSHARP_BENCH_FILTER='*CalcParse*'
make examples-benchmarks-csharp CSHARP_BENCH_JOB=medium CSHARP_BENCH_FILTER='*CalcParse*'
make examples-benchmarks-report BENCH_COUNT=10 BENCH_TIME=1sThe Go suite uses go test -bench and -benchmem. The C# suite uses
BenchmarkDotNet with memory diagnostics and writes artifacts under
dist/benchmarks/csharp. Both use the same vocabulary:
ParseFromLexemeSource includes scanner/lexeme-source work, while
ParsePreTokenized parses tokens prepared before the timed loop.
The default path is quick mode. Use repeated Go runs through BENCH_COUNT=5
or 10, and use CSHARP_BENCH_JOB=medium or default, before drawing
before/after conclusions. BenchmarkDotNet Error can be large in short mode
because it intentionally uses few iterations.
The benchmark report target writes raw and summarized output under
dist/benchmarks, including go-benchmarks-summary.md and
csharp-benchmarks-summary.md. Summary paths are repository-relative where
practical so reports can be shared between local checkouts.
Static generated artifact metrics are written by
make examples-benchmarks-report as Markdown and JSON under
dist/benchmarks; they are not reported as timed benchmark rows.
C and C++ benchmark harnesses remain future optional work. Their examples already expose the same source/token and typed/boxed entry points, but target-specific timing harnesses should stay outside normal CI.
Curated copies of the Pascal UCDT calc input files live under:
Run:
go run ./cmd/lang-forge validate \
--lex testdata/ucdt/calc/calc.l \
--yacc testdata/ucdt/calc/calc.yThis fixture is used to keep split-file parsing and sample translation
honest while the .lf format evolves. It is not a
contract with UCDT.
Additional curated UCDT-derived fixtures live under testdata/ucdt/draw and testdata/ucdt/metas. See UCDT reference for the source role and validation evidence.
Small LR parser algorithm fixtures live under:
They demonstrate one grammar that validates under LALR(1), IELR(1), and canonical LR(1) but reports an expected conflict under SLR, plus another grammar where LALR reports an expected false merge conflict while IELR(1) and canonical LR(1) validate. Run:
make -C examples/parser-algorithms testSee Parser Algorithms for the detailed automata explanation, pseudo-code, and algorithm selection guidance.
The DataKeeper scripting demo lives under:
It reconstructs the small Irony-based DataKeeperScripting language as a real
LangForge example. The syntax is defined
in examples/go/datakeeper/datakeeper.lf,
the generated Go scanner/parser is created on demand under
examples/go/datakeeper/generated, and the reducer-backed semantic layer lowers
recognized scripts into stack-machine code.
The action labels in datakeeper.lf build an AST through handwritten adapter
code. LangForge recognizes the syntax and reports reductions; the example code
decides how those reductions become script statements and VM instructions.
datakeeper.lf -> generated scanner/parser reducer -> AST -> stack-machine instructions -> mock execution report
Run:
make -C examples/go/datakeeper runThe demo reads examples/go/datakeeper/sample.dks,
builds dist/datakeeper-demo, and writes a report to
examples/go/datakeeper/dist/datakeeper-demo.log. It does not call a real
database. It logs mocked RunSQL, AddObject, RemoveObject, and
RunObjectsJob adapter calls, plus the VM stack trace.
To use a standalone LangForge binary instead of running from source:
make build
make -C examples/go/calc LANG_FORGE=../../../dist/lang-forge run
make -C examples/go/datakeeper LANG_FORGE=../../../dist/lang-forge run
make -C examples/go/draw LANG_FORGE=../../../dist/lang-forge run
make -C examples/go/vehicle-report LANG_FORGE=../../../dist/lang-forge run
make -C examples/csharp/calc LANG_FORGE=../../../dist/lang-forge run
make -C examples/csharp/datakeeper LANG_FORGE=../../../dist/lang-forge run
make -C examples/csharp/draw LANG_FORGE=../../../dist/lang-forge run
make -C examples/csharp/vehicle-report LANG_FORGE=../../../dist/lang-forge run
make -C examples/c/calc LANG_FORGE=../../../dist/lang-forge run
make -C examples/c/datakeeper LANG_FORGE=../../../dist/lang-forge run
make -C examples/c/draw LANG_FORGE=../../../dist/lang-forge run
make -C examples/c/vehicle-report LANG_FORGE=../../../dist/lang-forge run
make -C examples/cpp/calc LANG_FORGE=../../../dist/lang-forge run
make -C examples/cpp/datakeeper LANG_FORGE=../../../dist/lang-forge run
make -C examples/cpp/draw LANG_FORGE=../../../dist/lang-forge run
make -C examples/cpp/vehicle-report LANG_FORGE=../../../dist/lang-forge runThe DRAW demo lives under:
It adapts the UCDT
SAMPLES/DRAW language idea as a LangForge example that renders real PNG
images. The syntax is defined in
examples/go/draw/draw.lf, the generated Go
scanner/parser is created on demand under examples/go/draw/generated, and the
reducer-backed semantic layer builds an AST before the interpreter handles
variables, math functions, reusable figure blocks, draw, repdraw, and
drawing primitives.
The DRAW adapter is handwritten source code. It consumes generated reductions,
builds drawing nodes, and leaves image rendering to ordinary Go package code
outside generated.
DRAW is also the larger typed-semantics example. Its four target specs share
labels such as width=Expr, target=FigureReference, and right=Term, plus
target-native nonterminal result declarations. Supported targets generate typed
contexts or adapters for DRAW actions; the handwritten adapters use named
fields directly or validate typed contexts before delegating to boxed reducers.
The Go dependency-only model package prevents an
import cycle between generated parser code and the application AST.
draw.lf -> generated scanner/parser reducer -> AST -> interpreter -> PNG
Run:
make -C examples/go/draw runThe demo reads examples/go/draw/sample.draw,
builds dist/draw-demo, renders examples/go/draw/dist/sample.png, and writes a
report to examples/go/draw/dist/draw-demo.log.
The sample language includes canvas and style commands:
canvas 960,640;
background #101820;
stroke #F2AA4C;
fill none;
width 2;
The vehicle report demo lives under:
It is inspired by a small Flex/Bison-style compiler exercise that parsed a
car = { ... } source file and printed XML-like output. The LangForge version
keeps that migration-friendly shape while using a combined .lf grammar,
generated Go scanner/parser output, and reducer-backed AST construction.
vehicle.lf -> generated scanner/parser reducer -> AST -> text/XML-like report
Run:
make -C examples/go/vehicle-report runThe demo reads
examples/go/vehicle-report/sample.vehicle,
builds dist/vehicle-report-demo, prints the report, and writes
examples/go/vehicle-report/dist/vehicle-report-demo.log.