Skip to content

Architecture Overview

Devlish compiles human-readable .dvl source files through a multi-stage pipeline into bytecode that runs in a lightweight VM. The entire toolchain is implemented in Rust with zero runtime dependencies.

.dvl source -> Parser -> AST -> Compiler -> Bytecode (.dvlc.json) -> VM

Each stage is independent and inspectable:

  1. Source: Human-readable English-like syntax
  2. Parser: Converts source lines into typed statements
  3. Compiler: Emits bytecode instructions with a constant pool and source map
  4. VM: Executes bytecode with host-provided effects
Terminal window
# Validate source syntax
devlish validate program.dvl
# Compile to bytecode
devlish compile program.dvl --output program.dvlc.json
# Inspect compiled bytecode
devlish disassemble program.dvlc.json
# Execute (from source or bytecode)
devlish run program.dvl --input '{"key": "value"}'
devlish run program.dvlc.json --input '{"key": "value"}'

The Rust workspace contains six crates:

Crate Purpose
devlish_core Parser, compiler, CLI binary. Converts .dvl to bytecode JSON.
devlish_vm Bytecode VM. Executes instructions via the HostEffects trait.
devlish_wasm_runner WASM build of the VM for browser and Node embedding.
devlish_wasm_compiler WASM build of the compiler for in-browser compilation.
devlish_toolrun Command wrapper for token-saving output compression.
devlish_geocivic Domain-specific Energy Community verification tool.

The design goal is that compiled Devlish programs carry no runtime beyond the VM:

  • No Ruby, Python, or Node required to execute
  • The WASM VM runs in any WebAssembly host
  • The native VM runs as a single static binary
  • Bytecode packages are self-contained JSON artifacts

A compiled .dvlc.json file contains:

{
"format": "devlish-bytecode",
"format_version": 0,
"compiler_version": "0.1.0",
"constant_pool": [1000000, "approved", true],
"instructions": [
{"op": "CONST", "dest": "r0", "const": 0},
{"op": "STORE", "name": "threshold", "src": "r0"}
],
"source_map": [
{"line": 1, "source": "threshold equals 1000000"}
],
"manifest": null
}

The same bytecode runs in three contexts:

  • Native CLI: devlish run with full host effects (files, HTTP, services)
  • WASM sandbox: devlish-runtime npm package with restricted effects
  • MCP server: devlish mcp serving tools to LLM clients

When a program uses Import, the compiler searches:

  1. The directory containing the source file
  2. The project root (where devlish.toml lives)
  3. devlish/ and lib/ under the project root
  4. Paths in the DEVLISH_PATH environment variable
  5. ~/.devlish/lib/ standard library path

Imports are resolved and inlined at compile time. The resulting bytecode is self-contained.