"Your ZX Spectrum is now an enterprise-grade ABAP runtime."
MinZ compiles ABAP to native Z80 machine code via the HIR/MIR2 pipeline. The parser is powered by abaplint by Lars Hvam Petersen — a full ABAP parser written in TypeScript.
ABAP source → abaplint (Node.js) → JSON AST → Go lowerer → HIR → MIR2 → Z80
That's right: your REPORT program goes through 7 compilation stages to become Z80 opcodes.
| File | What it does | ABAP features |
|---|---|---|
| hello.abap | Hello world | DATA, WRITE |
| fibonacci.abap | Fibonacci sequence | WHILE, arithmetic |
| fizzbuzz.abap | FizzBuzz | IF/ELSEIF/ELSE, MOD |
| guessing_game.abap | Number guessing | DO..TIMES, CASE/WHEN, EXIT |
| bubble_sort.abap | Bubble sort | Nested WHILE, CASE, swap pattern |
| forms.abap | Subroutines | FORM, PERFORM, USING/CHANGING |
| oop.abap | OOP shapes | CLASS, INTERFACE, METHOD, inheritance |
| sysinfo.abap | System report | Multiple WRITE, string output |
| sqlite_demo.abap | Database ops | FORM, PERFORM, string params |
The ABAP parser is now embedded as a Wasm module (14MB). A standard go build produces a single binary (~36MB) that can parse and compile ABAP without Node.js or any external tooling.
Architecture: @abaplint/core (TypeScript) → esbuild (bundle) → Javy/QuickJS → .wasm → go:embed → wazero (pure-Go Wasm runtime)
The compiler detects the embedded Wasm blob at startup. If present, it uses wazero to execute the parser in-process. No network, no npm, no Node.js needed.
QBE native compilation also works — ABAP programs compile through the full pipeline to native AMD64 binaries: ABAP → HIR → MIR2 → QBE IL → qbe → cc → executable.
Only needed if you want to update @abaplint/core or modify the parser bridge:
cd minzc/pkg/abap/bridge
npm install # downloads @abaplint/core
npx esbuild parse.mjs --bundle --outfile=parse.bundle.js --platform=neutral
javy compile parse.bundle.js -o parse.wasm
cp parse.wasm ../parse.wasm # go:embed picks this up on next buildIf you prefer the Node.js path or want faster iteration on the bridge script:
cd minzc/pkg/abap/bridge
npm installThe compiler falls back to Node.js (parse.mjs) when the Wasm blob is not embedded.
| Tool | Version | Why |
|---|---|---|
| Go | 1.24+ | Compiler is written in Go |
| Node.js | 18+ | Optional — only for rebuilding the Wasm blob or using the Node.js fallback |
| npm | 9+ | Optional — same as above |
# Build the MinZ compiler (Wasm parser is embedded — no npm needed)
cd minzc
make build # produces ./mz binary with embedded ABAP parser# Should print "PASS" — parses ABAP and lowers to HIR
go test ./pkg/abap/ -v -run TestCompileToHIR# Compile ABAP to Z80 assembly
./mz ../examples/abap/hello.abap -o hello.a80
# Emit HIR to see the intermediate representation
./mz ../examples/abap/fibonacci.abap --emit=hir
# Emit MIR2 to see the optimized IR
./mz ../examples/abap/fizzbuzz.abap --emit=mir2
# Full pipeline to CP/M binary (if target supports it)
./mz ../examples/abap/hello.abap -o hello.com -t cpmREPORT— program declarationDATA— variable declaration withTYPEandVALUEWRITE— console output (maps to CP/M BDOS)IF / ELSEIF / ELSE / ENDIF— conditionalsWHILE / ENDWHILE— pre-condition loopsDO [n TIMES] / ENDDO— counted and infinite loopsCASE / WHEN / ENDCASE— multi-branch dispatchFORM / ENDFORM— subroutinesPERFORM— subroutine calls withUSING/CHANGINGEXIT/CONTINUE— loop control- Arithmetic:
+,-,*,/,MOD - Comparisons:
=,<>,<,>,<=,>=,EQ,NE,LT,GT,LE,GE - Logical operators:
AND,OR
CLASS / ENDCLASS— OOP with zero-cost dispatchINTERFACE / ENDINTERFACE— structural interfaces (no vtables!)METHOD / ENDMETHOD— instance methodsCREATE OBJECT— object instantiationINHERITING FROM— single inheritance
SELECT→ SQLite via MZV host functions (prototype working! seeexamples/nanz/sqlite_demo.nanz)- Open SQL → SQLite transpilation (later: client-server protocol over Z80 I/O ports)
CALL FUNCTION— no RFC on a SpectrumALV— the CRT is the gridSAP GUI— 256x192 pixels is all you getABAP Debugger— usemzv -traceinstead
Because we can. And because the idea of an SAP consultant debugging ABAP on a ZX Spectrum is objectively hilarious.
Also: it validates that MinZ's HIR pipeline is truly language-agnostic. If we can compile ABAP to Z80, we can compile anything to Z80.
The ABAP frontend lives in minzc/pkg/abap/ with three layers:
- Bridge (
bridge/parse.mjs) — calls@abaplint/coreto parse ABAP into a structured AST, emits JSON - Parser (
parse.go) — Go code that invokes Node.js, deserializes JSON, builds semanticProgram - Lowerer (
lower.go) — converts semantic ABAP constructs to HIR nodes
The lowerer maps ABAP concepts to Z80-friendly HIR:
DATA lv_x TYPE i VALUE 42→mir2.Global{Name: "lv_x", Ty: TyU16, Init: [42, 0]}WRITE lv_x→hir.CallExpr{Fn: "abap_write", Args: [VarRef("lv_x")]}FORM name USING p1→hir.Func{Name: "name", Params: [{Name: "p1", Ty: TyU16}]}IF cond ... ENDIF→hir.IfStmt{Cond: ..., Then: ..., Else: ...}CLASS lcl_foo→mir2.StructTy{Name: "lcl_foo"}+ method functions
abaplint bridge: ... is Node.js installed?
- Install Node.js 18+ (
brew install node/apt install nodejs) - Run
cd minzc/pkg/abap/bridge && npm install
no ABAP file parsed
- abaplint requires
REPORT zname.as the first statement for programs - The bridge auto-detects file type from source:
REPORT→.prog.abap,CLASS→.clas.abap
unsupported: SomeStatement
- Not all 392 ABAP statement types are lowered yet — the parser handles them fine (abaplint is complete), but the Go lowerer only maps Phase 1+2 constructs to HIR
- Want to add one? Look at
minzc/pkg/abap/lower.go— each statement type is acaseinlowerStmt()
Slow first compile
- First run downloads/compiles
@abaplint/corevia npm — subsequent runs are fast (~400ms) - The bridge is a cold
nodeprocess per compile; caching/daemon mode is planned
abaplint by Lars Hvam Petersen is a standalone ABAP linter and parser written in TypeScript. It:
- Parses all ABAP syntax (7.02–7.57) into a 3-layer AST: Token → Statement → Structure
- Supports 392 statement types, 100+ expression types
- Runs without an SAP system — pure static analysis
- MIT licensed, no external dependencies
- Also powers @abaplint/transpiler (ABAP → JavaScript)
We use @abaplint/core as an npm library. The bridge script (parse.mjs, ~80 lines) feeds source code to abaplint, walks the AST, and emits JSON that Go can deserialize. abaplint handles all the ABAP parsing complexity — we just consume structured nodes.
ABAP: Advanced Business Application Programming. Z80: Advanced Business Application Processor (retroactively).