# 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](https://ssw.jku.at/Research/Projects/Coco/) 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 ```text 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 .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 from `CRBIN`; defaults to the sibling `CocoGm2/CR`. - **`mcint`**, the MC64 interpreter, for running the produced images. Taken from `MCINT`; defaults to the sibling `../m-code-64/mcint`. - Standard shell tools (`sh`, `grep`, `sed`, `tr`). ## Build From the project root: ```sh ./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 ```sh ./M2c tests/t_arith.mod # parse/type-check + codegen mcint TArith.MC4 # run the image ``` - A `.LST` listing (with error messages) is written next to each source, and the driver prints `Parsed correctly` or `Incorrect source` on stdout. - Multi-file (separate compilation) sessions pass the units in dependency order; the *last* file must be the program module: ```sh ./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 `.MC4` in 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 ```modula2 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: ```sh ./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 ```text 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`/`VAR` including `INTEGER`, `CARDINAL`, subranges, `REAL`, `CHAR`, `BOOLEAN`, enumerations. - Composites: fixed and **open** `ARRAY`s, `RECORD`s, `SET`s (union `+`, difference `-`, intersection `*`, inclusive `..` ranges, `IN`), `POINTER`s (`NEW`/`DISPOSE`, `^`, `NIL`), and **strings** (`ARRAY OF CHAR` literal 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 `VAR` parameters, functions, recursion, `FORWARD` headings, `HIGH` on open arrays. - **Local `MODULE`s** (`EXPORT`ed constants, types, variables and procedures; optional `BEGIN` init bodies run at startup in declaration order). - **Separate compilation units**: `DEFINITION MODULE`, `IMPLEMENTATION MODULE`, `IMPORT`/`FROM ... IMPORT`, qualified `M.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 - `INTEGER` `DIV`/`MOD` truncate toward zero; `CARDINAL` past `MAXINT` compares as signed; `AND`/`OR` are eager. - `REAL` is binary64; `INTEGER` widens to `REAL`. - Array indexing is unchecked (no `DA`/`DB`); `NIL` dereference reads 0 (no static check, no VM trap yet). - `CHAR`/`BOOLEAN` arrays 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, 64 `FROM`-aliases; one image (`depCount` 0). ## 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 of `M2c`. - **`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.md` and tagged (`step1` … `step11`). - `*.mod` generated by Coco/R are never edited by hand: fix the `.frm`/`.atg` sources and rebuild.