- Modula-2 87.8%
- Shell 6.3%
- C 4.6%
- AMPL 1.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| bindings/sqlite3 | ||
| bootstrap | ||
| compiler | ||
| docs | ||
| lib | ||
| runtime/syslib | ||
| tools | ||
| vendor | ||
| .gitignore | ||
| m2make.md | ||
| README.md | ||
| TUTORIAL.md | ||
m2compiler-V3 — modern Modula-2, Coco/R frontend, QBE backend
A from-scratch Modula-2 compiler in the Blaise spirit (phased,
test-guarded, self-hosting as the end goal): a Coco/R-generated
frontend in GNU Modula-2, a fresh QBE SSA backend, and a
length-prefixed runtime data model. Modern syntax follows the
Benjamin R10 specs where they don't break classic code
(Kowarsch/Redux cleanups: # not <>, NOT/AND/OR only, no
B/C octals, 0x hex, // comments).
Prerequisites
- GNU Modula-2 (
gm2 -fiso; developed with GCC 16.0.1 experimental) - Coco/R
CRV1.53 (CRBINenv var, defaults to the siblingCocoGm2/CRused here) qbe(default targetamd64_sysv; pin:vendor/qbe-pin.txt)cc,timeout, standard shell tools
Build and test
cd compiler
./build.sh # M2.atg -> scanner/parser/driver -> ./M2
./run_tests.sh # M2 -> gen_ssa/*.ssa -> qbe -> cc -> exit codes
cd ..
TMP="$PWD/.scratch/fixpoint" ./bootstrap/fixpoint.sh
TMP="$PWD/.scratch/fixpoint-separate" ./bootstrap/fixpoint-separate.sh
Pipeline per test: ./M2 tests/foo.mod writes
gen_ssa/FOO.ssa; a global VAR ExitCode : INTEGER selects the
process exit code (else 0). Reject tests assert on .LST messages;
every error also prints as file:line:col: message on stdout.
M2 itself exits 0 on success, 1 on compile errors and 2 on
usage/IO problems (no input, unopenable file) — no aborts.
Tutorials
TUTORIAL.md— a short hands-on tour: build the compiler, compile a one-shot program, split it into library/program objects, and select a dialect library folder.m2make.md/tools/m2make/README.md— the build-order tool and its--separatemode.docs/separate-compilation.md— object model, stable symbols, init order and current limits.bootstrap/fixpoint-separate.sh— per-object self-hosting gate.
Quick compile
One-shot (all units in one image):
cd compiler
./M2 tests/hello.mod # writes gen_ssa/Hello.ssa
qbe -o gen_ssa/Hello.s gen_ssa/Hello.ssa
cc gen_ssa/Hello.s ../runtime/syslib/shim.c -o /tmp/hello -lm
/tmp/hello
Separate objects (library + program; interfaces only at compile time):
# after building M2make (see TUTORIAL.md)
tools/m2make/b_v3/M2make --separate --dialect iso \
--m2bin ./compiler/M2 --shim runtime/syslib/shim.c \
-o /tmp/hello compiler/tests/hello.mod
/tmp/hello
M2 -c emits one unit's QBE IR; M2make --separate is the transitive
.def resolver and object linker. The one-shot mode remains the default
for the test suite and corpus. M2make accepts repeated --extra C
shims plus --pkg-config pkg and --lib lib for C library linking.
Documentation PDFs are generated with Pandoc:
make -C docs pdfs # pandoc + lualatex; needs DejaVu and Symbola fonts
Layout (Blaise-adapted)
compiler/src/ M2.atg, frames, FileIO, SymTab, QbeGen, Lower (+ generated)
compiler/tests/ t_*.mod (check/reject/run) + showcase<N>.mod tours
compiler/gen_ssa/ build outputs (gitignored)
runtime/syslib/ shared runtime (SysShim, FileIO, SysIO, …)
lib/{pim,iso,common,gm2}/ opt-in dialect libraries
tools/ syntax helpers + M2make (build-order tool) + corpus
bindings/ optional C bindings (first SQLite smoke test)
bootstrap/ stage1/2/3 + fixpoint.sh (self-hosting proof)
vendor/ qbe toolchain pin
docs/ STATUS.md, plan.md, plan-*.md, analysis_*.md, summary_*.md
Status (2026-10-28)
- Suite 333/333; self-hosting fixpoint OK (byte-identical
stage2/stage3, 3,671,466 bytes); per-object fixpoint OK
(11 objects); corpus 521/612 compile-OK, 0 crashes
(
tools/v3-corpus; the corpus is gm2's testsuite, so it mixes conforming Modula-2 with gm2 leniencies — seedocs/gaps/). The remainingexcept*/onebyte*-style corpus files still hit V3's declaration-order policy. - Active branch
master, feature statev3-address-arith-2026-10-28. Recent milestones:PACKEDSET, ISO exceptions (EXCEPT/RETRY,EXCEPTIONS,M2EXCEPTION, catchable language traps includingfunctionException, moduleFINALLY), self-referential pointer records/chained fields, and multi-dimensional fixed/open arrays (M1–M4): flat payloads + per-level views, open-array calls, and payloadSIZE/viewADR, flat multi-dimensional record fields and constructors, pointer-based 1-D open views, anLFLOATbuiltin, dialect library folders (lib/pim,lib/iso,lib/common,lib/gm2), separate compilation (M2 -cobjects,M2make --separate), ISO address arithmetic (ADDADR/SUBADR/DIFADR), module/local absolute variables with 64-bit addresses, 4 KiB-class string literals, and the broad SQLite binding underbindings/sqlite3.
Lowers today: program modules; CONST/VAR/TYPE (scalars,
subranges, enums, arrays, records incl. variants and self-referential
pointer records, sets incl. PACKEDSET, pointers); module and local
absolute variables (VAR x [0x...] : T); SYSTEM byte-address
arithmetic (ADDADR/SUBADR/DIFADR);
ARRAY/RECORD/SET/POINTER, WITH, IN; fixed and open
multi-dimensional arrays (view descriptors, row/subarray conversion,
payload SIZE/ADR); whole array/record copy; NEW/DISPOSE; all
scalar arithmetic, relations, boolean connectives; IF/CASE/WHILE/REPEAT/LOOP/FOR, GOTO/labels;
procedures with value/VAR params, functions, recursion, FORWARD,
nested procedures (static-link display), procedure values;
ISO exceptions (EXCEPT/RETRY, SYSTEM.THROW, EXCEPTIONS,
M2EXCEPTION, catchable index/range/CASE/function traps, module
FINALLY);
Clarion-form CLASS with methods, inheritance and virtual dispatch;
PIM coroutines (PROCESS/NEWPROCESS/TRANSFER/IOTRANSFER);
Unicode (UCHAR codepoints, UString, UTF-8 at the edges); ISO type
conversions T(x) (incl. CONST folding); separate compilation
(M2 -c per-unit objects, M2make --separate) and the one-shot
multi-file mode (lib.def lib.mod prog.mod → one image, FROM/IMPORT,
qualified L.x, opaque-type completion); generics — ISO 10514-2
refinement form (GENERIC … MODULE M(T:TYPE) + MODULE X = M(actuals))
with type/value/procedure parameters, constraints
(T : TYPE (ORDINAL|INTEGER|REAL|POINTER|SCALAR)), nested generics,
and generic classes.
Architecture: a two-phase parse — each unit's procedure bodies are
deferred and re-parsed at unit end, so forward references bind
normally and the old forward-reference machinery is gone. There is no
separate resolve/check pass; Lower is the only emitter and walks the
AST. -notwophase restores the legacy inline parse (debug escape).
Long-term rules already locked: every ARRAY is LONGCARD count
- elements (all arrays, inline, element counts, no NUL);
UCHAR= 32-bit codepoint,UString= count + codepoints, UTF-8 at the edges with hard lex errors; self-hosting bar is byte-identical.ssaover three stages.
Working conventions
- Grammar grows section by section in
compiler/src/M2.atg(kept LL(1)-clean — Coco/R must report no errors). - Every step keeps the full suite green; per-session record in
docs/summary_*.md;showcase<N>.modtours each stage. Seedocs/INDEX.mdfor the load-bearing docs. - Tags
v3-*mark green states; no pushes without asking (seegit.txtfor the remote recipe).