Write source. Get native Win64 and Linux x64 binaries.
Zero dependencies -- AOT compiled -- Pascal/Oberon inspired
.myr
Lexer
Parser
IR/SSA
x64
PE / ELF
binary
Under active development
win64 · linux64
Clean syntax, native output

Myrissa reads like Pascal, compiles like C, and ships as a standalone binary with nothing else to install.

hello.myr
// 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.
Terminal
$ myrc -s hello.myr -r

Hello, Myrissa! (1)
Hello, Myrissa! (2)
Hello, Myrissa! (3)
The language, in code

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.
Everything built in. Nothing to install.

The full compiler pipeline -- lexer, parser, optimizer, code generator, linker -- runs in one invocation.

Zero Dependencies
No MSVC, no MinGW, no GCC, no external linker, no runtime. One tool produces standalone native binaries.
🌍
Cross-Platform Targets
Build for win64 or linux64 from the same source via @target. PE for Windows, ELF for Linux -- both cross-compiled from one Windows host.
Native x64 Output
Ahead-of-time compiled to x86-64 machine code. No interpreter, no VM, no bytecode layer. The output runs bare metal.
📦
Multiple Output Kinds
Compile the same source to executable, dynamic library (.dll/.so), or static library (.lib/.a). The module declaration drives the output.
🔗
CImporter
Parse C headers into Myrissa bindings. One generated binding unit selects the right native library per target and serves both win64 and linux64.
🐞
Built-in Debugger
Debug Adapter Protocol support provides breakpoints, stepping, call stacks, and variable inspection in VS Code and other DAP editors.
🧠
Language Server
LSP integration delivers diagnostics, completion, hover, go-to-definition, references, rename, and formatting in your editor.
🔌
Embeddable API
Ship Myrissa.dll and give your application native-code compilation at runtime. C/C++ and Delphi/Free Pascal bindings included.
Built-in Unit Testing
test "name" begin ... end; blocks with typed assertions. The compiler swaps in the test runner as the entry point automatically.
📊
Choices, Sets, and Overlays
choices for enumerations, set with the in operator, and overlay for C-style unions -- including anonymous overlays nested in records.
📋
Arrays and Dynamic Arrays
Fixed-size arrays with explicit bounds, plus managed dynamic arrays with setlength and len. Dynamic arrays are automatically cleaned up.
Routine Types
First-class function pointers: declare a routine type, assign any matching routine, and call it by variable. C calling convention by default.
One binding, every platform

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.

RayLib.myr — the generated binding
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;
game.myr — the consumer
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.

One source. Multiple targets.

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.

  • Identical language semantics across both targets
  • TARGET_* symbols injected automatically
  • PE output for Windows, ELF output for Linux
  • Cross-compiled from one Windows host -- no second toolchain
  • One vendor binding serves both targets
win64 -- Windows x86-64 Native PE
linux64 -- Linux x86-64 Native ELF
Four kinds. One declaration.

The output type is determined by the module declaration, not by CLI flags.

exe
Executable
win64 name.exe
linux64 name
A program with a begin..end. entry point. Native executable.
dll
Dynamic Library
win64 name.dll
linux64 name.so
Shared library with exported routines. Ideal for plugins and FFI.
lib
Static Library
win64 name.lib
linux64 name.a
Static library, linkable by Myrissa or other compilers.
unit
Unit
output (none)
Reusable module compiled inline into the importing module.
Built for people who ship native code

Whether you are building a game, a tool, or just want to understand how compilers work.

Game Developers
Scripting-language convenience with native compilation. Import C libraries like raylib and SDL via the built-in CImporter -- one generated binding serves both Windows and Linux. Pairs naturally with the PIXELS 2D engine.
Tool Builders
Embed Myrissa.dll and give your application native-code compilation at runtime. The flat C API covers compiler, debugger, CImporter, LSP, and test runner. C/C++ and Delphi/Free Pascal bindings included.
Language Enthusiasts
Study a complete native compiler stack from parsing and SSA IR through register allocation and PE/ELF linking -- all in one codebase with no third-party dependencies.
Windows and Linux Developers
Produce standalone native binaries for either platform without shipping .NET, JVM, Python, or a pile of runtime libraries alongside your application.
See Myrissa in action

Infographic, walkthroughs, and a deep dive into the compiler architecture.

Myrissa Infographic
🎵
Deep Dive -- Compiler Architecture
Use the expand button to view full size
Three steps to native code

No SDK, no package manager, no setup wizard.

01
Write a source file
// hello.myr module exe hello; begin println("Hello!"); end.
02
Compile and run
$ myrc -s hello.myr -r Hello! // Cross-compile for Linux: // add @target linux64;
03
Ship it
$ dir output\ hello.exe win64 hello linux64 // That's it. No runtime.