Skip to content

Latest commit

 

History

History
261 lines (203 loc) · 8.67 KB

File metadata and controls

261 lines (203 loc) · 8.67 KB

MiniLang – Reference Manual

MiniLang is a tiny, dynamically typed, C-style language that drives a full pipeline: lexer -> parser -> HIR -> MIR -> execution hosts. The solution contains an interpreter, a register-VM backend, a custom VM runtime with GC, shared host/tooling infrastructure in Compiler.Tooling, and an isolated Experimental/Typing area for incomplete type-system work. Program files use the extension .minl.

Repository Structure

  • Solution: Compiler.sln
  • Projects: Compiler.Frontend, Compiler.Frontend.Translation, Compiler.Backend.JIT.Abstractions, Compiler.Backend.VM, Compiler.Runtime.VM, Compiler.Interpreter, Compiler.Tooling, Compiler.Benchmarks, Compiler.Tests
  • Shared solution settings: Directory.Build.props, Directory.Packages.props

The repository currently targets .NET 10 (net10.0).

Build & Test

  • Build: dotnet build Compiler.sln -c Debug
  • Test: dotnet test Compiler.sln
  • Format: dotnet format

Run

The executable hosts use System.CommandLine and Microsoft.Extensions.Hosting.

  • Interpreter: dotnet run --project Compiler.Interpreter -- run --file Compiler.Tests/Tasks/factorial_calculation.minl
  • VM backend: dotnet run --project Compiler.Backend.VM -- run --file Compiler.Tests/Tasks/factorial_calculation.minl

Common options

  • -h|--help show help
  • -f|--file <path> path to .minl source file
  • -v|--verbose verbose logs (parse, MIR dump, return, timing)
  • --quiet suppress program stdout (builtins like print)
  • --time print total execution time (ms)

VM GC options

  • --gc-threshold=N initial VM heap collection threshold (objects)
  • --gc-growth=X threshold growth factor (e.g., 1.5)
  • --gc-auto=on|off enable/disable opportunistic collections
  • --gc-stats print VM GC statistics after execution

Backends

  • The default backend compiles MIR -> register bytecode and executes it on the VM.
  • The interpreter is a separate execution host used alongside the VM in regression and parity tests.

Benchmarks

  • Run benchmark harness: dotnet run --project Compiler.Benchmarks -c Release
  • Covered workloads: factorial, array sorting, prime number generation

Example GC stats output

[gc] mode=vm auto=on threshold=64 growth=1.5
[gc] allocations=128 collections=3 live=10 peak_live=70

1 · Lexical structure

Token Example(s) Notes
Identifiers foo, bar42 ASCII letters / digits / _, must not start with a digit
Integer lit. 0, -42, 123456 64-bit signed (long)
Char lit. 'a', '\n', '\\' Simple C-style escapes
String lit. "hi", "\"x\"" Back-slash escapes \" and \\
Keywords fn, var, if, else, while, for, break, continue, return, true, false
Operators `+ - * / % < <= > >= == != &&
Punctuation , ; ( ) { } [ ]
Comments // to end of line Discarded by the lexer

2 · Types & values (run-time)

Tag Literal(s) Description
long 42 64-bit signed integer
bool true Logical value
char 'x' 16-bit Unicode scalar
string "foo" Immutable UTF-16 sequence
array Mutable, zero-indexed VM array, elements default to null
null Absence of a value

3 · Expressions (precedence / associativity)

postfixExpr      calls & indexing      (left)
unary            +  -  !               (right)
multiplication   *  /  %               (left)
addition         +  -                  (left)
comparison       <  <= > >=            (left)
equality         == !=                 (left)
logicalAnd       &&                    (left, short-circuit)
logicalOr        ||                    (left, short-circuit)
assignment       =                     (right)  value of expr is RHS

Truthiness (used by if, while, …)

  • bool → itself
  • long → non‑zero
  • string → non‑empty
  • array → non‑empty
  • null → false

4 · Statements

var i = 0;                // variableDecl
if (cond) stmt            // ifStmt (+ optional else)
while (cond) stmt         // whileStmt
for (init; cond; iter) stmt
break; continue;          // loop control
return expr?;             // returnStmt
expr;                     // exprStmt (may be empty)
{ ... }                   // block

All variables are function-local; there are no globals.


5 · Functions

fn gcd(a, b) {
    while (b != 0) {
        var t = b;
        b = a % b;
        a = t;
    }
    return a;
}
  • No overloading; a single global namespace of functions.
  • If execution “falls off” the end, the function yields null.
  • Exactly one entry function main() with zero parameters is required.

6 · Arrays

var a = array(5);  // 5 nulls
a[0] = 123;
print(a[0]);       // 123

Indices are bounds‑checked; out‑of‑range access raises a runtime error.


7 · Built‑in functions

Name Arity Behaviour
array(n) 1 Fresh array of length n (all elements null).
array(n, init) 2 Fills array with init.
print(x, …) varargs Writes space‑separated values and a newline (to stdout)
len(x) 1 Length of string or array (returns long)
ord(c) 1 Code point of char or 1‑length string (returns long)
chr(i) 1 char for integer code point (range‑checked)
assert(cond, msg?) 1–2 Throws on false; optional message
clock_ms() 0 Monotonic timestamp in milliseconds as long

8 · Complete example programs

8.1 Factorial (recursive) – fact.minl

fn fact(n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
}

fn main() {
    print(fact(10));  // 3628800
}

8.2 Quick-sort – qsort.minl

fn qsort(arr, lo, hi) {
    if (lo >= hi) return;
    var p = arr[(lo + hi) / 2];
    var i = lo;
    var j = hi;
    while (i <= j) {
        while (arr[i] < p) i = i + 1;
        while (arr[j] > p) j = j - 1;
        if (i <= j) {
            var t = arr[i]; arr[i] = arr[j]; arr[j] = t;
            i = i + 1; j = j - 1;
        }
    }
    qsort(arr, lo, j);
    qsort(arr, i, hi);
}

fn main() {
    var a = array(10);
    var k = 0;
    while (k < 10) { a[k] = 9 - k; k = k + 1; }
    qsort(a, 0, 9);
    k = 0; while (k < 10) { print(a[k]); k = k + 1; }
}

8.3 Prime sieve – sieve.minl

fn sieve(limit) {
    var isPrime = array(limit + 1);
    var i = 2;
    while (i <= limit) { isPrime[i] = true; i = i + 1; }

    var p = 2;
    while (p * p <= limit) {
        if (isPrime[p]) {
            var j = p * p;
            while (j <= limit) { isPrime[j] = false; j = j + p; }
        }
        p = p + 1;
    }

    i = 2; while (i <= limit) { if (isPrime[i]) print(i); i = i + 1; }
}

fn main() { sieve(100); }