- Modula-2 73.4%
- HTML 22.8%
- Shell 3.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
- README.md: overview, build/test instructions, language status, error-code table, layout, related projects. - tutorial.md: verified walk-through with ten compile-and-run examples (incl. separate compilation units) and gotchas. |
||
| docs | ||
| MC4 | ||
| src | ||
| tests | ||
| build.sh | ||
| FileIO.o | ||
| gnu-m2-grammar.txt | ||
| img1.png | ||
| M2c | ||
| M2c-git instructions.txt | ||
| M2c.o | ||
| M2cP.o | ||
| M2cS.o | ||
| MGen.o | ||
| README.md | ||
| run_tests.sh | ||
| skills.html | ||
| skills.jpeg | ||
| skills.md | ||
| skills.pdf | ||
| skills.png | ||
| SymTab.o | ||
| tutorial.md | ||
M2c — a Modula-2 compiler for the MC64 virtual machine
A small, self-contained Modula-2 compiler written in Modula-2 (built with
GNU Modula-2, gm2 -fiso). The front end is generated by
Coco/R from an attributed
grammar; the back end emits a runnable MC64 bytecode image (.MC4) that
runs on the mcint interpreter.
This is the "V1" of a family of experimental compilers (V1 → V2 → V3). It was
developed in numbered, test-guarded steps, each recorded in docs/. As of
step 11 the suite is 158/158 green (99 accept/run, 65 rejection).
How it works
src/M2c.atg --Coco/R (CR)--> src/M2cS.mod + M2cP.mod + M2c.mod
| |
(scanner) (parser + semantic actions)
\ /
\ gm2 -fiso
v
./M2c (native compiler)
|
parse + type-check + codegen (MGen)
v
<Module>.MC4 --> mcint
src/M2c.atg— the whole language: tokens, productions, and the semantic actions that drive the symbol table and the code generator.src/M2cS.mod/src/M2cP.mod/src/M2c.mod— generated by Coco/R on every build. Do not edit them: edit their sources (src/compiler.frm,src/scanner.frm,src/parser.frm) and the grammar.src/SymTab— scopes, type descriptors, and all static type checking.src/MGen— the MC64 emitter (buffers an image in memory and writes it).src/FileIO— the FileIO/Storage I/O library reused from Coco/R.
The driver runs one session over several files: definition files, then
implementation files, then exactly one program module; SymTab.Init and
MGen.OpenModule run once, and the program unit names the image.
Prerequisites
- GNU Modula-2 (
gm2 -fiso), developed with GCC 16.0.1 experimental. - Coco/R
CR(the generator). Path is taken fromCRBIN; defaults to the siblingCocoGm2/CR. mcint, the MC64 interpreter, for running the produced images. Taken fromMCINT; defaults to the sibling../m-code-64/mcint.- Standard shell tools (
sh,grep,sed,tr).
Build
From the project root:
./build.sh
This regenerates the scanner/parser/driver from src/M2c.atg, recompiles the
hand-written modules, and links ./M2c. Object files (.o) stay in the
project root; the generated sources live in src/.
Compiling a program
./M2c tests/t_arith.mod # parse/type-check + codegen
mcint TArith.MC4 # run the image
-
A
.LSTlisting (with error messages) is written next to each source, and the driver printsParsed correctlyorIncorrect sourceon stdout. -
Multi-file (separate compilation) sessions pass the units in dependency order; the last file must be the program module:
./M2c tests/d_lib.def tests/d_libimpl.mod tests/d_basic.mod mcint DBasic.MC4 -
The image name comes from the program module, so the output is
<Module>.MC4in the current directory.
Test convention (no I/O in the language)
There is no text I/O in the language subset, so programs signal their result through a global variable. If the source declares
VAR ExitCode : INTEGER;
the epilogue prints its value as decimal + CRLF through an embedded print helper; without it the program simply ends.
Testing
After building, and with mcint available:
./run_tests.sh
The runner compiles each test with ./M2c, runs the resulting .MC4 under
mcint, and compares the printed ExitCode; rejection tests assert the exact
message text in the .LST. Run tests:
- accept/parse —
expect_ok - compile + run —
expect_run/expect_run_none - multi-file run —
expect_run_files - rejections —
expect_fail/expect_fail_files
158/158 green at step 11.
Layout
src/ M2c.atg (grammar), *.frm (frames), hand modules, generated
tests/ *.mod test cases + expected .LST listings
MC4/ compiled .MC4 images (test outputs)
docs/ per-step summaries (summary_stepN.md) and goal notes
build.sh regenerate + compile + link -> ./M2c
run_tests.sh compile -> mcint -> compare
Root-level *.o and ./M2c are build artifacts. Shared reference material
lives at the root as well: skills.md (a Modula-2 language reference),
gnu-m2-grammar.txt, and the Showcase*.mod tours in src/.
Language status
Lowered today (steps 1–11):
- Program modules;
CONST/TYPE/VARincludingINTEGER,CARDINAL, subranges,REAL,CHAR,BOOLEAN, enumerations. - Composites: fixed and open
ARRAYs,RECORDs,SETs (union+, difference-, intersection*, inclusive..ranges,IN),POINTERs (NEW/DISPOSE,^,NIL), and strings (ARRAY OF CHARliteral assignment and comparison). - Full scalar and composite expressions; whole array/record copy.
- Statements: assignment, procedure call,
IF,CASE,WHILE,REPEAT,LOOP/EXIT,FOR,WITH(nested),RETURN,NEW,DISPOSE. - Procedures: nested, value and
VARparameters, functions, recursion,FORWARDheadings,HIGHon open arrays. - Local
MODULEs (EXPORTed constants, types, variables and procedures; optionalBEGINinit bodies run at startup in declaration order). - Separate compilation units:
DEFINITION MODULE,IMPLEMENTATION MODULE,IMPORT/FROM ... IMPORT, qualifiedM.x, compiled into a single image.
Parsed and type-checked, but rejected with error 230 (deferred): open
arrays beyond the supported shapes, whole array/record copy except string
literals, value composite params/returns, non-literal CONST expressions and
BY steps, EXIT outside LOOP, imported names used as values, procedure
types/variables, LONGINT/LONGCARD, LONGREAL, and opaque types.
Known semantic edges
INTEGERDIV/MODtruncate toward zero;CARDINALpastMAXINTcompares as signed;AND/ORare eager.REALis binary64;INTEGERwidens toREAL.- Array indexing is unchecked (no
DA/DB);NILdereference reads 0 (no static check, no VM trap yet). CHAR/BOOLEANarrays are byte-packed (1 byte/element, slots over-allocated 8×).- Phased caps: 64 procedures, 64 actuals per call, 16 names per FP-section,
8-deep nested calls, 8-deep
WITH, 1024 slots max per type; 8 definitions, 32 interface names each, 64FROM-aliases; one image (depCount0).
Error codes
Static semantic errors use the following codes (the listing prints the message, not the number):
| code | message |
|---|---|
| 200 | duplicate identifier |
| 201 | undeclared identifier |
| 202 | module/procedure name mismatch |
| 210 | incompatible assignment |
| 211 | arithmetic operand must be numeric |
| 212 | boolean operand required |
| 213 | incompatible comparison |
| 214 | BOOLEAN condition required |
| 215 | not a RECORD type |
| 216 | unknown field |
| 217 | not an ARRAY type |
| 218 | array index must be integer |
| 219 | not a POINTER type |
| 220 | FOR needs integer variable and bounds |
| 221 | not a type name |
| 222 | set operand mismatch |
| 223 | cyclical type definition |
| 224 | ordinal type required |
| 230 | not supported in this phase |
| 231 | procedure forward mismatch or missing body |
| 232 | bad RETURN |
| 233 | invalid procedure call |
Documentation
docs/ contains a summary per development step (summary_step1.md …
summary_step11.md, plus summary_step11.1.md for the real-comparison
codegen fix) and the forward-looking goal_step4.md / goal_step5.md. Read
src/M2c.atg's header comment for the most current feature/limits list.
Related projects
Siblings in MyWork/:
m-code-64— the MC64 virtual machine (mcint, loader, assembler) and its specification (docs/mc64-spec.md); the execution target ofM2c.CocoGm2— the GNU-Modula-2 port of Coco/R (CR) and its frames, used to generate this compiler's scanner/parser/driver.m2compiler-V2,m2compiler-V3— later, more ambitious generations of the same experiment.
Working conventions
- Grammar grows section by section in
src/M2c.atg, kept LL(1)-clean. - Every step keeps the full suite green; each step is recorded in
docs/summary_stepN.mdand tagged (step1…step11). *.modgenerated by Coco/R are never edited by hand: fix the.frm/.atgsources and rebuild.