The Myra Programming Language
C++ is always one line away.
A systems programming language in the Pascal/Oberon tradition.
Compiles to C++23 via Zig/Clang. Six targets from one source --
Windows, Linux, macOS, ARM64 and WebAssembly.
The language definition ships as readable .mld files
you can inspect, patch, and extend.
module exe hello; type Color = choices(Red, Green, Blue); Point = record x: int32; y: int32; end; routine greet(const name: string); begin println("Hello, {}!", name); end; var p: Point; begin @ifdef TARGET_WIN64 greet("Windows"); @else greet("Linux"); @endif p.x := 42; p.y := 99; println("({}, {})", p.x, p.y); end.
Core strengths
Oberon-inspired minimalism. Only the constructs that earn their place.
Compiles to real machine code via C++23. No VM, no garbage collection pauses. Decades of compiler optimisation work, for free.
#include any C++ header, use std::vector, call std::sqrt, mix new/delete with Myra's create/destroy. If it's not a Myra keyword, it's C++.
The language definition ships as readable .mld files. Fix a compiler bug. Add a keyword. Change code generation. The language is yours to shape.
Records with inheritance and bit fields. Objects with virtual dispatch. Overlays. Typed sets. Fixed and dynamic arrays. Pointer types. Routine types.
Windows, Linux, macOS, ARM64 and WebAssembly from the same file. Conditional compilation with @ifdef TARGET_WIN64. Zig handles every cross-compile; wasm builds run straight in the browser.
test "name" begin...end; blocks live in the same source file. Typed assertions (testAssertEqualInt, testAssertTrue, etc.) with file and line reporting.
Language tour
Every example below compiles and runs. No hand-waving.
// Overloading -- same name, different signatures routine max(const a: int32; const b: int32): int32; begin if a > b then return a; end; return b; end; routine 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 Point = record x: int32; y: int32; end; // Record inheritance Point3D = record(Point) z: int32; end; // Packed -- no padding between fields Header = record packed magic: uint32; version: uint16; flags: uint8; end; // Bit fields Flags = record packed active: uint8 : 1; mode: uint8 : 3; level: uint8 : 4; end; var p: Point3D; begin p.x := 10; p.y := 20; p.z := 30; println("({}, {}, {})", p.x, p.y, p.z); end.
type TAnimal = object Name_: string; method Init(AName: string); begin self.Name_ := AName; end; method Speak(); begin println("{} says: ...", self.Name_); end; end; TDog = object(TAnimal) // Override -- virtual dispatch method Speak(); begin println("{} says: Woof!", self.Name_); end; end; var dog: pointer to TDog; base: pointer to TAnimal; begin create(dog); dog.Init("Buddy"); // Dispatch through base pointer base := pointer to TAnimal(dog); base.Speak(); // calls TDog.Speak() destroy(dog); end.
routine getZero(): int32; begin return 0; end; var x: int32; begin // Software exception guard raiseexception("Something went wrong"); except println("Caught: {}", getexceptionmessage()); end; // With error code and finally guard raiseexceptioncode(42, "Custom error"); except println("Code: {}", getexceptioncode()); finally println("Always runs"); end; // Hardware exception (div-by-zero) guard x := 10 div getZero(); except println("HW exception: {}", getexceptionmessage()); end; end.
// User-defined variadic routine routine sumInts(...): int32; var i: int32; sum: int32; arg: int32; begin sum := 0; for i := 0 to varargs.count - 1 do arg := varargs.next(int32); sum := sum + arg; end; return sum; end; begin println("sum = {}", sumInts(10, 20, 30)); // 60 println("sum = {}", sumInts(1, 2, 3, 4, 5)); // 15 println("sum = {}", sumInts()); // 0 end.
var s1: set; s2: set; s3: set; begin s1 := [1, 3, 5]; // bit positions 1, 3, 5 s2 := [3, 5, 10]; // Set arithmetic s3 := s1 + s2; // union: [1,3,5,10] s3 := s1 * s2; // intersection: [3,5] s3 := s1 - s2; // difference: [1] // Membership test if 1 in s1 then println("1 is in s1"); end; // Range literal s1 := [1..5]; // bits 1 through 5 end.
module exe ffi_demo; // Per-target library paths -- TARGET_* is defined by the compiler @ifdef TARGET_LINUX64 @copydll "output/zig-out/lib/libmathlib.so"; @librarypath "output/zig-out/lib"; @elseif TARGET_WIN64 @librarypath "output/zig-out/bin"; @endif @linklibrary "mathlib"; // clink = C linkage (extern "C", unmangled). cpplink is the default. routine clink add(const a: int32; const b: int32): int32; external "mathlib"; routine clink quadruple(const x: int32): int32; external "mathlib"; // Variables can be imported too var version: int32; external "mathlib"; var r: int32; begin r := add(3, 5); println("add(3, 5) = {}", r); println("quadruple(4) = {}", quadruple(4)); println("lib version = {}", version); end.
module exe mixed; // C++ headers pass through verbatim #include <cmath> #include <string> #include <vector> var LS: std::string; LV: std::vector<int32_t>*; begin // C++ functions in Myra expressions println("sqrt(16) = {}", std::sqrt(16.0)); // C++ types as Myra variables LS := "hello from C++"; println("len = {}", LS.length()); // C++ new/delete alongside Myra LV := new std::vector<int32_t>(); LV->push_back(42); println("size = {}", LV->size()); delete LV; end.
Cross-platform
The same Myra source compiles to Windows, Linux, macOS, ARM64 and WebAssembly.
The compiler defines a TARGET_* symbol for the active target, so
@ifdef TARGET_WIN64 handles the differences. Zig performs every
cross-compile -- there is no second toolchain to install.
TARGET_* symbols injected automaticallyModule system
Every Myra source file is a module. The kind declaration determines the output artifact.
A program with a begin..end. entry point. Produces a native executable. The most common module kind.
Compiled to .lib (Windows) or .a (Linux). Use exported to expose symbols. Importable with import.
Compiled to .dll or .so. Export C-compatible APIs with exported routine clink. Ideal for plugins and FFI.
Vendor modules
The compiler knows about exactly one library path: the standard library. Everything else carries its own build configuration. Dropping in a new dependency requires zero compiler changes.
module lib raylib_import; // It owns every path it needs. // The langdef knows nothing about raylib. @includepath "res/libs/vendor/raylib/include"; @ifdef TARGET_WIN64 @librarypath "res/libs/vendor/raylib/win64/bin"; @copydll "res/libs/vendor/raylib/win64/bin/raylib.dll"; @elseif TARGET_LINUX64 @librarypath "res/libs/vendor/raylib/linux64/bin"; @endif @linklibrary "raylib"; #include "raylib.h" end.
module exe game; // Name where it lives, then import it. // That is the whole integration. @modulepath "res/libs/vendor/raylib"; import raylib_import; const CScreenWidth = 800; CScreenHeight = 450; begin InitWindow(CScreenWidth, CScreenHeight, "Raylib Window"); SetTargetFPS(60); while not WindowShouldClose() do BeginDrawing(); ClearBackground(RAYWHITE); EndDrawing(); end; CloseWindow(); end.
Media
Infographic, walkthroughs, and a deep dive into the compiler architecture.
"Start with Pascal. Remove everything that is not essential."
-- The Myra design principle