Myrissa reads like Pascal, compiles like C, and ships as a standalone binary with nothing else to install.
// A complete program in Myrissa module exe hello; @optimize debug; routine greet(const name: string; const times: int32); var i: int32; begin for i := 1 to times do println("Hello, %s! (%d)", name, i); end; end; begin greet("Myrissa", 3); end.
$ myrc -s hello.myr -r
Hello, Myrissa! (1)
Hello, Myrissa! (2)
Hello, Myrissa! (3)
Every example below is current Myrissa syntax, checked against the language reference.
// Overloading -- cpplink enables C++ name mangling routine cpplink max(const a: int32; const b: int32): int32; begin if a > b then return a; end; return b; end; routine cpplink max(const a: float64; const b: float64): float64; begin if a > b then return a; end; return b; end; // var parameter -- modified in place routine swap(var a: int32; var b: int32); var tmp: int32; begin tmp := a; a := b; b := tmp; end; // Recursion routine fib(const n: int32): int32; begin if n <= 1 then return n; end; return fib(n - 1) + fib(n - 2); end;
type Shape = record x: int32; y: int32; end; // Record inheritance Circle = record(Shape) radius: float32; end; // Packed -- no padding between fields Header = record packed magic: uint16; version: uint8; flags: uint8; end; // Bit fields Flags = record packed visible: uint8 : 1; enabled: uint8 : 1; priority: uint8 : 3; reserved: uint8 : 3; end; Color = record r: uint8; g: uint8; b: uint8; end; // Record literal var red: Color = Color(r: 255, g: 0, b: 0);
type TBase = object x: int32; method describe(): int32; begin return self.x * 10; end; end; TDerived = object(TBase) y: int32; // Override -- call up with parent method describe(): int32; begin return parent.describe() + self.y; end; end; var d: pointer to TDerived; begin create(d); d.x := 7; d.y := 3; println("describe: %d", d.describe()); // 73 destroy(d); end.
begin // Throw and catch guard throw(42); except println("caught code %d", exccode()); end; // Code, message, and finally guard throwcode(7, "custom error"); except println("%d: %s", exccode(), excmsg()); finally println("finally runs always"); end; println("continues after guard"); end.
// User-defined variadic routine routine sum_ints(...): int32; var i: int32; total: int32; begin total := 0; for i := 0 to varargs.count - 1 do total := total + varargs.next(int32); end; return total; end; // Indexed access -- read arg by position without advancing routine show_args(...); var i: int32; begin for i := 0 to varargs.count - 1 do println("arg[%d] = %d", i, varargs.get(i, int32)); end; varargs.reset(); // rewind cursor to start end; begin println("sum = %d", sum_ints(10, 20, 30)); // 60 show_args(1, 2, 3); end.
module exe mathlib; @unittestmode on; routine add(const a: int32; const b: int32): int32; begin return a + b; end; end. // Test blocks live after end. -- the compiler swaps in the test runner test "add returns correct sum" var result: int32; begin result := add(2, 3); asserteq(5, result); end; test "add handles negatives" begin asserteq(-2, add(-5, 3)); asserteq(-8, add(-5, -3)); end;
// Value-based branching match value of 1: println("one"); 2: println("two"); 3..5: println("three to five"); else println("other"); end; // Multiple values per arm match ch of "a", "e", "i", "o", "u": println("vowel"); else println("consonant"); end;
// Fixed-size array var numbers: array[10] of int32; numbers[0] := 42; numbers[9] := 100; // Explicit range bounds var grid: array[0..7] of int32; // Dynamic array -- no bounds at declaration var items: array of int32; setlength(items, 10); items[0] := 42; println("len = %d", len(items)); // 10
type TColor = choices(Red = 0, Green = 1, Blue = 2); TDirection = choices(North, South, East, West); var c: TColor; begin c := TColor.Green; println("%d", int32(c)); // 1 match int32(c) of 0: println("red"); 1: println("green"); 2: println("blue"); end; end.
type PInt32 = pointer to int32; var x: int32 = 42; p: PInt32; begin p := address of x; println("via pointer: %d", p^); // 42 p^ := 100; println("x is now: %d", x); // 100 // Const pointers prevent writes type PConstInt = pointer to const int32; end.
// First-class routine types (function pointers) type TCompareFunc = routine(const a: int32; const b: int32): int32; routine ascending(const a: int32; const b: int32): int32; begin return a - b; end; var cmp: TCompareFunc; begin cmp := ascending; println("cmp(3,7) = %d", cmp(3, 7)); // -4 end.
// Conditional directives take no terminator @define VERBOSE @ifdef VERBOSE println("Debug: entering main loop"); @endif // TARGET_* symbols come from @target win64|linux64; @ifdef TARGET_WIN64 println("Running on 64-bit Windows"); @elseif TARGET_LINUX64 println("Running on 64-bit Linux"); @endif // DEBUG is defined at @optimize none; RELEASE otherwise. // Conditionals also work inside imported units, // evaluated with the root module's defines.
The full compiler pipeline -- lexer, parser, optimizer, code generator, linker -- runs in one invocation.
win64 or linux64 from the same source via @target. PE for Windows, ELF for Linux -- both cross-compiled from one Windows host..dll/.so), or static library (.lib/.a). The module declaration drives the output.Myrissa.dll and give your application native-code compilation at runtime. C/C++ and Delphi/Free Pascal bindings included.test "name" begin ... end; blocks with typed assertions. The compiler swaps in the test runner as the entry point automatically.choices for enumerations, set with the in operator, and overlay for C-style unions -- including anonymous overlays nested in records.setlength and len. Dynamic arrays are automatically cleaned up.routine type, assign any matching routine, and call it by variable. C calling convention by default.The CImporter turns a C header into a binding unit that works on both targets. The generated unit selects the right native library per target and declares every routine against a single module-level constant.
module unit RayLib; @ifdef TARGET_WIN64 @copydll "res/libs/vendor/raylib/win64/raylib.dll"; @elseif TARGET_LINUX64 @copydll "res/libs/vendor/raylib/linux64/libraylib.so.550"; @else @message error "RayLib: unsupported target"; @endif public const DLL_NAME: string = "raylib"; public routine InitWindow(const width: int32; const height: int32; const title: pointer); external DLL_NAME;
module exe game; @libpath "res/libs/vendor/raylib"; @libpath "res/libs/vendor/raylib/linux64"; import RayLib; begin RayLib.InitWindow(800, 450, "Myrissa + raylib"); RayLib.SetTargetFPS(60); while not RayLib.WindowShouldClose() do RayLib.BeginDrawing(); RayLib.ClearBackground(RayLib.RAYWHITE); RayLib.DrawText("Hello from Myrissa!", 280, 200, 20, RayLib.DARKGREEN); RayLib.EndDrawing(); end; RayLib.CloseWindow(); end.
On win64 the extensionless DLL_NAME resolves to raylib.dll; on linux64 the library search paths are probed for libraylib.so.<version> and the found file becomes the runtime dependency, loaded from beside the executable. @copydll places the correct native library next to your binary at build time. Same source, same binding -- two platforms.
The same Myrissa source compiles to Windows and Linux. The
@target win64|linux64; directive selects the platform, and the
compiler defines a TARGET_* symbol for the active target, so
@ifdef TARGET_WIN64 handles the differences. Everything runs on a
single Windows host -- Linux binaries are cross-compiled with no Linux
toolchain installed.
TARGET_* symbols injected automaticallyThe output type is determined by the module declaration, not by CLI flags.
Whether you are building a game, a tool, or just want to understand how compilers work.
Infographic, walkthroughs, and a deep dive into the compiler architecture.
No SDK, no package manager, no setup wizard.