- Modula-2 95.4%
- Shell 3.6%
- AMPL 1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| gm2-port | ||
| selfhost | ||
| src | ||
| .gitignore | ||
| ERRLIST.md | ||
| README.md | ||
Lilith single-pass Modula-2 compiler (GNU Modula-2 port)
Wirth/Gutknecht one-pass Modula-2 compiler for Lilith (1983–86, M3PL
banner ETHZ M3L NW 2.8.86). This port builds and runs under Linux and
emits the same 16-bit M-code .OBJ files that
../Interpreter executes — no Lilith hardware needed.
Background: src/singlepasscompiler.txt (Wirth 1.5.84 / rev. 15.11.85) and
src/M2SinglePass.pdf. Original sources live in src/ (M2S, M3DL,
M3TL, M3RL, M3GL, M3PL, plus M2.atg, M2SP.COM and the
original decode.mod); the Linux port lives in gm2-port/ (see
gm2-port/PORT-SUMMARY.md for every porting change).
Build
cd gm2-port && ./build.sh # produces ./m3pl and ./decode (gm2 -fpim)
build.sh compiles the six modules separately and links via a module
list (direct linking fails with duplicate main, one per module with
initialization — same flow as CocoGm2).
Use
The compiler reads one source file per in> line and derives all output
names from the module name, not the file name. Work in an empty
directory; Medos filenames traditionally start with DK.:
mkdir -p /tmp/opencode/hello && cd /tmp/opencode/hello
cat > DK.HELLO.MOD <<'EOF'
MODULE HELLO; VAR x: CARDINAL; BEGIN x := 1 END HELLO.
EOF
printf 'HELLO.\n\033' | /path/to/m3pl
ETHZ M3L NW 2.8.86
in> HELLO.MOD
+ DK.HELLO.RFC
+ DK.HELLO.OBJ 11
in>
- Type the module name without prefix/extension (
HELLO.— the trailing dot triggers the default.MOD); ESC (or empty input / host EOF) quits thein>loop. Piped input works. - Batch mode: command-line arguments skip the prompt —
m3pl HELLO TWOcompiles both.HELLO,HELLO.,HELLO.MODandDK.HELLO.MODall nameDK.HELLO.MOD; another extension is used as-is.--version/--helpprint the banner/usage. 11is informational (code size). Errors go toDK.err.LST/DK.err.DATwith an error number; on failure the output files are deleted (errors detected). Every number is documented inERRLIST.md.- Append
/rto a name (FOO./r) to suppress range-check code for that unit (rngchk := FALSE).
Outputs per unit:
| source | outputs |
|---|---|
DEFINITION MODULE |
DK.<name>.SBL (symbol file + 3-word key) |
IMPLEMENTATION / program module |
DK.<name>.OBJ (M-code) + DK.<name>.RFC (debugger reference) |
Definitions must be compiled before their users: the compiler reads the
imported .SBL files (InRef) and records their 3-word keys in the
.OBJ; the interpreter re-checks those keys at load (import key mismatch = stale .OBJ, recompile the imported module first).
Multi-module example:
printf 'FOO.\nFOOIMP.\nBAR.\n\033' | /path/to/m3pl
printf 'DK.BAR.OBJ\n\033' | /path/to/mcode
Module map
| module | role |
|---|---|
M2S |
scanner: GetSym, identifiers/numbers, error marking (DK.err.LST) |
M3DL |
base structures (ObjPtr, StrPtr, WordSize = 16) + private heap |
M3TL |
symbol table / scopes (Find, NewObj, NewScope, InitTableHandler) |
M3RL |
symbol/reference file I/O (reads .SBL, writes .SBL/.RFC) |
M3GL |
code generator: expression Items → M-code bytes (PutOp*, GenCall, …) |
M3PL |
recursive-descent parser + in> main loop (one pass, no IR) |
gm2-port/decode.mod (built as gm2-port/decode) disassembles an .OBJ to a .DEX
text file (header/data/entries/code + imports/relocation); it is the
reference for what the compiler emitted when the interpreter's /T
trace disagrees with expectations. It also reads the multipass .o
(16-bit big-endian) to a .DEX, and a multipass symbol file (.sy) to a
grouped .SYD: module header and key, imports/exports, then
const/type/var/proc sections with each type rendered
(enumeration, subrange, pointer, set, array, record with CASE
variants, procedure type), e.g.
type
Structrec = RECORD size : CARDINAL; ...; CASE Structform OF 0 (size 6) | ... END END
proc
Compare #2 (ARRAY OF CHAR; ARRAY OF CHAR) : INTEGER
Coroutines
SYSTEM also provides PROCESS (a word-sized handle type) and the
standard procedures NEWPROCESS/TRANSFER (numbers 21/22, free slots
in the standard table): NEWPROCESS(P, a, s, VAR q) emits CLX 0 2 and
TRANSFER(old, new) emits TRA <mask>, exactly the encodings the
multipass C18 uses, so mcode runs both. A coroutine's workspace is a
plain array; its address and SIZE are passed to NEWPROCESS.
M3TL registers the type and procedures (in both gm2-port/, the host
build, and src/, the self-hosted one) and M3GL generates the code.
Interpreter/tests/DK.COR.MOD interleaves two routines (1 2 3 4 5 6)
and is part of make test; Interpreter/examples/showcase runs the same
larger program through this compiler and through mcc, and make showcase checks that both print identical output.
Dialect notes (1983 revisions, pre-existing — not port bugs)
DEFINITION MODULEs take noEXPORTclause (error 38); everything declared is exported.TRUNCreturnsCARDINAL—TRUNCof a negative aborts at runtime (the compiler emitsCHKS).- Unary minus / operators on
LONGREALconstants are rejected at compile time (error 112, originalGenNeglimitation) — use variables. - Single-character string literals (
"7") type asCHAR; pad short patterns ("07") where a string is needed. - Real literals must fit IEEE-754 single range (else error 207); code generation assumes 16-bit words throughout.
Longer worked examples (libraries, I/O, tracing, test suites) are in
../Interpreter/TUTORIAL-LANGUAGE.md; the .OBJ
container layout the interpreter accepts is documented in
../Interpreter/README.md.
Self-hosting (selfhost/)
selfhost/build-self.sh compiles the original src/ sources with
m3pl itself (host-interface stubs DK.FileSystem/DK.Terminal/
DK.Heap/DK.Clock.MOD stand in for Lilith-only modules at type-check
time; SYSTEM needs no stub — the compiler declares it builtin). Two
mechanical patches are applied to the copies (never to src/):
M3DL.Size (CODE 247B is Lilith-firmware magic — even the reference
emulator crashes on SYS — so RETURN current - last, identical to its
Available twin) and five lines in M3GL (16-byte names, 7-word
imports, Real MIN/MAX literals, LSB-first MOD mask). Result: all
six DK.M*.OBJ, which run under mcode (Heap/Clock/FileSystem
synthesis, traps 70–81) and compile to byte-identical OBJs modulo
timestamp keys. selfhost/test-self.sh replays T1/T3/T4/TS/
MSK/MODT/PV/HFS plus two negative tests through the
self-compiled compiler as a regression test.
Known limits
- Generated code is Lilith M-code; only the compiler itself runs on
Linux.
Wfields are written as 4-byteCARDINALs, so.OBJs are readable by this port'sdecode/interpreter only, not by original Lilith tools. M3DL.ResetHeapis a no-op underStorage(leaks per-scope memory; fine for small compiles).- Sources are PIM dialect (
-fpim);-fisoadditionally wants emptyELSEs on pre-existing non-exhaustiveCASEs, no logic change.