Skip to content
dorialangPublic

About

Doria is a compiled programming language for building native applications, command-line tools, services, games, and systems software with expressive syntax, strong static typing, safe defaults, and modern concurrency.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Doria Logo

Doria

A statically checked, natively compiled language for building software you can read and binaries you can trust.


What is Doria?

Doria is a general-purpose systems language built around three commitments: code that reads plainly, safety that is checked at compile time, and performance that is deterministic. It compiles to standalone native executables with deterministic ownership and no tracing garbage collector. Runtime and allocation costs are specified explicitly and verified with workload-specific measurements, while the syntax stays approachable enough to be someone's first language.

The standalone compiler carries its deterministic fast and release native runtime archives with it. It verifies and materializes the selected archive by content identity when linking, so an installed doriac does not depend on a source checkout or Cargo build directory remaining on disk.

function greet(string $name, int $year): string
{
    return "Hello, {$name}! Welcome to {$year}.";
}

function main(): void
{
    let $message = greet("newcomer", 2026);
    echo "{$message}\n";
}

Memory safety, in plain words

Doria's memory model is built on ownership: every value has exactly one owner, and when the owner's scope ends, the value is cleaned up — immediately, deterministically, every time. Sharing is governed by two words:

  • Everything is readonly by default. Passing a value grants the right to look, not to touch.
  • writable grants exclusive access: mutation with a compile-time guarantee that nobody else is watching.
  • take hands ownership over entirely — the signature says so, and the compiler holds everyone to it.

There are no annotations to sprinkle, no sigils to memorize, and no jargon in the diagnostics. When something is wrong, the compiler names the problem, labels every relevant source location, explains the Doria rule, and offers only fixes whose safety is explicit:

Error[E0470]: Value Used After Ownership Transfer

$user was given away here and cannot be used afterward.

Use-after-free, data races, double-frees, null surprises: these are compile errors in Doria, not production incidents.

Construction follows the same rule: every property must be initialized on every normal constructor path before the new object can be observed. Readonly properties are initialized exactly once, writable properties may be changed after their first initialization, and a branch that ends in a fatal panic does not produce a partially initialized object. A constructor may traverse a definitely initialized writable property and mutate the owned child normally; it does not thereby gain a generally writable $this. Independently owned values may initialize owning properties or replace initialized writable owning properties, with the replacement acquired before the previous value is destroyed.

Single class inheritance preserves those guarantees. Classes are closed unless declared open; methods are direct unless declared open function; and every replacement is explicit with override function. Construction runs parent phases before child phases, destruction runs child phases before parents, and parent::member() names the immediate parent implementation directly. Upcasts are implicit and allocation-free, while is and match perform checked hierarchy narrowing. Constructor promotion remains the concise default. A derived constructor uses override string $title to reuse one inherited property and still calls parent::__construct($title) explicitly; it uses parameter string $raw when an input should exist only during construction. Neither form creates hidden duplicate storage, and actual property hiding remains an error. Closed exact values keep the compact one-word class representation; open static types carry dynamic hierarchy identity without adding a header to each object. See docs/class-inheritance.md.

Bare { ... } blocks provide an explicit shorter lifetime boundary when a value or access guard should be cleaned up before the surrounding function continues.

Closures use explicit with capture lists and structural function types. Captures are acquired in written order, function values move as one owned value, and owned captures are released in reverse order when the closure dies. Valid closures execute through the debug interpreter, both native profiles, and the PHP compatibility backend where the surrounding value surface is supported. No-capture closures allocate no environment. Native nonescaping captures use stack storage and escaping environments use one allocation without reference counting; PHP uses explicit compiler-generated carriers, environments, and stable places rather than PHP automatic capture or references as language semantics.

List<T> currently provides map, filter, and writable-accumulator reduce. These methods borrow the source readonly, visit elements in insertion order, accept readonly- or writable-repeatable callbacks, and propagate the callback's exact checked Errors. map may produce owned Move values; filter preserves Copy-or-Cloneable elements. Other collection families do not expose these algorithms. Decision 0134 defines the broader core value contracts.

Every foreach binding has an explicit type. Two-binding foreach makes collection roles explicit without adding tuple or iterator allocation: foreach ($items as int $index => Item $item). List<T> and T[] provide a readonly zero-based int index; dictionaries provide their actual readonly key. Other current iterable families remain value-only. Indexed property-rooted sequences are borrowed once, and existing writable value iteration remains governed separately by exclusive access.

Canonical display contexts produce ordinary reusable strings. Interpolation, string-anchored concatenation, and %s results can be assigned, returned, passed, and stored. Doria still does not implicitly coerce a scalar in ordinary assignment or calls, and it does not add primitive toString methods or casts.

When a graph genuinely needs several owners, Doria provides an explicit escape hatch rather than changing the default model. shared new Node() creates a readonly SharedReference<Node>; writable shared graphs use WritableSharedReference<T> and scoped readonly/writable access objects. The two families are deliberately disjoint, additional ownership is always requested with share(), and weak references break back-reference cycles without keeping the payload alive. Ordinary owned values still pay no reference-counting or runtime access-check cost.

