Skip to content

Repository files navigation

Luna

Luna is an experimental, statically typed programming language focused on concise source, strong inference, functional composition, predictable native semantics, and first-class systems interoperability.

The project is currently building an interpreter-first language core in F#. Source is parsed into a source-oriented syntax tree, resolved and type-checked into typed HIR, and executed by a reference interpreter. That pipeline defines Luna semantics before native, WebAssembly, or JavaScript backends are introduced.

Language direction

Luna combines familiar object-oriented syntax with functional language features:

  • immutable bindings by default, with explicit mutation;
  • constraint-based static type inference;
  • expression-oriented functions and control flow;
  • first-class functions and lexical closures;
  • discriminated unions and exhaustive pattern matching;
  • Option<T> and Result<T, TError> instead of ordinary null values;
  • proper direct and mutual tail recursion without language-stack growth;
  • checked integer arithmetic and explicit conversions;
  • structural interfaces, also spelled contract, with trait-like defaults;
  • experimental external impl Contract for Type conformance with coherent, deterministic witness selection;
  • typed synchronous events with subscription handles, kept distinct from competing-consumer channels and asynchronous broadcast topics;
  • higher-order collection pipelines and future provider-backed queries;
  • value-first structs with explicit Box<T>, Rc<T>, and Arc<T> ownership in the future native runtime;
  • read-only-by-default span<T> scoped views and memory<T> storable/async leases, with explicit <mut T> writable forms and separate storage owners;
  • read-only parameters by default, with explicit future mut access and take ownership transfer;
  • logical async fun(...): T source signatures whose calls produce Task<T> without repeating task machinery in every declaration;
  • explicit C ABI and unsafe boundaries for systems integration.

The canonical source extension is .luna. The short .ln extension has identical semantics and tooling behavior.

let offset = 1
let increment: fun(i32): i32 = fun(value) => value + offset

let answer = increment(41)
log.Info($"answer = {answer}")

Single-file applications may use top-level statements, so an explicit main() function is optional.

Experimental external conformance can adapt a type without modifying its declaration:

pub contract Drawable {
    Draw(): void
}

impl Drawable for ThirdPartyCircle {
    Draw(): void {
        RenderCircle(this)
    }
}

Language examples

Discriminated unions and pattern matching

Discriminated unions define a closed set of named cases. Cases may carry typed values, and matching can unpack those values exhaustively:

pub union LookupResult
    | Found value: i32
    | Missing
    | Failed message: string

pub describe(result: LookupResult): string {
    result is
        | Found value => $"found {value}"
        | Missing => "nothing was found"
        | Failed message => $"lookup failed: {message}"
}

let result = Found 42
log.Info(describe(result))

Pattern expressions also support literal values, ranges, bindings, guards, and _ discards:

let score = 8

let label = score is
    | 1..5 => "low"
    | candidate when candidate % 2 == 0 => $"even: {candidate}"
    | _ => "unclassified"

Option, Result, and postfix ?

Postfix ? unwraps Some or Ok. A None or Error returns immediately from the current compatible function. Result<T> uses the standard ErrorInfo error type; Result<T, TError> selects a domain-specific error:

pub increment(value: Result<i32, string>): Result<i32, string> {
    let number = value?
    Ok(number + 1)
}

let result = increment(Ok(41))

let answer = result is
    | Ok value => value
    | Error message => {
        log.Error(message)
        0
    }

The operand is evaluated once. Result<T, TError>? requires the enclosing function to return a compatible Result with the same error type; Option<T>? similarly requires an Option return.

Mutable bindings

Bindings are immutable unless the declared name ends in ?:

let count?: i64 = 0

while count < 10 {
    count = count + 1
}

log.Info($"count = {count}")

The two uses of ? are distinguished by context: let count? declares mutable storage, while value? is an Option/Result propagation expression.

Current implementation

