Design

The design, in its own words.

The main page says what DominScript is and shows it working. This page is the longer form: the philosophy behind it, the pillars every design choice traces back to, and the seven things you notice in the first hour. Nothing here is required to get started — Getting started is — but if you want to know why the language is shaped the way it is, it is all here.

01 — Philosophy

The distilled result of decades of systems engineering.

DominScript is not an academic language experiment. It's the deliberate distillation of decades of professional work — on-site data collectors, real-time statistics under broadcast pressure, performance-critical clients — into a reusable tool.

Every design choice traces back to a real problem: byte-precise schemas, packed structs, ref-style callbacks, zero-copy boundaries, plugin architecture. The goal is simple: let the engineer think about the system, not fight the scripting language.

In most languages, parsing is something you do because the data isn't yet in the right shape — bytes come in, objects come out, and a tax is paid in between. In DominScript, packed structs and zero-copy blobs mean the data is often already in the right shape; the parse step disappears. (The fastest parse is the parse you don't run.)

And when you genuinely need runtime code generation — REPLs, formula engines, config DSLs — validate-then-eval lets you do it without giving up the static guarantees the rest of the language gives you.

That is the entire goal: the script and the native program do not have to live in two separate worlds.

02 — Design pillars

Keep the language small. Extend the system in C.

That is the design principle everything else follows from, and it explains why plugins carry so much weight here. DominScript does not try to build every library and every special case into itself. The language provides the safe, convenient scripting layer; the performance-critical and platform-specific parts are native plugins — and a hot script function is deliberately easy to move across that line. Three rules keep it honest.

01

Minimal interpreter

The core knows almost nothing on its own. No built-in network, file, graphics, or OS calls. Every capability arrives through a plugin — a platform-specific shared library (.so on Linux, .dll on Windows). Scripts get exactly the powers their loaded plugins grant. Nothing more.

02

Early error detection

Static validation before execution; runtime guards where dynamic checks are unavoidable. The pipeline is preprocessor → lexer → parser → binder → runtime guards: the first four run before a single instruction executes (types, scopes, definite assignment, plugin signatures), the last keeps only what cannot be static — index and range checks. Every diagnostic carries at least file:line.

03

C-translatability

A hot script function should be mechanically and conceptually easy to move into a C plugin. The #define directive, brace blocks, typed signatures, and explicit casts all serve this goal. When performance demands it, the path from script to native is short and predictable.

03 — Seven things you will notice

Seven things you'll notice immediately.

/ 01

Strong static types

i8 i16 i32 i64, unsigned variants, float double, bool, string, blob, plus user schemas. Explicit casts with exact-numeric runtime checks.

/ 02

Definite assignment

The binder traces every code path. Reading an uninitialized variable is a bind-time error, even across branchy while-if-break flow. The runtime never sees a half-set value.

/ 03

Zero-copy at the boundary

A ref blob parameter aliases memory across the plugin / script line — in both directions. A plugin hands bytes to a callback without allocating; a script hands a blob to a plugin without the host copying first. No per-frame allocation, no host-object boxing — it's the same data on both sides. The plugin chooses when to retain.

/ 04

Packed schemas

Byte-precise struct layouts you can lay over a blob, like a C struct over a buffer. Predictable offsets, no padding surprises, native interop without bindings.

/ 05

Four static phases, then runtime guards

Preprocessor, lexer, parser and binder check the script before it runs: includes and macros, tokens, syntax, then types & scopes & plugin ABI compatibility. Runtime guards cover only what cannot be decided statically — range and dynamic type checks at execution. Most error classes are caught before the first instruction runs.

/ 06

Race-free callback state by construction

A single dispatcher thread runs every callback, one at a time — the same model as a JavaScript event loop. Two callbacks never execute concurrently, so script-level state shared between them is safe without you writing a single mutex. No surprise GC pauses, no fire-and-forget concurrency.

/ 07

Validated runtime code

Build a script fragment at runtime, gate it with os.ValidateScript() to catch lexer / parser / binder errors before a single line runs, then os.Eval() it in the caller's own scope. REPLs, formula engines, config DSLs — without the "fingers crossed" pattern.