Myra is C++23. You just write it in Pascal syntax.
Under the hood, Myra compiles to C++ 23 and uses Zig as the build system. That means you get everything C++ 23 gives you: the full standard library, every platform target Zig/clang supports, every optimization the compiler can produce, without writing a line of C++. You write clean, structured Pascal-style code. Myra handles the rest.
module exe hello;
begin
writeln("Hello from Myra! 🚀");
end.
When you need to drop to raw C++, to call a C++ API directly, include a header, or write a helper that C++ expresses better, you can do that inline, without leaving your source file. cppstart header / cppstart source / cppend inject raw C++ into the generated output. cpp("expression") embeds a C++ expression at any call site. It is not an escape hatch. It is a designed feature. The standard library itself uses it.
Myra takes its syntax philosophy from Oberon: start with Pascal and remove everything that is not essential. What remains is clean, readable, and unambiguous. begin..end blocks, := assignment, strong static typing, and a module system that replaces header files entirely. No cruft, no legacy baggage. Just the parts of Pascal that were always right.
The entire toolchain ships in the box. Zig, clang, the C++ runtime, the standard library, the LSP server, the debugger adapter: everything needed to go from source to native binary is included in the release. There is nothing to install, configure, or set up. You unzip, add bin\ to your PATH, and write code.
Myra is for developers who want native performance and low-level control without fighting the language. If you are building any of the following, Myra is worth a look:
- Systems software: Write low-level code with full pointer arithmetic, packed structs, union types, and direct memory management. Myra does not hide the machine from you.
- Game engines and tools: Call C libraries (SDL3, raylib, etc.) directly via FFI with no boilerplate. Target Windows and Linux from the same codebase. Shared library interop is a first-class feature, not an afterthought.
- DLL / shared library development: Export clean C-linkage APIs from a Myra
dllorlibmodule and consume them from any language that speaks C ABI. - Cross-platform CLI tools: Compile once, run on Windows and Linux64. WSL2 integration means you can build and test Linux binaries without leaving Windows.
- Embedded tooling: Small, predictable binaries with configurable optimization levels (
releasesmall,releasefast,releasesafe). The Zig backend produces tight output. - Learning systems programming: Pascal-family syntax is famously readable and explicit. Myra adds modern ideas while keeping the code approachable for people coming from high-level languages.
Myra is a complete language for systems-level development. These are the capabilities that ship today:
- 🔧 Pascal-family syntax: Clean, readable, case-insensitive.
begin..endblocks,:=assignment, strong typing throughout. Familiar to Pascal and Delphi developers; readable even to those who are not. - 🎯 Native binaries: Compiles to real x86-64 executables, DLLs, and static libraries. No VM, no bytecode, no interpreter. The output runs bare metal.
- 🌐 Cross-platform: Target Windows (Win64) or Linux (Linux64) from the same source. Cross-compile from Windows via WSL2 with no additional configuration.
- 🔗 FFI / C interop: Call any C library with
externaldeclarations and"C"linkage. Full varargs support forprintf-style APIs. Perfect ABI compatibility — structs, unions, anonymous unions, and bit fields map directly to their C equivalents with no manual adjustment. - 🔄 C Header Importer:
TMyraCImporterconverts C headers into ready-to-use Myra modules automatically. Point it at a.hfile and it preprocesses with Zig/Clang, parses all declarations, and emits a complete.myrabinding with the correct types, signatures, and cross-platform directives — no manual tweaking required. - 📦 Module system: Three module kinds:
exe(executable),dll(shared library),lib(static library). A cleanimportmechanism wires them together without header files. - 🧬 Rich type system: Records with inheritance and field alignment, classes with methods and virtual dispatch, unions, enums, sets, fixed and dynamic arrays, typed and untyped pointers, routine types, and bit fields.
⚠️ Structured exception handling:try/except/finallywith full hardware exception support for divide-by-zero, access violations, and other CPU-level faults.- 🔄 Routine overloading: Multiple routines with the same name resolved by parameter signature. The C++ backend handles name mangling transparently.
- 📊 Sets: Pascal-style bit-set types backed by a 64-bit integer. Full set arithmetic: membership (
in), union (+), intersection (*), and difference (-). - 📝 Managed strings: Reference-counted UTF-8
stringand UTF-16wstringtypes with automatic lifecycle management. Emoji, CJK, and accented characters work without any special handling. - 💾 Full memory control:
new/disposefor class instances,getmem/freememfor raw allocation,resizememfor reallocation. You decide what lives where. - 🔢 Variadic routines: Define your own variadic routines with
.... Access argument count and values viavarargs.countandvarargs.next(T). - 🏷️ Version info and icons: Embed metadata and application icons into Windows executables via directives. No post-build steps or resource compilers required.
- 🔀 Conditional compilation:
$ifdef/$ifndef/$elseif/$else/$endifwith predefined platform symbols. Write platform-specific code in the same file cleanly. - 🛠️ Built-in CLI: The
myracommand scaffolds new projects, builds, runs, and cleans with a single command. No Makefiles, no CMake, no build configuration required for the common case. - 🔌 Language Server Protocol: A full LSP server ships with Myra and works with any LSP-compatible editor. Run it out-of-process as
MyraLSP.exeor embed it in-process via theTMyraLSPServerclass. - 🐛 Integrated debugger: DAP-compatible debugger integration via LLDB. The
$breakpointdirective records source locations into a.breakpointsfile that the debugger loads automatically. An interactive debug REPL is included for command-line debugging. - ✅ Built-in test blocks:
test "name" begin ... end;blocks for inline unit tests, compiled and run as part of the test suite with colour-coded pass/fail output.
Every Myra program is a module. The module kind (exe, dll, or lib) is declared at the top of the file and determines what artifact gets built. An executable module has a begin..end. body that serves as the program entry point. There is no main() to wire up, no boilerplate to write.
Here is a complete program that demonstrates cross-platform conditional compilation and the writeln format string syntax:
module exe hello;
//$target win64
//$target linux64
begin
$ifdef TARGET_WIN64
writeln("Hello from Myra, running on WIN64");
$elseif TARGET_LINUX64
writeln("Hello from Myra, running on LINUX64");
$else
writeln("Hello from Myra, running on UNKNOWN");
$endif
writeln("Name: {}, Age: {}", "Jarrod", 42);
writeln("Pi is approximately {:.4f}", 3.14159);
writeln("Hex: 0x{:X}, Octal: {:o}", 255, 255);
writeln("Hello 🌍 World!");
end.
The writeln statement accepts a format string with {} placeholders that are matched left-to-right to the arguments that follow. Standard format specifiers are supported inside the braces: {:.4f} for fixed-point precision, {:X} for uppercase hex, {:o} for octal, and more. The write statement works identically but does not append a newline.
The $target directive (commented out above) lets you lock a source file to a specific platform. When left commented, the compiler target is controlled at the command line or from the build configuration. The TARGET_WIN64 and TARGET_LINUX64 symbols are injected automatically based on the active target, so you can write portable code that branches cleanly at compile time.
Routines are the basic unit of abstraction in Myra. They are declared with the routine keyword and can return a value using return. Parameters are passed by value (as const) by default. Use var to pass by reference when you need the routine to modify the caller's variable. Routines can be overloaded, meaning multiple routines can share the same name as long as their parameter signatures differ.
Local type, const, and var sections can appear inside a routine body before the begin block, keeping definitions scoped to where they are needed.
module exe routines;
routine add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
// Routine overloading - same name, different types
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;
// Recursion
routine fib(const n: int32): int32;
begin
if n <= 1 then return n; end;
return fib(n - 1) + fib(n - 2);
end;
// var parameter - modified in place
routine inc(var x: int32);
begin
x := x + 1;
end;
var
x: int32;
begin
writeln("add(3, 4) = {}", add(3, 4));
writeln("max int = {}", max(3, 7));
writeln("max float = {}", max(3.5, 2.8));
writeln("fib(10) = {}", fib(10));
x := 10;
inc(x);
writeln("after inc: x = {}", x);
end.
Records are value types. They live on the stack (or inline in their containing structure) and are copied on assignment. Myra gives you full control over memory layout: you can pack a record to remove padding, specify an explicit alignment for SIMD or DMA use cases, define individual fields as bit fields with a : width specifier, and derive one record from another to inherit its fields.
Record literal syntax (RecordType{ field: value, ... }) lets you construct and assign records in a single expression, which is particularly useful for initialising nested structures or passing records to routines.
module exe records;
type
// Basic record
Point = record
x: int32;
y: int32;
end;
// Packed record - no padding between fields
PackedRec = record packed
a: int8;
b: int8;
c: int8;
end;
// Aligned record - useful for SIMD, DMA buffers, etc.
Align16Rec = record align(16)
a: int8;
b: int8;
end;
// Record inheritance - Point3D extends Point
Point3D = record(Point)
z: int32;
end;
// Bit fields (: width)
Flags = record
active: int32 : 1;
mode: int32 : 3;
level: int32 : 4;
end;
var
p: Point;
p3: Point3D;
f: Flags;
begin
// Record literal syntax
p := Point{ x: 10, y: 20 };
writeln("Point: {}, {}", p.x, p.y);
// Inherited fields accessible directly
p3.x := 100;
p3.y := 200;
p3.z := 300;
writeln("Point3D: {}, {}, {}", p3.x, p3.y, p3.z);
f.active := 1;
f.mode := 7;
f.level := 1;
writeln("Bits: {}, {}, {}", f.mode, f.level, f.active);
end.
Classes are reference types: they are always heap-allocated and accessed through pointers. They support fields, methods, single-level inheritance, virtual dispatch, and parent calls for delegating to an overridden method in the base class. Virtual dispatch happens automatically when you call a method through a base-class pointer, with no explicit vtable management needed.
Classes are allocated with new and released with dispose. There is no garbage collector; you manage lifetime explicitly. This keeps the memory model predictable and deterministic, which matters in games, embedded tools, and any context where latency spikes are unacceptable.
module exe classes;
type
TAnimal = class
Name_: string;
Age: int32;
method Init(AName: string; AAge: int32);
begin
Self.Name_ := AName;
Self.Age := AAge;
end;
method Speak();
begin
writeln("Animal {} says: ...", Self.Name_);
end;
method Describe();
begin
writeln("I am {}, age {}", Self.Name_, Self.Age);
end;
end;
TDog = class(TAnimal)
Breed: string;
method Init(AName: string; AAge: int32; ABreed: string);
begin
Self.Name_ := AName;
Self.Age := AAge;
Self.Breed := ABreed;
end;
// Override - virtual dispatch through base pointer
method Speak();
begin
writeln("Dog {} says: Woof!", Self.Name_);
end;
// Override with parent call
method Describe();
begin
parent.Describe();
writeln("I am a {} breed", Self.Breed);
end;
end;
var
animal: pointer to TAnimal;
dog: pointer to TDog;
cat_as_animal: pointer to TAnimal;
begin
// Allocate on heap
new(animal);
animal^.Init("Generic", 5);
animal^.Speak();
new(dog);
dog^.Init("Buddy", 3, "Golden Retriever");
dog^.Speak();
dog^.Describe();
// Virtual dispatch through base pointer
cat_as_animal := pointer to TAnimal(dog);
cat_as_animal^.Speak(); // calls TDog.Speak()
dispose(animal);
dispose(dog);
end.
Constants in Myra are typed and can be formed from constant expressions. Enumerations define an ordered set of named values. By default the ordinal values start at zero and increment, but you can assign explicit integer values to any member, which is useful when the enum must match a C API or a wire protocol.
Enumeration values support comparison operators (=, <>, <, >, etc.) and can be cast to and from integer types for interop purposes.
module exe enums_consts;
const
MAX_SIZE = 100;
PI = 3.14159;
APP_NAME = "MyApp";
EXPR_CONST = 10 + 5;
type
Color = (Red, Green, Blue, Yellow);
Priority = (Low = 0, Medium = 5, High = 10, Critical = 20);
var
c: Color;
p: Priority;
begin
writeln("MAX_SIZE = {}", MAX_SIZE);
writeln("PI = {}", PI);
c := Green;
writeln("c = Green: {}", c = Green);
writeln("Green > Red: {}", Green > Red);
writeln("Green < Yellow: {}", Green < Yellow);
p := Medium;
writeln("Medium = {}", int32(p));
writeln("Medium > Low: {}", p > Low);
writeln("Medium < Critical: {}", p < Critical);
end.
Myra supports both fixed-size and dynamic arrays. Fixed arrays have their bounds declared at compile time and live on the stack or inline in their containing record. Dynamic arrays are heap-allocated, reference-counted, and resized with setlength. Both kinds are zero-indexed by default, but fixed arrays can declare any integer range as their index bounds.
The len() intrinsic returns the number of elements in either kind of array. For dynamic arrays, setlength grows or shrinks the allocation while preserving existing elements. Shrinking is safe. Elements beyond the new length are discarded.
module exe arrays;
type
IntArr5 = array[0..4] of int32;
var
// Fixed array - zero-based
nums: IntArr5;
// Fixed array - one-based
oneBased: array[1..3] of int32;
// Dynamic array
dyn: array of int32;
i: int32;
begin
// Fixed array
nums[0] := 10; nums[1] := 20; nums[2] := 30;
nums[3] := 40; nums[4] := 50;
writeln("Fixed len: {}", len(nums));
// Dynamic array - setlength allocates/resizes
setlength(dyn, 3);
dyn[0] := 10; dyn[1] := 20; dyn[2] := 30;
writeln("Dyn len: {}", len(dyn));
// Grow - existing elements preserved
setlength(dyn, 5);
writeln("After grow: {} {} {} {} {}", dyn[0], dyn[1], dyn[2], dyn[3], dyn[4]);
// Shrink
setlength(dyn, 2);
writeln("After shrink: {} {}", dyn[0], dyn[1]);
end.
Pascal-style sets are one of Myra's most expressive features for flag and membership logic. A set is backed by a 64-bit integer where each bit represents the presence or absence of an element. Sets support the full algebra: union (+), intersection (*), difference (-), and membership testing (in). This makes them significantly more readable than hand-rolled bitmask operations while compiling down to the same bitwise instructions.
Set literals use curly-brace syntax ({1, 3, 5}). Membership tests read naturally: if 1 in s1 then.
module exe sets;
var
s1: set;
s2: set;
s3: set;
begin
s1 := {1, 3, 5}; // bits 1, 3, 5
s2 := {3, 5, 10}; // bits 3, 5, 10
// Union, intersection, difference
s3 := s1 + s2; // {1, 3, 5, 10}
writeln("Union: {}", int64(s3));
s3 := s1 * s2; // {3, 5}
writeln("Intersection: {}", int64(s3));
s3 := s1 - s2; // {1}
writeln("Difference: {}", int64(s3));
// Membership test
s1 := {1, 3, 5, 10};
if 1 in s1 then
writeln("1 in s1: true");
end;
if not (2 in s1) then
writeln("2 not in s1: true");
end;
end.
Myra has two managed string types. string stores UTF-8 encoded text and is the default for all general-purpose string work. wstring stores UTF-16 encoded text and is useful when interfacing with Windows APIs that require wide strings. Both types are reference-counted and freed automatically. There is no StrNew/StrDispose dance, no null terminator to manage by hand.
String literals can contain any Unicode codepoint directly in source: accents, CJK characters, emoji, whatever your editor can represent. The utf8() intrinsic converts a wstring to string so it can be passed to writeln or any routine that expects UTF-8.
Raw string literals are prefixed with @" and treat everything literally. No escape sequences are processed inside them, making them ideal for regular expressions, Windows paths, and any text that would otherwise need heavy escaping. Wide string literals are prefixed with L".
module exe strings;
var
s: string;
ws: wstring;
begin
s := "Hello, Myra!";
writeln("Basic: {}", s);
// UTF-8: accents, CJK, emoji all work natively
s := "Héllo Wörld! 你好 🎉";
writeln("UTF-8: {}", s);
// Raw string - no escape processing
s := @"C:\Users\Dev\no\escaping\needed";
writeln("Raw: {}", s);
// wstring - UTF-16
ws := L"Wide Hello!";
writeln("WString: {}", utf8(ws));
ws := L"Héllo Wörld! 你好 🎉";
writeln("Wide UTF-16: {}", utf8(ws));
end.
Myra provides the full set of structured control flow constructs you would expect from a Pascal-family language. for..to and for..downto handle counted iteration. while..do tests before the loop body. repeat..until tests after. case dispatches on an ordinal value with support for single values, comma-separated value lists, and inclusive ranges, all in the same statement.
Every block is closed with end, making nesting unambiguous without relying on indentation rules or brace counting.
module exe control;
var
i: int32;
x: int32;
begin
// for..to and for..downto
for i := 0 to 4 do
write(" {}", i);
end;
writeln("");
for i := 5 downto 1 do
write(" {}", i);
end;
writeln("");
// repeat..until
i := 0;
repeat
i := i + 1;
until i = 5;
writeln("repeat ended at: {}", i);
// while..do
i := 10;
while i > 0 do
i := i - 3;
end;
writeln("while ended at: {}", i);
// case with single values, comma lists, and ranges
x := 5;
case x of
1: writeln("one");
2, 3: writeln("two or three");
4..6: writeln("four to six");
else
writeln("other");
end;
end.
Myra uses try/except/finally for structured error handling. The except block runs if an exception is raised inside the try block. The finally block always runs, whether an exception occurred or not, making it the right place to release resources. Both blocks can appear together in a single try statement.
Hardware exceptions raised by the CPU (divide-by-zero, null pointer dereference, illegal instruction) are caught by the same mechanism. The getexceptioncode() and getexceptionmessage() intrinsics retrieve details about the active exception from within an except block. Use raiseexception() to raise a software exception with a message, or raiseexceptioncode() to raise one with both a code and a message.
module exe exceptions;
routine getZero(): int32;
begin
return 0;
end;
begin
// Basic try/except
try
raiseexception("Test error");
except
writeln("Caught: code={}, msg={}", getexceptioncode(), getexceptionmessage());
end;
// try/except/finally
try
raiseexceptioncode(42, "Custom error");
except
writeln("Except: code={}", getexceptioncode());
finally
writeln("Finally always runs");
end;
// Hardware exception - divide by zero
try
writeln("Result: {}", 10 div getZero());
except
writeln("Hardware exception caught: {}", getexceptionmessage());
end;
end.
Myra gives you two levels of memory control depending on what you are building. For class instances, use new to allocate on the heap and dispose to release. For raw blocks of memory such as buffers, C-style arrays, and interop structures, use getmem to allocate, freemem to release, and resizemem to grow or shrink an existing allocation while preserving its contents.
Typed pointers (pointer to T) support the address-of operator (address of), dereference (^), and pointer arithmetic. You can cast between pointer types with an explicit cast expression. This gives you the same memory-level access you would have in C, with the same explicitness that makes the code easy to audit.
module exe memory;
var
p: pointer;
pb: pointer to int8;
x: int32;
px: pointer to int32;
begin
// Raw allocation
p := getmem(100);
pb := pointer to int8(p);
pb^ := 42;
writeln("Value at offset 0: {}", pb^);
pb := pointer to int8(p) + 50;
pb^ := 99;
writeln("Value at offset 50: {}", pb^);
// Grow - preserves existing data
p := resizemem(p, 200);
pb := pointer to int8(p);
writeln("Preserved offset 0: {}", pb^);
freemem(p);
// Typed pointer and address-of
x := 42;
px := address of x;
writeln("Via pointer: {}", px^);
px^ := 100;
writeln("x is now: {}", x);
end.
Named pointer types let you give a typed pointer an alias, which improves readability and makes complex declarations easier to work with. Unions declare a set of fields that all share the same memory region. Reading through one field and writing through another is a well-defined operation in Myra, matching C union semantics exactly.
Unions can be standalone types or embedded anonymously inside a record. The anonymous union form is the idiomatic way to represent a tagged variant type where a tag field discriminates between interpretations of the shared payload.
module exe pointers_unions;
type
// Named pointer type
PInt32 = pointer to int32;
// Standalone union - all fields share memory
IntOrFloat = union
i: int32;
f: float32;
end;
// Record with anonymous union inside (tagged variant)
Variant = record
tag: int32;
union
asInt: int32;
asFloat: float32;
end;
end;
var
p: PInt32;
x: int32;
u: IntOrFloat;
v: Variant;
begin
x := 12345;
p := address of x;
writeln("Via PInt32: {}", p^);
// Write int, read back as float bits
u.i := 0x3F800000; // IEEE 754 for 1.0
writeln("Union float: {}", u.f);
// Tagged variant
v.tag := 1;
v.asInt := 99;
writeln("Variant int: {}", v.asInt);
end.
User-defined variadic routines accept a variable number of arguments. Declare the variadic parameter with ... and use varargs.count to get the argument count and varargs.next(T) to retrieve each argument in order. Type safety is the caller's responsibility. varargs.next(T) reinterprets the next slot as type T, matching the semantics of C's va_arg.
varargs.copy() returns a snapshot of the current argument cursor, enabling multi-pass iteration over the same argument list, useful when you need to scan arguments twice: once to count, once to process.
module exe variadic;
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;
routine printAll(const prefix: string; ...);
var
i: int32;
begin
for i := 0 to varargs.count - 1 do
writeln("{}: {}", prefix, varargs.next(int32));
end;
end;
begin
writeln("sumInts(10, 20, 30) = {}", sumInts(10, 20, 30));
writeln("sumInts(1, 2, 3, 4, 5) = {}", sumInts(1, 2, 3, 4, 5));
writeln("sumInts() = {}", sumInts());
printAll("item", 100, 200, 300);
end.
Myra's module system maps one source file to one output artifact. The module kind is declared on the first line of the file. There are three kinds: exe for executables, dll for shared libraries, and lib for static libraries. This explicit declaration means the build system always knows what to produce without external configuration files for the common case.
Symbols are module-private by default. Use the public keyword to export a symbol from a lib or dll module.
An executable module has a begin..end. body that serves as the entry point:
module exe myprogram;
begin
writeln("Hello!");
end.
A static library is compiled to .lib on Windows and .a on Linux. Import it into another module with the import statement. Only symbols marked public are visible to importers.
module lib math;
// Private helper - not exported
routine dbl(const x: int32): int32;
begin
return x + x;
end;
// Public - visible to importers
public routine "C" add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
public routine "C" quadruple(const x: int32): int32;
begin
return dbl(dbl(x));
end;
end.
A shared library is compiled to .dll on Windows and .so on Linux. Export symbols with public. External callers use the C ABI when the routine is declared with "C" linkage.
module dll mylib;
public routine "C" add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
public var version: int32 = 1;
end.
Use import to bring a lib module into scope. Imported symbols are accessed through the module name as a namespace qualifier:
module exe myapp;
import math;
begin
writeln("{}", math.add(3, 5));
writeln("{}", math.quadruple(5));
end.
Myra's FFI story is straightforward: declare the function signature, name the library, and call it. Use the external keyword to declare a C function that lives in a DLL or shared object. Use "C" linkage to tell the compiler to emit an unmangled symbol name, which is what virtually every C library expects. Varargs (...) are fully supported for printf-style functions.
The optional name "alias" form lets you map a Myra identifier to a differently-named export, which is useful when the C symbol name conflicts with a Myra keyword or contains characters that are not valid in identifiers.
module exe ffi_demo;
// Declare an external C function
routine "C" printf(const fmt: pointer to char; ...): int32;
external "msvcrt.dll";
// External with name alias
routine "C" myFunc(): int32;
external "mylib.dll" name "my_actual_export";
// External variable
var gCounter: int32;
gCounter: external "mylib.dll";
Use $linklibrary to tell the build system which library to link, and $librarypath to add a directory to the linker search path:
module exe uselib;
$linklibrary "mylib.lib"
$librarypath "libs/"
Use $copydll to copy a shared library to the output directory at build time, so the executable can find it at runtime:
$copydll "libs/mylib.dll"
TMyraCImporter automates the conversion of C headers into native Myra modules. It invokes Zig's bundled Clang frontend to preprocess the header — expanding macros, resolving includes, and handling platform ifdefs — then parses the result and emits a complete .myra module file. The generated module contains all types (structs, unions, anonymous unions, enums, typedefs, bit fields), constants extracted from #define values, and external routine declarations with the correct "C" linkage. The output is ready to import and use directly; no manual editing is required for the common case.
The importer is cross-platform aware from the start. You configure per-target DLL names, library paths, and $copydll directives separately for each platform, and the generated module uses $ifdef TARGET_WIN64 / $elseif TARGET_LINUX64 blocks to select the right settings at compile time.
Here is the complete configuration used to import raylib:
LImporter := TMyraCImporter.Create();
try
LImporter.SetSavePreprocessed(True);
LImporter.SetDllName(tpWin64, 'raylib');
LImporter.SetDllName(tpLinux64, 'raylib');
LImporter.AddLibraryPath(tpWin64, 'res/libs/raylib/bin');
LImporter.AddCopyDLL(tpWin64, 'res/libs/raylib/bin/raylib.dll');
LImporter.AddLibraryPath(tpLinux64, 'res/libs/raylib/bin');
LImporter.AddCopyDLL(tpLinux64, 'res/libs/raylib/bin/libraylib.so.550');
LImporter.SetOutputPath('res\\libs\\raylib\\src');
LImporter.AddIncludePath('res\\libs\\raylib\\include\\');
LImporter.AddSourcePath('res\\libs\\raylib\\include\\');
LImporter.SetHeader('res\\libs\\raylib\\include\\raylib.h');
LImporter.SaveToConfig('res\\libs\\raylib\\raylib.toml');
if LImporter.Process() then
WriteLn('SUCCESS')
else
WriteLn('ERROR: ', LImporter.GetLastError());
finally
LImporter.Free();
end;This produces res/libs/raylib/src/raylib.myra — a complete binding for raylib that compiles and runs on both Win64 and Linux64 from the same source file. The generated module header looks like this:
module lib raylib;
$ifdef TARGET_WIN64
$librarypath "res/libs/raylib/bin"
$copydll "res/libs/raylib/bin/raylib.dll"
$elseif TARGET_LINUX64
$librarypath "res/libs/raylib/bin"
$copydll "res/libs/raylib/bin/libraylib.so.550"
$endif
$ifdef TARGET_WIN64
const cDllName = "raylib";
$elseif TARGET_LINUX64
const cDllName = "raylib";
$endif
All C declarations follow. Structs map to Myra records, unions map to Myra unions (including anonymous unions embedded in records), enums become Myra enum types, #define integer and float constants become public const values, and function declarations become external routines.
The importer provides post-processing hooks for the cases where generated output needs adjusting:
AddExcludedType(name)— skip a type and all declarations that reference it (useful for platform types likeva_list)AddExcludedFunction(name)— skip a specific function declarationInsertTextBefore(target, text)/InsertTextAfter(target, text)— inject lines at a specific point in the outputInsertFileBefore(target, path)/InsertFileAfter(target, path)— splice in the contents of a file at a specific pointReplaceText(old, new)— perform a targeted substitution in the outputAddModuleImport(name)— add animportstatement to the generated moduleSaveToConfig(path)/LoadFromConfig(path)— persist and reload the full importer configuration as a TOML file so imports are reproducible
Because Myra compiles to C++ 23, it provides a set of escape hatches that let you drop raw C++ directly into generated code when the situation demands it. These are intentionally low-level tools. They exist for cases where you need access to the C++ standard library, need to declare a helper function with C++ idioms, or need to compute a value using a C++ expression that has no direct Myra equivalent. They are not the normal way to write Myra code, but they make the standard library modules possible without requiring FFI declarations for every C++ API.
There are three forms.
cppstart header / cppend: placed at module level, injects raw C++ text into the header section of the generated file. Use this for #include directives and any declarations that must appear before the module's code:
module lib Files;
cppstart header
#include <fstream>
#include <filesystem>
#include <string>
cppend
cppstart source / cppend: placed inside a routine body or at module level, injects a block of raw C++ statements into the generated source:
cppstart source
static inline std::fstream* myra_files_stream(void* p) {
return reinterpret_cast<std::fstream*>(p);
}
cppend
public routine OpenRead(const AFileName: string): TFileStream;
begin
cppstart source
auto* __s = new std::fstream(AFileName, std::ios::in);
if (!__s->is_open()) { delete __s; __s = nullptr; }
cppend
return cpp("(void*)__s");
end;
cpp("expression"): an inline expression escape. The string literal is emitted verbatim as a C++ expression at the call site:
public routine ReadChar(): char;
begin
return char(cpp("std::cin.get()"));
end;
public routine Flush();
begin
cpp("std::cout.flush()");
end;
The convention in Myra's standard library is to use double-underscore prefixed names (e.g. __s, __r, __ok, __sz) for variables declared inside cppstart source blocks to avoid name collisions with Myra-generated identifiers.
Directives begin with $ and are processed at compile time. They control which code gets compiled, what the compiler emits, and how the build system links the output. Directives are not preprocessor macros. They are part of the language grammar and interact cleanly with the rest of the compiler pipeline.
The conditional compilation directives ($ifdef, $ifndef, $elseif, $else, $endif) let you include or exclude code based on defined symbols. This is the idiomatic way to write platform-specific code in Myra without splitting it across separate files.
module exe conditional;
// Define your own symbols
$define DEBUG
$ifdef DEBUG
$message hint "Debug build active"
$endif
// Target-specific declarations
$ifdef TARGET_WIN64
routine "C" Sleep(const ms: uint32);
external "kernel32.dll";
$endif
$ifdef TARGET_LINUX64
routine "C" usleep(const us: uint32): int32;
external "libc.so.6";
$endif
begin
$ifdef DEBUG
writeln("Running in DEBUG mode");
$endif
$ifdef TARGET_WIN64
Sleep(0);
$endif
end.
Predefined symbols (automatically set by the compiler based on $target):
| Symbol | When defined |
|---|---|
MYRA |
Always |
TARGET_WIN64 |
$target win64 |
WIN64, MSWINDOWS, WINDOWS |
$target win64 |
TARGET_LINUX64 |
$target linux64 |
LINUX, POSIX, UNIX |
$target linux64 |
CPUX64 |
win64 or linux64 |
CONSOLE_APP |
$subsystem console (default) |
GUI_APP |
$subsystem gui |
Full directive reference:
| Directive | Description |
|---|---|
$subsystem type |
console (default) or gui, controls whether the executable has a console window |
$target platform |
Set target: win64, linux64 |
$optimize level |
debug, releasesafe, releasefast, releasesmall |
$define name |
Define a conditional symbol |
$undef name |
Undefine a symbol |
$ifdef name |
Compile if symbol defined |
$ifndef name |
Compile if symbol not defined |
$elseif name |
Else-if branch |
$else |
Else branch |
$endif |
Close conditional |
$message level "text" |
Emit hint/warn/error/fatal at parse time |
$breakpoint |
Record DAP debugger breakpoint location |
$linklibrary "path" |
Link a library |
$librarypath "path" |
Add linker search path |
$modulepath "path" |
Add module search path |
$copydll "path" |
Copy DLL to output directory |
$exeicon "path" |
Embed icon in Windows EXE |
$addverinfo |
Enable version info embedding |
$vimajor, $viminor, $vipatch |
Version number fields |
$viproductname, $videscription |
Version info strings |
$vifilename, $vicompanyname, $vicopyright |
Version info strings |
$unittestmode on|off |
Toggle unit test mode in emitter |
Intrinsics are built-in operations that look and feel like routine calls but are handled directly by the compiler. They have no overhead and cannot be reimplemented in user code. Use them whenever you need a capability that requires compiler cooperation, such as querying the length of an array, getting the size of a type, or retrieving exception information from inside a handler.
| Intrinsic | Description |
|---|---|
len(expr) |
Number of elements in an array or characters in a string |
sizeof(T) or sizeof(expr) |
Size in bytes of a type or expression |
utf8(wstr) |
Convert a wstring to string |
getmem(size) |
Allocate size bytes on the heap |
resizemem(ptr, size) |
Resize a heap allocation, preserving contents |
paramcount() |
Number of command-line arguments passed to the program |
paramstr(n) |
The Nth command-line argument as a string |
getexceptioncode() |
Integer code of the active exception (use inside except) |
getexceptionmessage() |
Message string of the active exception (use inside except) |
Myra has first-class support for unit tests baked into the language. test blocks are appended after the module's closing end. and are part of the source file but are only compiled and executed when the test runner invokes the module in test mode. Each test block has a name string that appears in the test output, making failures easy to locate.
Tests can have their own var section for local state. The test assertion intrinsics handle failure signalling and reporting automatically. All tests are independent. A failure in one does not abort the suite.
Test assertion intrinsics:
| Intrinsic | Description |
|---|---|
TestAssertEqualInt(expected, actual) |
Fail if two integer values are not equal |
TestAssertEqualBool(expected, actual) |
Fail if two boolean values are not equal |
TestAssertTrue(expr) |
Fail if expression is not true |
TestAssertFalse(expr) |
Fail if expression is not false |
TestAssertNil(ptr) |
Fail if pointer is not nil |
When an assertion fails, the test runner reports which assertion failed, the file and line number, and the expected vs actual values. Execution continues with the next test.
module exe math_module;
$unittestmode on
routine add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
routine isPositive(const a: int32): boolean;
begin
return a > 0;
end;
begin
// main program body
end.
test "addition"
begin
TestAssertEqualInt(5, add(2, 3));
TestAssertEqualInt(0, add(-1, 1));
end;
test "boolean checks"
begin
TestAssertTrue(isPositive(5));
TestAssertFalse(isPositive(-5));
TestAssertEqualBool(true, isPositive(1));
end;
test "pointer check"
var
p: pointer;
begin
p := nil;
TestAssertNil(p);
end;
The $unittestmode on directive tells the emitter to include the test framework scaffolding. The test runner prints a summary showing pass/fail counts and highlights any failed assertions with the source location.
The myra command is the primary interface to the Myra toolchain. It handles project creation, compilation, execution, and cleanup, all from the current directory, without any build configuration files for the common case.
myra <COMMAND> [OPTIONS]
COMMANDS:
init <n> [type] Create a new Myra project
build Compile Myra source and build output
run Build and execute the program
clean Remove all generated files
version Display version information
help Display help
myra init scaffolds a new project directory with the correct structure and a starter source file.
myra init <n> [exe|lib|dll]
The second argument is optional and defaults to exe. The three project types map directly to Myra's module kinds:
| Type | Output | Description |
|---|---|---|
exe |
Executable | Stand-alone program with a begin..end. entry point |
lib |
Static library | .lib / .a, imported by other modules at link time |
dll |
Shared library | .dll / .so, loaded at runtime with C-linkage exports |
After myra init MyGame, your project looks like this:
MyGame/
src/
MyGame.myra ← starter source file
The starter source matches the project type:
// exe starter
module exe MyGame;
begin
writeln("Hello from Myra!");
end.
// lib starter
module lib MyLib;
public routine "C" add(const a: int32; const b: int32): int32;
begin
return a + b;
end;
end.
Next steps after init:
cd MyGame
myra build
myra run
Run myra build from inside a project directory (the one that contains the src/ folder). The compiler derives the project name from the directory name and looks for src/<n>.myra as the entry point.
cd MyGame
myra buildThe build pipeline runs in five steps: tokenization, parsing, semantic analysis, C++ code generation, and Zig compilation. Status messages are printed for each phase. On success you will see:
Build completed successfully!
Build output lives in the project directory:
MyGame/
generated/ ← intermediate C++ sources (managed by the compiler)
zig-out/
bin/MyGame.exe ← final executable (Windows)
bin/MyGame ← final executable (Linux)
build.zig ← generated Zig build file
.zig-cache/ ← Zig's incremental build cache
myra run is equivalent to myra build followed by running the resulting executable. Build output is printed first, then the program runs immediately:
myra runIf the build fails, the program does not run. If the target is linux64 and WSL2 is installed, the Linux binary is executed automatically through WSL.
myra clean removes everything the build system created: the generated/ directory, zig-out/, .zig-cache/, and build.zig. Your source files are untouched.
myra clean# Create an exe project
myra init MyGame
# Create a static library project
myra init MyLib lib
# Create a shared library project
myra init MyPlugin dll
# Build the current project
myra build
# Build and immediately run
myra run
# Clean all generated output
myra clean
# Show version
myra versionMyra ships with a full Language Server Protocol implementation. It provides rich editor features such as completion, hover, go-to-definition, and rename, for any editor that speaks LSP. The server works in two deployment modes: out-of-process as a standalone executable, or in-process as an embeddable library class.
| Capability | Description |
|---|---|
| Diagnostics | Compiler errors and warnings published on every document change |
| Completion | Context-aware completion for keywords, built-in types, symbols, and snippets |
| Hover | Type information and documentation for the symbol under the cursor |
| Go to Definition | Jump to the declaration of any routine, type, or variable |
| Go to Type Definition | Jump to the type declaration for any expression |
| Find All References | List every use of a symbol across the open documents |
| Document Symbols | Outline of all declarations in the current file |
| Workspace Symbols | Search across all open files by name |
| Signature Help | Parameter hints when typing a routine call |
| Inlay Hints | Inline type annotations for variable declarations and return values |
| Rename | Rename a symbol everywhere it is used |
| Folding Ranges | Code folding for begin..end blocks and routines |
| Semantic Tokens | Full token classification for syntax highlighting |
| Call Hierarchy | Navigate callers and callees of any routine |
| Document Formatting | Format the document: normalise keyword casing, spacing, and trailing whitespace |
| Code Actions | Quick fixes and formatting actions |
All features read directly from the semantically enriched AST produced by the Myra compiler pipeline. There is no separate symbol table or secondary type resolution. The same analysis that powers the compiler powers the editor.
The simplest deployment is to run MyraLSP.exe as an external server process. The server communicates over stdin/stdout using the standard LSP JSON-RPC 2.0 framing (Content-Length: N\r\n\r\n{...}).
Most editors that support LSP can be configured to launch MyraLSP.exe as the language server for .myra files.
VS Code (.vscode/settings.json):
{
"myra.server.path": "C:/path/to/MyraLSP.exe"
}Or configure it manually in your extension host settings using the languageserver extension pattern:
{
"languageserver": {
"myra": {
"command": "C:/path/to/MyraLSP.exe",
"filetypes": ["myra"]
}
}
}Neovim (via nvim-lspconfig):
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
if not configs.myra then
configs.myra = {
default_config = {
cmd = { 'MyraLSP.exe' },
filetypes = { 'myra' },
root_dir = lspconfig.util.root_pattern('src'),
},
}
end
lspconfig.myra.setup({})Sublime Text (via LSP package):
{
"clients": {
"myra": {
"enabled": true,
"command": ["C:/path/to/MyraLSP.exe"],
"selector": "source.myra"
}
}
}Once the server is running, open any .myra file and the editor will receive diagnostics automatically. All other LSP features activate on-demand as you interact with the file.
For tooling that embeds the Myra compiler directly, such as custom editors, IDEs, and language workbenches, the TMyraLSPServer class can be instantiated and driven programmatically. This avoids the overhead of process spawning and pipe I/O for tight integrations.
The server accepts any pair of TStream objects for input and output. Wire up memory streams, pipes, or sockets as your host application requires:
uses
Myra.LSP;
var
LServer: TMyraLSPServer;
LInput: TMemoryStream;
LOutput: TMemoryStream;
begin
LServer := TMyraLSPServer.Create();
try
// Provide your own I/O streams
LServer.SetStreams(LInput, LOutput);
LServer.Run(); // blocks until 'exit' notification
finally
LServer.Free();
end;
end;The TMyraLSPService class one level below the server exposes the full logic layer without any JSON. You can call GetCompletions, GetHover, GetDefinition, and so on directly with Myra types if you want to bypass JSON serialisation entirely.
The LSP implementation has three layers:
TMyraLSPDocument ← holds source text, runs the full compiler pipeline on change
TMyraLSPService ← pure logic, answers every LSP query by reading the enriched AST
TMyraLSPServer ← JSON-RPC 2.0 framing and dispatch loop
Each open document maintains a TMyraLSPDocument that runs the Myra lexer, parser, and semantic analyser on every content change. The enriched AST is kept alive so that all LSP queries, completion, hover, references, rename, operate on the same fully analysed tree that the compiler uses.
Myra includes an integrated debugger built on the Debug Adapter Protocol (DAP) via LLDB. The debugger connects to lldb-dap (the LLDB DAP adapter) and exposes full source-level debugging for Myra programs: breakpoints, stepping, call stack inspection, variable evaluation, and expression evaluation.
When you place a $breakpoint directive in your Myra source, the compiler records the source file and line number into a .breakpoints TOML file alongside the compiled executable. The debugger loads this file automatically at launch and sets the corresponding breakpoints in LLDB before execution begins.
module exe demo;
routine compute(const x: int32): int32;
begin
$breakpoint // ← the debugger will stop here
return x * x;
end;
begin
writeln("Result: {}", compute(7));
end.
After myra build, the output directory will contain MyProgram.breakpoints, a TOML file listing every $breakpoint location. The debugger reads this file before launch and registers each location with LLDB. This means you never have to manually re-enter your breakpoints after a recompile.
The TMyraDebugREPL class provides an interactive command-line debug session. It wraps TMyraDebug with a prompt-driven interface suitable for scripting and terminal-based workflows.
Start a session by providing the path to the compiled executable:
(lldb-dap) r ← run/restart
(lldb-dap) c ← continue after a breakpoint hit
(lldb-dap) n ← step over
(lldb-dap) s ← step into
(lldb-dap) finish ← step out
(lldb-dap) bt ← show call stack
(lldb-dap) locals ← show local variables
(lldb-dap) p myVar ← evaluate expression
(lldb-dap) b myfile.myra:42 ← set breakpoint at line 42
(lldb-dap) bl ← list all breakpoints
(lldb-dap) bd 1 ← delete breakpoint #1
(lldb-dap) bc ← clear all breakpoints
(lldb-dap) threads ← list all threads
(lldb-dap) verbose on ← enable raw DAP message logging
(lldb-dap) quit ← exit
| Command | Description |
|---|---|
h, help |
Show command reference |
b <file>:<line> |
Set a breakpoint at the given source location |
bl |
List all breakpoints with verified status |
bd <id> |
Delete breakpoint by index |
bc |
Clear all breakpoints |
threads |
Show all running threads |
bt |
Show call stack (backtrace) for the current thread |
locals |
Show local variables in the current stack frame |
p <expr> |
Evaluate an expression in the current context |
c |
Continue execution until the next breakpoint or exit |
n |
Step over: execute one line, stepping over routine calls |
s |
Step into: execute one line, entering routine calls |
finish |
Step out: run until the current routine returns |
r |
Run or restart the program |
file <path> |
Load a different executable |
verbose on|off |
Toggle raw DAP message logging to the console |
quit |
Terminate the debug session and exit |
TMyraDebug exposes the full DAP session as a clean Delphi API for embedding in tools and IDEs:
uses
Myra.Debug;
var
LDebugger: TMyraDebug;
LFrames: TArray<TStackFrame>;
LVars: TArray<TVariable>;
LResult: string;
begin
LDebugger := TMyraDebug.Create();
try
LDebugger.LLDBDAPPath := 'C:\LLVM\bin\lldb-dap.exe';
// Load breakpoints from compiler output
LDebugger.LoadBreakpointsFromFile('MyProgram.breakpoints');
// Start LLDB-DAP adapter, initialize, and launch
LDebugger.Start();
LDebugger.Initialize();
LDebugger.Launch('MyProgram.exe');
LDebugger.SetBreakpointsFromFile(); // registers loaded breakpoints
LDebugger.ConfigurationDone();
// Event loop
LDebugger.ProcessPendingEvents(500);
if LDebugger.State = dsStopped then
begin
// Inspect the stopped location
LFrames := LDebugger.GetCallStack(LDebugger.CurrentThreadID);
LVars := LDebugger.GetLocalVariables(LFrames[0].ID);
// Evaluate an expression
LDebugger.EvaluateExpression('myVar + 1', LFrames[0].ID, LResult);
LDebugger.ContinueExecution();
end;
LDebugger.Disconnect();
finally
LDebugger.Free();
end;
end;The debugger follows a well-defined state progression:
NotStarted → Initializing → Ready → Launched → Running → Stopped → Exited
↑___________↓ (continue/step loops)
State transitions fire the OnStateChange callback, which you can use to update UI or log session events. Breakpoint hits fire OnBreakpointHit with the source file and line number. Program output appears through OnOutput.
Myra supports Windows (Win64) and Linux (Linux64) targets from a single codebase. The core language is identical on both platforms. Only the names of external libraries differ when you are calling OS APIs. Shared library names change (kernel32.dll vs libc.so.6), but all your application logic, data structures, algorithms, and module organisation stay the same.
Cross-compilation from Windows to Linux64 is a first-class workflow. When you target linux64, Myra uses Zig as the cross-compiler backend to produce a Linux ELF binary without requiring a Linux toolchain on the host. If WSL2 is installed, Myra automatically runs the resulting binary through WSL to execute it.
// In the source file:
$target win64
// or
$target linux64
You can also override the target at the command line without modifying the source. The compiler injects the appropriate TARGET_* and platform alias symbols automatically based on the active target.
To build and run Linux binaries from a Windows host, install WSL2 with Ubuntu:
wsl --install -d UbuntuThen inside WSL, install the base build tools (needed for standard C runtime linking):
sudo apt update && sudo apt install build-essentialThat is the full setup. Myra handles the rest: locating WSL, invoking the binary, and routing stdout/stderr back to your terminal.
| Target | Status |
|---|---|
Windows x64 (win64) |
✅ Supported |
Linux x64 (linux64) |
✅ Supported (native; via WSL on Windows) |
macOS x64 (macos64) |
🔜 Planned |
Windows ARM64 (winarm64) |
🔜 Planned |
Linux ARM64 (linuxarm64) |
🔜 Planned |
The release is the only way to use Myra. Downloading or cloning the source alone will not give you a working compiler. The release package contains everything required to compile and run Myra programs: the compiler, the LSP server, the Zig build backend, the C++ runtime, the standard library, and the debugger adapter. None of these components are built from source; they ship pre-compiled and ready to use.
⬇️ Download the latest release
Unzip the release to a directory of your choice. Add bin\ to your PATH so that myra is available from any terminal. That is the complete installation. There is nothing else to install, configure, or compile.
Myra/
bin/
myra.exe ← CLI compiler and project tool
MyraLSP.exe ← LSP server (out-of-process mode)
res/
runtime/ ← Myra C++ runtime (required at compile time)
libs/std/ ← Standard library modules
tests/ ← Test suite (.myra files)
zig/ ← Bundled Zig compiler (the build backend)
lldb/ ← LLDB-DAP debugger adapter
| Requirement | |
|---|---|
| Host OS | Windows 10/11 x64 |
| Linux target | WSL2 + Ubuntu (install once with wsl --install -d Ubuntu) |
To target Linux from Windows: install WSL2 with Ubuntu if you haven't already. Myra discovers it automatically and uses it to run Linux binaries without any additional configuration.
This section is for contributors who want to modify the Myra compiler itself. If you just want to write Myra programs, stop at the release download above. You do not need this.
Building from source produces new myra.exe, MyraLSP.exe, and Testbed.exe binaries. It does not produce the toolchain (Zig, runtime, standard library, debugger). Those come from the release and are required even when developing the compiler.
| Minimum | Tested | |
|---|---|---|
| Host OS | Windows 10 x64 | Windows 11 x64 |
| Compiler host | Delphi 11 (Alexandria) | Delphi 12 (Athens) |
Option 1: Download ZIP
Option 2: Git Clone
git clone https://github.com/tinyBigGAMES/Myra.git- Open
src\Myra Programming Language.groupprojin Delphi - Build all projects in the group (
Myra,MyraLSP,Testbed) - The
Testbedproject compiles and executes every test inbin\res\tests\and reports colour-coded results
The test suite lives in bin\res\tests\. Each .myra file contains a /* EXPECT: ... */ block describing the expected output. The test runner compiles the file, runs the result, compares the actual output against the expectation, and prints colour-coded results: green for pass, red for fail. You can run individual tests by index or run the full suite in one shot.
Myra is an open project and contributions are welcome at every level. Whether you are fixing a typo in the documentation, tracking down a compiler bug, or proposing a new language feature, your involvement is appreciated.
Here is how to get involved:
- Report bugs: Open an issue on GitHub with a minimal reproduction case. The smaller the example, the faster the fix.
- Suggest features: Describe the use case first, then the syntax you have in mind. Features that emerge from real problems get traction fastest.
- Submit pull requests: Bug fixes, documentation improvements, new test cases, and well-scoped features are all welcome. Keep changes focused and include a test if you can.
- Review and discuss: Even reviewing open pull requests and issues helps move the project forward.
- Give feedback: Star the repo if you find it useful. It helps others discover the project.
Join our Discord to discuss development, ask questions, share what you are building with Myra, or just hang out with people who care about this kind of programming.
Myra is a labour of love, built in the open. If it saves you time, sparks an idea, or becomes part of something you ship, here are a few ways you can give back:
- ⭐ Star the repo: It costs nothing and helps others find the project
- 🗣️ Spread the word: Write a post, mention it on social media, or share it in a community you are part of
- 💬 Join us on Discord: Share what you are building and help shape what comes next
- 💖 Become a sponsor: Support ongoing development via GitHub Sponsors. Sponsorship directly funds time spent on the compiler, runtime, tooling, and documentation.
Every bit of support keeps the project alive and moving forward.
Myra is licensed under the Apache License 2.0. See LICENSE for details.
Myra™ Programming Language.
Copyright © 2025-present tinyBigGAMES™ LLC All Rights Reserved.