The current F# compiler and reference interpreter support executable vertical slices for:

  • source text, spans, lexing, parsing, and structured diagnostics;
  • module declarations, open, top-level statements, and named functions;
  • scalar values, checked integers, strings, interpolation, and multiline strings;
  • bindings, mutable locals, blocks, if, while, and loop/exit;
  • function values, higher-order calls, closures, and captured values;
  • direct and mutual proper tail calls;
  • pattern expressions and user-defined discriminated unions;
  • generic functions and generic unions, including trailing default type arguments on unions;
  • core Option<T> and Result<T, TError> unions;
  • postfix ? propagation for compatible Option and Result functions;
  • bootstrap std.log levels: Debug, Info, Warn, Error, and Fatal.

The repository is still in the Language Alpha stage. LLVM/native emission, JavaScript and WebAssembly backends, the public standard library beyond bootstrap logging, package management, networking, web frameworks, Godot integration, and self-hosting remain planned work rather than current implementation.

Compiler architecture

Luna source (.luna or .ln)
    -> SourceText and source spans
    -> tokens
    -> syntax AST
    -> declaration collection and name resolution
    -> type inference and semantic checks
    -> typed HIR
    -> F# reference interpreter

The syntax AST records what the programmer wrote. Typed HIR records what the program means, including resolved symbols, inferred types, overload choices, pattern decisions, conversions, and tail transfers. The interpreter executes typed HIR rather than reinterpreting syntax.

Repository layout

src/
  Luna.Compiler/       compiler frontend, typed HIR, and interpreter
  luna/                command-line host
  luna.slnx            .NET solution
tests/
  Luna.Compiler.Tests/ compiler and interpreter tests
  fixtures/language/   paired .luna and .ln conformance fixtures
.docs/
  luna-lang-spec.md    canonical language specification
  impl-roadmap.md      implementation order and phase gates
  decisions/           accepted architectural decisions

Build and test

Luna uses the .NET 10 SDK pinned in global.json, which also selects Microsoft.Testing.Platform for the .NET 10 dotnet test experience.

cd src
dotnet build luna.slnx
dotnet test --solution luna.slnx

The test suite uses MSTest with Shouldly assertions and deterministic Bogus data where generated inputs are useful.

CLI

The current CLI exposes check and run:

cd src

dotnet run --project luna/luna.fsproj -- check ../path/to/program.luna
dotnet run --project luna/luna.fsproj -- run ../path/to/program.luna
dotnet run --project luna/luna.fsproj -- run --log-level debug ../path/to/program.ln
dotnet run --project luna/luna.fsproj -- check ../path/to/project-directory
dotnet run --project luna/luna.fsproj -- run ../path/to/core.luna ../path/to/app.ln

check and run accept one or more files or directories. Directory inputs are searched recursively for both .luna and .ln files in deterministic path order, and overlapping inputs are de-duplicated. Luna does not require a magic app or program filename.

Until the CLI is packaged, invoke it through dotnet run as shown above.

Documentation

The specification describes accepted language direction, while the roadmap and passing tests determine what is implemented today.

Long-term path

The planned bootstrap sequence is:

F# compiler and reference interpreter
    -> target-neutral Luna IR
    -> LLVM native backend
    -> small native runtime and C ABI
    -> Luna becomes capable of expressing its compiler
    -> incrementally self-hosted Luna compiler

Self-hosting and replacing LLVM are separate milestones. LLVM may remain Luna's production machine-code backend even after the compiler is written in Luna.

Planned C ABI libraries

Luna's initial foreign calling convention is the platform C ABI, spelled with bare extern. A declaration without a body imports C; a public declaration with a body exports Luna:

extern fun puts(value: Pointer<u8>): i32

pub extern fun luna_add(left: i32, right: i32): i32 {
    left + right
}

The planned shared-library build emits a native library, generated C header, and versioned ABI manifest so C, Rust, and other C-interop hosts consume the same verified contract. This remains design-stage work until the native backend and runtime ABI are implemented. See ADR-0012.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages