The project is a simplified Modula-2-like compiler implemented in GNU Modula-2.
The language is intentionally smaller than full Modula-2:
INTEGER and CHAR typesThe objective is to have a real compiler pipeline rather than only a parser.
The compiler follows this pipeline:
MiniM2 source
|
v
+-----------+
| Lexer |
+-----------+
|
v
+-----------+
| Parser |
+-----------+
|
v
+----------------+
| Symbol table |
| Type checking |
+----------------+
|
v
+----------------+
| Code generator |
+----------------+
|
v
Generated GNU Modula-2
|
v
+-----------+
| gm2 |
+-----------+
|
v
Native executable
The compiler itself is written in GNU Modula-2.
Coco/R has traditionally provided compiler-generator targets such as C#, Java and C++, but GNU Modula-2 is not a standard Coco/R target.
Instead of trying to make Coco/R generate GNU Modula-2 code, the compiler can be implemented directly in GNU Modula-2.
The first native backend is source-to-source:
gm2.This is already a genuine compiler architecture: the front-end performs lexical analysis, parsing and semantic checking, while GNU Modula-2 acts as the native backend.
GNU Modula-2 is provided by GCC.
Typical compilation is:
gm2 -g hello.mod
GNU Modula-2 uses .mod files for implementation/program modules and .def files for definition modules.
The generated program can therefore be compiled by the GNU Modula-2 compiler.
The generated project is:
mini-modula2-real-compiler/
├── MiniM2C.mod
├── Makefile
├── README.md
└── examples/
├── hello.mod
└── bad.mod
MiniM2C.mod contains the compiler implementation.
examples/hello.mod is a valid example program.
examples/bad.mod is intended to demonstrate semantic/type errors.
From the project directory:
make
The compiler executable is intended to be:
minim2c
A MiniM2 source file can then be compiled with:
./minim2c examples/hello.mod
The compiler generates a GNU Modula-2 source file:
examples/hello.mod.gen.mod
and then invokes gm2 to build the native executable.
The exact executable name can depend on the generated module and GNU Modula-2 toolchain.
The intended workflow is:
cd mini-modula2-real-compiler
make
./minim2c examples/hello.mod
./mini-program
If the generated executable has a different name, use the executable name reported by the compiler or inspect the generated build output.
The lexer converts source characters into tokens.
Typical token categories include:
Examples of keywords include:
MODULE
BEGIN
END
VAR
CONST
INTEGER
CHAR
READ
WRITE
IF
THEN
ELSE
WHILE
DO
The lexer is responsible for recognizing these tokens before parsing.
The parser consumes the token stream and verifies that the program follows the MiniM2 grammar.
A simplified structure is:
Module
= MODULE identifier ;
declarations
BEGIN
statements
END identifier .
Expressions are parsed according to precedence.
For example:
a + b * c
must be interpreted as:
a + (b * c)
rather than:
(a + b) * c
The symbol table records declared identifiers.
For each identifier the compiler can keep information such as:
name
kind
type
For example:
x : INTEGER
letter : CHAR
When an identifier is used, the compiler looks it up in the symbol table.
This allows the compiler to detect errors such as:
unknown variable
duplicate declaration
The semantic phase checks that expressions and statements use compatible types.
Examples:
INTEGER + INTEGER
is valid.
But:
INTEGER + CHAR
is rejected by the type checker.
Assignments are also checked.
For example:
VAR
x : INTEGER;
c : CHAR;
Then:
x := 10;
is valid, while:
x := c;
should produce a type error.
The current backend generates GNU Modula-2 source.
For example, an internal MiniM2 construct such as:
x := 10;
can be emitted as GNU Modula-2:
x := 10;
The generated source is then compiled by gm2.
This approach makes the compiler relatively small while still producing native executables.
The first implementation is deliberately simple.
In particular, the initial WRITE implementation supports only simple operands such as an identifier or literal rather than every possible expression.
For example, this may be supported:
WRITE(x);
while something such as:
WRITE(x + 1);
requires an expression-aware WRITE code-generation path.
That is one of the natural next improvements.
The compiler should report lexical, syntactic and semantic errors.
Examples:
unexpected token
expected identifier
undeclared identifier
duplicate declaration
type mismatch
A useful future improvement is to preserve exact source line and column information for every token and report errors like:
example.mod:12:7: type mismatch
A compiler does not have to directly emit machine instructions itself.
A common architecture is:
source
-> front-end
-> intermediate representation / generated source
-> backend compiler
-> object code
-> executable
In this project, GNU Modula-2 is the backend.
The compiler therefore performs real compilation work:
A stronger version can evolve toward:
Instead of immediately generating code while parsing, build an Abstract Syntax Tree:
Module
├── Declarations
└── Statements
├── Assignment
├── If
├── While
└── Write
The AST can then be converted into an IR.
For example:
LOAD_CONST 10
STORE x
or:
LOAD x
LOAD 1
ADD
STORE x
The IR can then be translated to:
Possible additions include:
BOOLEANFORIFWHILEThe compiler and the Theia Modula-2 extension can eventually be integrated.
Theia can provide:
The compiler can provide:
A future language-server integration could expose compiler diagnostics directly in the editor.
The Theia extension has a separate editor feature for automatic Modula-2 keyword uppercasing.
The required behavior is:
Only the keyword currently being typed is converted to uppercase.
It should not uppercase unrelated existing text in the document.
For example, when typing:
mod
the editor can turn the current keyword into:
MOD
without modifying other words.
The Theia extension can also provide Modula-2 snippets.
Typical snippets can accelerate constructs such as:
MODULE ...;
BEGIN
...
END ... .
or declarations and control structures.
Snippets are normally triggered through the editor's completion/snippet mechanism.
The project was generated as a GNU Modula-2 compiler prototype.
It should not be described as production-ready without compiling and testing it with an installed GNU Modula-2 (gm2) toolchain.
The development environment used to prepare the project did not have gm2 available, so the generated compiler was not actually built and executed there.
Before treating it as a finished compiler, install GNU Modula-2 and run:
gm2 --version
make
./minim2c examples/hello.mod
Then test both successful programs and intentionally invalid programs.
The most useful next improvement is to make the compiler robust enough to compile its complete test suite under GNU Modula-2.
After that, the compiler can be upgraded from the simple source-to-source backend to:
Lexer
-> Parser
-> AST
-> Symbol table
-> Type checker
-> IR
-> Code generator
-> Native executable
This provides a cleaner foundation for adding the full set of desired Modula-2 features.