Design principles

  • Contracts are written down. Every parameter is explicitly typed — always. Nothing silently defaults to a dynamic type, and nullability is spelled ?T and enforced.
  • One word, one meaning. use imports. uses composes traits. with captures in closures. No keyword in Doria ever has two jobs.
  • A standard library with one voice. Type-specific operations live on companions such as String::startsWith, while cross-domain capabilities use fully worded free functions such as read_line. Each operation has one canonical spelling.
  • Unicode text with explicit units. $text->length counts what a reader sees as graphemes, $text->byteLength reports UTF-8 storage, and String search, slicing, casing, splitting, and padding use deterministic Unicode rules on every native backend.
  • Honest defaults. Booleans print as true and false. Integer overflow is an error, not a wraparound. Format strings are checked at compile time. Reusable callables declare required checked errors with throws; canonical I/O failures propagate ambiently and remain catchable. The selected program entrypoint infers what escapes it, and try/catch handling remains statically checked.
  • Small language, sharp edges filed off. Where a familiar construct is a known footgun, Doria deliberately does the safer thing instead.
  • Purpose-shaped collections. Insertion-ordered dictionaries and sets sit beside ascending sorted variants, a min-first priority queue, and one deque that handles both FIFO and LIFO workflows. Collection ordering and ownership remain identical across the debug, native, and compatibility backends.
  • Concise initialization without hidden work. Grouped locals such as let writable $red, $green, $blue = 0; evaluate one Copy initializer once, create independent bindings in source order, and never hide cloning, sharing, or a runtime tuple.
  • Nominal choices without object overhead. Unit, backed, and payload enums are inline values with exact case identity. Payloads are Copy only when every field is Copy; otherwise ownership moves with the enum. Backed values are explicit through readonly value, and enums never silently become integers, strings, objects, or display text.
  • Nominal abstraction without object headers. Decision 0134 requires classes to explicitly author implements; concrete generic constraints remain statically dispatched; deliberate interface erasure uses a two-word data/vtable value over the same headerless allocation. Traits use uses and flatten at compile time, never as runtime mixin objects.

Interface requirements preserve receiver mutability, parameter ownership, and checked effects. Conformance is nominal: a class declares its contracts rather than acquiring them through matching method names. Owned interface values retain the concrete object's destruction, while borrowed views retain their source's lifetime and access capability. Error and Displayable use that same model, including shared interface payloads with explicit ownership and leases. See the implementation plan for compiler coverage.

What people build with it

  • Native services and CLI tools — single-binary deployment, measured cold startup, predictable memory.
  • Portable terminal applications — first-class, cross-platform TUI support (Windows, macOS, Linux) with no hand-written escape sequences: terminal games and tools that just run everywhere.
  • Game engines and performance-critical systems — deterministic destruction, fixed-width numerics, statically specialized abstractions, and a safe interop story with native libraries.
  • Native power for PHP applications — Doria libraries compile to packages that PHP code calls like ordinary classes, with generated, type-checked bindings.

Lineage

Doria was created by a PHP developer who wanted compile-time safety and native performance without giving up readable syntax, and it doesn't hide that. If you know PHP, you'll feel at home in minutes; the $variables, the class shapes, the pragmatism all carry over. But familiarity is a doorway, not the destination: Doria is its own language, with its own type system, its own memory model, and its own opinions about what a language owes the people who read code as often as they write it.

match is an exhaustive value expression. It handles enum cases, payload destructuring, exact constants, nullable values, and exact type narrowing while evaluating its input once and executing one arm:

string $message = match ($result) {
    Delivery::Queued($attempt) if $attempt > 1 => "retried",
    Delivery::Queued($attempt) => "queued",
    Delivery::Sent($reference) => "sent {$reference}",
    Delivery::Failed($code, $reason) => "failed {$code}: {$reason}",
};

Open domains use a final default. match (true) provides ordered strict-bool conditions, and full ternary uses the same typing and ownership rules. A guard uses if, runs only after its pattern matches, and may fall through to a later arm. match (take $value) explicitly gives the whole value to the match so a selected Move payload can become an owned arm binding.

when handles branch-local work that must produce one value, while given prepares shared state and strict-bool predicates for if, when, or while:

string $message = given {
    let $ready = serviceIsReady();
    $ready;
} when ($hasWork): string {
    return "ready";
} else {
    return "waiting";
};

Base do ... while and control-flow finally are executable. finally attaches to if, when, while, and do ... while, runs once for each normal or structured exit, and may propagate a checked Error. A failing finalizer replaces the pending nonfatal outcome and destroys any superseded owned payload exactly once; an outer catch may recover, while a sibling catch on the same try cannot. Fatal panic remains abort-only and runs no cleanup.

Doria parses, checks, and executes explicit Error conformance, throw, source-ordered throws, and try/catch/finally. Handled errors use one backend-independent MIR and deterministic cleanup model across the interpreter, Cranelift, LLVM, and the PHP compatibility backend. Text, file, and standard- device I/O expose exactly the ambient checked errors Doria\Std\Io\IoError and Doria\Std\Io\InvalidUtf8Error. They do not require source throws, but remain exact and explicitly catchable. An Error that escapes main is reported as R1000 after cleanup and exits with status 70. Fatal panic remains a distinct cleanup-free status-101 outcome.

