Documentation
The official Dryad language documentation. Browse by category or use the sidebar on individual pages for quick navigation between sections.
Overview
1. Introduction and Language Philosophy
Dryad is a dynamically-typed, multi-paradigm programming language inspired by JavaScript/TypeScript, but with low-level features and a robust module system that make it ideal for both automation scripts and more efficient compiled systems. Dryad's design is centered on simplicity and code clarity, offering a pragmatic development experience with a strong design heritage similar to the Rust and C++ ecosystems.
1.2 Distinctive Features & Principles
The language design is governed by the axioms of Clarity over Performance (prioritizing readability before premature optimizations), Syntactic Consistency, and Informative Errors (clear error messages with correction suggestions and call stack traces).
Lexicography
2. Lexical Structure
Dryad's lexical structure is end-to-end Unicode (UTF-8). The alphabet consists of letters [a-zA-Z], digits [0-9], insignificant whitespace (except in literals and template strings), and special characters. The lexer ensures ambiguity-free interpretation.
2.6 Native Directives and Lexical Disambiguation
The `#` character serves two purposes in Dryad. It is used exclusively to indicate Native Directives (when immediately followed by '<' and '>' characters or in the simplified #module format without spaces). In other inappropriate contexts, the lexer reports a Syntax Error (1000-1999 Lexer Errors).
Data Structures
3. Type System
Dryad implements a dynamic type system with optional development-time annotations. Types are associated with runtime values rather than directly with variables. However, the compiler has a strict development mode ('use strict types') that enables extreme static optimizations in Ahead-of-Time (AOT) compilations.
3.5 Strict Type Mode (AOT)
'use strict types' activates the static validator at compile time. With this, the AOT compiler eliminates dynamic type checks at runtime, enables stack allocations for local references (Escape Analysis), resolves method devirtualizations statically, and converts collection loops directly into vectorized SIMD blocks. The compiler employs a three-level gradient inference strategy: static CFG analysis promotes monomorphic variables to native types; user annotations are treated as binding; and genuinely polymorphic bindings fall back to the Variant Enum. This 'Bind Once' approach means type specialization is final at compile time — there is no deoptimization or reoptimization, guaranteeing deterministic performance with zero warm-up.
Expressions
4. Operators & Precedence
Dryad follows formal precedence rules that group operations from left to right (left associativity), except for local assignments and exponentiations. Implicit conversions include automatic 'number' to 'string' concatenation if one of the operands is a character string.
4.6 Control Expressions & Match
The 'match' statement provides robust functional branching through pattern matching. It supports matching against constants (numeric literals, boolean literals, or strings) and the discard wildcard character (_), as well as allowing boolean sentences as structural guards.
Statements
5. Execution Flow
Control statements behave analogously to C++, but with clean, fast-to-parse syntax. It features C-style loops (`for (let i = 0; i < N; i++)`), iterable iteration loops (`for (element in collection)`), and structured error handling with Try-Catch-Finally.
6. Functions & Classes
Regular functions in Dryad are Hoisted (can be invoked in the file before their syntactic declaration) and support optional named parameters and rest collectors (...rest). Dryad classes offer object-oriented programming with single inheritance (extends) and scope control (public, private, and protected).
Architecture
8. Triple Execution Model
Dryad's execution engine is not a single interpreter or compiler — it is a three-layer pipeline where each layer feeds the next with progressively richer type information. Layer 1 (AST Interpreter) walks the syntax tree directly and collects runtime type observations for every variable slot, call site, and branch. Layer 2 (Bytecode VM) consumes the AST and the interpreter's type observations to emit type-specialized opcodes — ADD_F64 instead of ADD_GENERIC when the observed type is f64. Each compiled function carries a Metadata Header with the observed type signatures, allowing subsequent layers to skip type inference. Layer 3 (AOT Compiler) reads the type-annotated bytecode and emits LLVM IR with zero type checks for statically-known paths, falling back to guarded branches (fast path with runtime check) only when types are genuinely ambiguous. This design distributes type intelligence across all three layers: the interpreter observes, the VM annotates, and the AOT consumes.
12. Minimal Runtime Architecture
Dryad's runtime implements in C++ a minimal set of approximately 50 primitive syscalls (intrinsics), enabling the Standard Library to be almost 100% written in pure Dryad, facilitating self-hosting and broad debugging. All raw intrinsic calls are wrapped in safe Dryad functions that validate inputs and manage cleanup — raw intrinsics may only be used within the standard library's boundary layer. The runtime includes a native Buffer type with bounds-checked access, zero-copy I/O operations, and a Hardware Abstraction Layer (HAL) that isolates platform-specific syscall differences behind a uniform interface.
13. Bytecode & Intermediate Representation
The Dryad Bytecode VM is a stack-based virtual machine that compiles AST into linear OpCodes for efficient sequential execution. The Intermediate Representation (IR) sits between bytecode and native assembly — it is register-based (unlike the stack-based bytecode), architecture-independent, and enables classical optimizations before code generation.
14. AOT Compilation Pipeline
The Ahead-of-Time (AOT) compiler transforms Dryad bytecode into native machine code through a multi-stage pipeline that avoids emitting assembly or object files directly — instead it leverages LLVM IR as its backend target. The pipeline: (1) Bytecode → Micro-IR — stack-based opcodes are converted to a register-based 3-address SSA form where each instruction is R1 = R2 op R3, using the Metadata Header to drive type mapping; (2) Micro-IR → LLVM IR — each Micro-IR instruction maps to LLVM IR instructions with native types (double, i32, i8*); (3) LLVM Optimization — LLVM's pass manager applies constant folding, dead code elimination, loop unrolling, SIMD vectorization, and inlining at -O2; (4) Code Generation — LLVM's backend emits object files for ARM64 or x86_64; (5) Linking — the object file is linked against the Dryad runtime library to produce an ELF or PE executable. Every intrinsic syscall has dual entry points: a Fast Path (native C ABI, zero overhead) and a Slow Path (Variant-based, for dynamic fallback). The dryad --analyze tool reports every call site that would use the Slow Path, with suggestions for promoting to Fast Path via type annotations.