ماه — "moon" in Persian

A small language, built entirely from scratch.

Mah is a hand-written lexer, parser, resolver, bytecode compiler, and VM for its own portable bytecode format -- plus a formatter, a real language server, and editor integrations. Every one of them pure Python, standard library only, with zero third-party runtime dependencies anywhere in the toolchain. A native Rust runtime runs the exact same bytecode.

struct Point { x, y }
fn add(a, b) {
    return Point { x: a.x + b.x, y: a.y + b.y }
}
let p = add(Point { x: 1, y: 2 }, Point { x: 3, y: 4 })
print(p)        # Point { x: 4, y: 6 }

enum Shape {
    Circle { r },
    Square { s },
    Empty
}
fn area(s) {
    match s {
        Shape.Circle { r } => { 3 * r * r }
        Shape.Square { s } => { s * s }
        Shape.Empty => { 0 }
    }
}
print(area(Shape.Circle { r: 5 }))   # 75

Get started

Clone the repo and run programs immediately -- nothing to pip install. Once mah is on your PATH (make install-mah), every command below works from anywhere.

mah init my-app        # scaffolds mah-project.toml, src/main.mh, docs/
cd my-app
mah run                 # runs the entry point from mah-project.toml
mah build                # writes every [[target]], e.g. build/my-app.mahc

What's in the language

Closures that capture by reference

Heap-allocated Frames linked by a static chain (the classic SCP/DCP technique) mean a closure that outlives its call still sees later mutations of the variables it captured -- like JavaScript, not Python.

Structs, enums, and pattern matching

struct fields have no types; enum variants can be unit or struct-shaped. match handles literals, some/none, struct/enum destructuring, range patterns, guards, and wildcards.

Rust-style traits

trait declares required and default methods; impl Trait for Type implements them, including for your own types and (orphan-rule-respecting) built-ins like Number.

Lazy iterators and ranges

map/filter/skip/take/reduce work lazily over ranges, Strings, Vectors, Maps, and any type that implements Iterable + Iterator -- plus for loops and range patterns.

Cooperative async

detach any call or expression to get a Promise back, then .await it. Single-threaded and cooperative -- no hidden threads.

Default params and kwargs

fn greet(name, greeting = "Hello") and greet("Mah", greeting: "Salam") -- defaults and keyword arguments work for functions, methods, and static calls alike.

Modules with export/import

export fn / export let mark what's visible to importers; import "lib.mh" or import m from "lib" bring it in, flat or namespaced. Diamond imports and cycles are safe.

mah format

A whitespace-only formatter that verifies its own output: same tokens, same comments, same AST before it ever writes a file.

A real LSP + VS Code / Neovim

Live diagnostics, hover, completion, go-to-definition, cross-file rename, and format-on-save -- built on the same resolver the compiler uses, not a second analysis.

Portable bytecode, two VMs

mah build compiles to a portable .mahc format (docs/MAHC_FORMAT.md) with both a Python reference VM and a from-scratch Rust runtime (mah-vm) -- plus self-contained, dependency-free executables.

Project manifests

mah init scaffolds mah-project.toml, src/main.mh, and docs written for coding agents. mah run / mah build / mah check work from any subdirectory.

Optional type annotations

fn add(a: Number, b: Number) -> Number { a + b } -- syntax and generics exist today; a static type checker with inference is the next milestone (see /docs/types).

Closures capture by reference

let counter = fn() {
    let count = 0
    return fn() {
        count = count + 1
        return count
    }
}
let next = counter()
print(next())   # 1
print(next())   # 2

Async: detach, then .await

fn slow(n) { sleep_async(10); n * 2 }
let a = detach slow(21)
let b = detach { sleep_async(5); "from a block" }
print(a.await, b.await)   # 42 from a block

Tooling that isn't an afterthought

mah lsp is a dependency-free LSP built directly on the same resolver the compiler uses: live diagnostics, hover, completion, go-to-definition, cross-file rename, and document formatting. It ships with a VS Code extension and Neovim integration (tree-sitter grammar + ftplugin).

mah build compiles to a portable .mahc bytecode file that runs on the Python VM or the from-scratch Rust runtime (--vm rust), and mah build --self-contained bundles the runtime and bytecode into one executable file that needs neither Python nor mah installed to run.