|
|
@@ -0,0 +1,267 @@
|
|
|
+# M2comp — 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; a tree-walking back end emits a runnable **MC64** bytecode image
|
|
|
+(`.MC4`) that runs on the `mcint` interpreter.
|
|
|
+
|
|
|
+This is the "V2" of a family of experimental compilers (V1 → V2 → V3). Unlike
|
|
|
+V1, which emits code in a single pass while parsing, **V2 follows the Blaise
|
|
|
+front/back split**: the front end builds a complete checked **abstract syntax
|
|
|
+tree** (per Mössenböck's Coco/R recipe), and the back end (`MGen`) walks that
|
|
|
+tree to produce the image. V2 was developed in numbered, test-guarded steps;
|
|
|
+as of **step 7** the suite is **65/65 green**.
|
|
|
+
|
|
|
+## How it works
|
|
|
+
|
|
|
+```text
|
|
|
+src/M2comp.atg --Coco/R (CR)--> src/M2compS.mod + M2compP.mod + M2comp.mod
|
|
|
+ | |
|
|
|
+ (scanner) (parser + semantic actions,
|
|
|
+ SymTab checks, AST build)
|
|
|
+ |
|
|
|
+ AST
|
|
|
+ |
|
|
|
+ MGen.EmitModule
|
|
|
+ (tree walk)
|
|
|
+ v
|
|
|
+ <Module>.MC4 --> mcint
|
|
|
+```
|
|
|
+
|
|
|
+- **`src/M2comp.atg`** — the whole language: tokens, productions, and the
|
|
|
+ semantic actions that drive the symbol table and build the AST.
|
|
|
+- **`src/M2compS.mod` / `src/M2compP.mod` / `src/M2comp.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, static type checking
|
|
|
+ (errors 200–224, 230–233), procedure signatures, exports, loop depth.
|
|
|
+- **`src/AST`** — heap-allocated tree of declarations/statements/expressions
|
|
|
+ per the Mössenböck "How to Build Abstract Syntax Trees with Coco/R" recipe;
|
|
|
+ error-tolerant so dumps exist for rejected sources too.
|
|
|
+- **`src/MGen`** — the MC64 backend: walks the checked AST, writes
|
|
|
+ `<Module>.MC4`, and prints the step-7 disassembly listing (`DumpImage`).
|
|
|
+- **`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 (program last). The
|
|
|
+symbol table and the emit buffer live for the whole session; each
|
|
|
+implementation is merged into its definition unit, and the *program* module
|
|
|
+names the image. `SymTab.Init`/`MGen` run once; up to 15 library units per
|
|
|
+session.
|
|
|
+
|
|
|
+## 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`, `timeout`).
|
|
|
+
|
|
|
+## Build
|
|
|
+
|
|
|
+From the project root:
|
|
|
+
|
|
|
+```sh
|
|
|
+./build.sh
|
|
|
+```
|
|
|
+
|
|
|
+This runs Coco/R on `src/M2comp.atg` (which reports **"Compilation completed.
|
|
|
+No errors detected."**), recompiles `FileIO`, `SymTab`, `AST`, `MGen` and the
|
|
|
+generated modules with `gm2 -fiso`, and links `./M2comp`. Object files (`.o`)
|
|
|
+and the generated sources stay in `src/`.
|
|
|
+
|
|
|
+## Compiling a program
|
|
|
+
|
|
|
+```sh
|
|
|
+./M2comp tests/r_flow.mod # parse + type-check + AST + codegen
|
|
|
+../m-code-64/mcint RFlow.MC4 # run the image
|
|
|
+```
|
|
|
+
|
|
|
+For every unit the driver prints `Parsing <file>`, the symbol table and the
|
|
|
+`--- AST ---` tree on stdout. After a successful session it prints the
|
|
|
+step-7 backend listing `--- Code <Module> ---` (global layout per scope, the
|
|
|
+procedure table, then the disassembled image) followed by the verdict
|
|
|
+`Parsed correctly` — or `Incorrect source` on any error. A `.LST` listing
|
|
|
+(with `***** ^ message` markers) is written next to each source file.
|
|
|
+
|
|
|
+- Multi-file (separate compilation) sessions pass the units in dependency
|
|
|
+ order — definitions, then implementations, then the program:
|
|
|
+
|
|
|
+ ```sh
|
|
|
+ ./M2comp tests/d_lib.def tests/d_lib.mod tests/d_basic.mod
|
|
|
+ ../m-code-64/mcint DBasic.MC4
|
|
|
+ ```
|
|
|
+
|
|
|
+- The image name comes from the program module, so the output is always
|
|
|
+ `<Module>.MC4` in the current directory. A session without a program unit
|
|
|
+ is rejected (`Incorrect source`).
|
|
|
+
|
|
|
+### Test convention (no I/O in the language)
|
|
|
+
|
|
|
+The V2 language subset has no text I/O statements (no `WriteString` /
|
|
|
+`WriteInt` as in V1), 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 `./M2comp`, runs the resulting `.MC4` under
|
|
|
+`mcint`, and compares the printed `ExitCode`; it also asserts AST-dump node
|
|
|
+names and backend-listing opcodes for representative sources. Helpers:
|
|
|
+
|
|
|
+- **accept** — `expect_ok` / `expect_run` / `expect_run_none` /
|
|
|
+ `expect_run_files` (multi-file sessions)
|
|
|
+- **AST dump** — `expect_dump`
|
|
|
+- **rejections** — `expect_fail` / `expect_fail_files` /
|
|
|
+ `expect_fail_multi` (program-last gate)
|
|
|
+- **backend listing** — `expect_code` (e.g. `realCmp`, `jpfalse`,
|
|
|
+ `storeIndir0`, the proc-table entry)
|
|
|
+
|
|
|
+Emitted images are collected into `MC4/`. **65/65 green** at step 7
|
|
|
+(61 inherited + 4 listing checks).
|
|
|
+
|
|
|
+## Layout
|
|
|
+
|
|
|
+```text
|
|
|
+src/ M2comp.atg (grammar), *.frm (frames), FileIO/SymTab/AST/MGen,
|
|
|
+ generated M2compS/M2compP/M2comp and *.o
|
|
|
+tests/ *.mod test cases + expected .LST listings
|
|
|
+MC4/ collected .MC4 images (test outputs)
|
|
|
+docs/ per-step summaries (summary_m2comp_stepN.md), goal_step8.md
|
|
|
+ roadmap, session notes, AST.pdf, oberon-master/, skills.*
|
|
|
+build.sh regenerate + compile + link -> ./M2comp
|
|
|
+run_tests.sh compile -> mcint -> compare
|
|
|
+gnu-m2-grammar.txt GNU Modula-2 EBNF reference
|
|
|
+```
|
|
|
+
|
|
|
+`src/*.o`, the generated `.mod` sources and `./M2comp` are build artifacts
|
|
|
+(regenerated by `./build.sh`).
|
|
|
+
|
|
|
+## Language status
|
|
|
+
|
|
|
+**Lowered today** (steps 1–7):
|
|
|
+
|
|
|
+- Program modules; `CONST`/`TYPE`/`VAR` with the predefined `INTEGER`
|
|
|
+ family (`CARDINAL`, `SHORTINT`, `LONGINT`), subranges (`[lo .. hi]`,
|
|
|
+ `INTEGER[lo .. N]`), `REAL`/`LONGREAL`, `CHAR`, `BOOLEAN`.
|
|
|
+- Composites: fixed `ARRAY`s (multi-dimensional, whole-array copy),
|
|
|
+ `RECORD`s (whole-record copy, `WITH`), `SET OF <ordinal>` (assignment,
|
|
|
+ `IN`, `=`, `#`), `POINTER TO` (assignment, `NIL`, `^`), and **strings**
|
|
|
+ (`ARRAY OF CHAR` literal assignment, indexing, `CHAR` literals).
|
|
|
+- Full expressions: `+ - * / DIV MOD AND OR NOT`, all six relations plus
|
|
|
+ `<>=` aliases (`#` = `<>`), `IN`; `INTEGER` widens to `REAL`; `REAL` is
|
|
|
+ binary64 with E-notation literals.
|
|
|
+- Statements: assignment, procedure call, `IF`/`ELSIF`/`ELSE`, `CASE`
|
|
|
+ (label lists, ranges, `ELSE`, `CHAR` selectors), `WHILE`, `REPEAT`,
|
|
|
+ `LOOP`/`EXIT`, `FOR` (runtime bounds, `BY`, downward), `WITH` (nested),
|
|
|
+ `RETURN` (proper check of value/variable procedures).
|
|
|
+- Procedures: nested, value and `VAR` parameters, functions, recursion.
|
|
|
+- **Local `MODULE`s** (Wirth form, optional priority `[n]`, `EXPORT`
|
|
|
+ [`QUALIFIED`], optional module init bodies that run at startup).
|
|
|
+- **Separate compilation units**: `DEFINITION MODULE`, `IMPLEMENTATION
|
|
|
+ MODULE`, `IMPORT` / `FROM ... IMPORT`, qualified `M.x`, compiled into a
|
|
|
+ single image; an implementation's `BEGIN` init body runs first.
|
|
|
+
|
|
|
+**Parsed and type-checked, but rejected with error 230** (deferred): value
|
|
|
+composite parameters/returns, open arrays outside `VAR` formals, non-literal
|
|
|
+`CONST` expressions and subrange bounds, real/string `CONST` in definitions,
|
|
|
+`EXIT` outside `LOOP`, non-foldable `CASE` labels, imported names used as
|
|
|
+values, plus — not in the grammar yet — enumeration types, set literals
|
|
|
+`{..}`, `NEW`/`DISPOSE`, procedure types/variables, and standard procedures
|
|
|
+(`HIGH`, `INC`, `DEC`, …). Opaque types are the next milestone
|
|
|
+(`docs/goal_step8.md`).
|
|
|
+
|
|
|
+### Known semantic edges
|
|
|
+
|
|
|
+- `INTEGER` `DIV`/`MOD` truncate toward zero; `AND`/`OR` are eager.
|
|
|
+- `REAL` is binary64; `INTEGER` widens to `REAL`.
|
|
|
+- Array indexing is unchecked, and `NIL` dereference reads 0 (no static
|
|
|
+ check, no VM trap yet).
|
|
|
+- Composites are stored slot-per-element (no byte packing) — a documented
|
|
|
+ deviation from m2c; `CHAR`/`BOOLEAN` elements still take one slot.
|
|
|
+- A single-character literal (`"h"`, `'b'`) is a `CHAR`, not a string.
|
|
|
+- In `DEFINITION` units, formal parameter names live in the definition
|
|
|
+ scope: reusing a parameter name in a *second* heading is a duplicate
|
|
|
+ identifier (error 200).
|
|
|
+- `<>` is accepted as an alias of `#`.
|
|
|
+- Phased caps: 256 symbols, 8-deep call stack, 7 array dimensions, 15
|
|
|
+ library units per session, one image per program.
|
|
|
+
|
|
|
+## 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_m2comp_step1.md` …
|
|
|
+`summary_m2comp_step7.md`), the forward-looking `goal_step8.md` roadmap
|
|
|
+(opaque types, procedure types, quad-driven backend), `session_steps1-4.md`
|
|
|
+(a day-wrap note with hard-won facts), and reference material (`AST.pdf`,
|
|
|
+`oberon-master/`, `skills.*`, `BenjaminKowarsh.txt`, `blaise-process.md`).
|
|
|
+Read `src/M2comp.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 M2comp.
|
|
|
+- **`CocoGm2`** — the GNU-Modula-2 port of Coco/R (`CR`) and its frames,
|
|
|
+ used to generate this compiler's scanner/parser/driver.
|
|
|
+- **`m2compiler-V1`**, **`m2compiler-V3`** — the earlier and later
|
|
|
+ generations of the same experiment (V1 emits in one pass; V3 is the
|
|
|
+ larger successor).
|
|
|
+
|
|
|
+## Working conventions
|
|
|
+
|
|
|
+- Grammar grows section by section in `src/M2comp.atg`, kept LL(1)-clean and
|
|
|
+ confirmable by Coco/R (**"No errors detected."**).
|
|
|
+- Front/back split: the front end builds the AST, the back end walks it;
|
|
|
+ op-code groups use disjoint ranges so the tree-walk dispatches on the code
|
|
|
+ alone.
|
|
|
+- Every step keeps the full suite green and is recorded in
|
|
|
+ `docs/summary_m2comp_stepN.md`, tagged (`m2comp-step1` … `m2comp-step7`,
|
|
|
+ plus `m2comp-day1`, `m2comp-docs`, `m2comp-goal8`).
|
|
|
+- `*.mod` generated by Coco/R are never edited by hand: fix the `.frm`/`.atg`
|
|
|
+ sources and rebuild.
|