Tooling

Official language-server and editor integrations are developed separately in dorialang/doria-language-server, which consumes reusable doriac frontend services without duplicating compiler semantics.

Doria source may declare one file-wide namespace and use individual, aliased, or grouped imports. Qualified names are exact and absolute whenever they contain \; unqualified names resolve through explicit imports, the current namespace, and the documented edition prelude. Package source discovery and include operate at compile time; Doria source loading is never lowered to runtime PHP. doriac accepts a strict versioned JSON build plan containing the selected target, explicit source inventory, package graph, source scopes, namespace mappings, and compiler profile. Every active source is checked as one package compilation graph. Top-level internal declarations remain visible only inside their package; an internal class member remains visible only to its declaring class. The compiler-facing format and CLI forms are documented in docs/build-plan-schema.md.

Doria attributes are typed compiler metadata with no runtime reflection:

#[Attribute]
class Route
{
    function __construct(string $path) {}
}

#[Route(path: "/posts")]
#[Test]
function routeCanBeMatched(): void
{
}

Attribute arguments reuse ordinary named/default argument binding and the bounded constant evaluator. Applying an attribute executes no constructor or other Doria code. #[Test] is metadata for Baton orchestration, while #[PHPExport] remains metadata for the later PHP bridge. Use doriac metadata or doriac metadata --build-plan to inspect deterministic typed metadata; explicit --schema-version 2 adds callable facts while schema 1 remains the default. Schema 3 preserves both older schemas exactly and adds one unified compiler-owned table for #[Test] methods and behavioral tests. The compiler validates the versioned processor protocol but executes no processor and writes no generated source. See docs/attribute-metadata-protocol.md.

Development source may import describe, it, and test from Doria\Std\Test. They are compiler-owned declarations, not runtime registration calls: descriptions are const-evaluated, test bodies become stable generated callables, and Baton discovers both behavioral declarations and #[Test] through metadata schema 3 without parsing Doria. Decision 0129 records the implemented native testing model: scalar, string, collection, dictionary, and checked-Error expectations; bounded failure differences; strict DORIAO4 outcomes; and compiler-fact-based Baton and editor tooling. The Native Testing Foundation is complete.

Baton is Doria's accepted project and package tool. The current executable implementation is the disposable dorialang/baton-php product-contract bootstrap; it is packaged with its own private runtime and does not require users to install PHP or Composer. The production implementation will live in the clean dorialang/baton repository and be written in Doria before the first unsuffixed toolchain release.

Baton preserves exact schema 1 compatibility and supports strict schema 2 projects with local/scoped package identity, binary and library targets, compile-time autoload discovery, and normal path or Git dependencies. Package versions use SemVer, one package identity resolves once, and deterministic JSON Baton.lock files pin exact Git commits without storing credentials, cache locations, or absolute local paths. install, add, remove, update, and fetch share one offline-aware resolver and a global immutable Git cache. Baton emits explicit multi-package build plans for doriac; neither compiled programs nor the compiler search for project source or parse Baton files at runtime.

Baton supports development dependencies, workspaces, graph inspection, tests, processors, generated sources, and project inventory across the compiler and language server. The current PHP bootstrap freezes that product contract while Decision 0124 keeps the Doria-native implementation mandatory before the first unsuffixed toolchain release.

The CLI supports human, concise, and versioned JSON diagnostics. Human and concise output go to stderr; JSON goes to stdout for tools:

doriac check main.doria --diagnostic-format human
doriac check main.doria --diagnostic-format concise
doriac check main.doria --diagnostic-format json

Color is controlled independently with --diagnostic-color auto|always|never; automatic color honors NO_COLOR.

Performance evidence lives in the separate dorialang/benchmarks repository. Its manifest checks committed exact output before timing, keeps Cranelift and LLVM results distinct, retains the raw samples behind its statistics, and records the compiler, build driver, toolchain, commands, and host used for each report. Public comparisons are workload-specific and reproducible rather than broad language rankings.

The benchmark matrix includes deterministic compiler scaling, runtime-subsystem cases, process resources, artifact sizes, and optional profiler evidence. Shared CI compares deterministic structure; controlled runners alone own timing thresholds.

Runtime panics use the same structured diagnostic model and format selection. Built-in panics have stable P codes, a precise Doria source label, an explanation, a Doria-only Call Path, and status 101. doriac run receives native runtime facts over a private structured channel; neither it, the language server, nor the Playground parses rendered terminal prose.

Unhandled checked errors use that same structured model without becoming panics. Human and concise output safely escape untrusted Error messages, JSON preserves the exact logical message, and the first Doria throw or built-in I/O effect site remains the origin. Ordinary closed stdout/stderr pipes retain their separate clean status-0 behavior.


Readable code. Checked contracts. Native binaries.

License

Apache License 2.0. See LICENSE. Third-party components retain their own licenses and notices.

About

Doria is a compiled programming language for building native applications, command-line tools, services, games, and systems software with expressive syntax, strong static typing, safe defaults, and modern concurrency.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages