Skip to content

Compiler

The compiler translates a Devlish source file into instructions the runtime can execute. It checks supported statement forms, resolves reusable sources, and records where the generated instructions came from so errors can point back to the original program.

This page is for contributors and developers inspecting compiled programs. It introduces the internal statement and instruction representations; you do not need to understand them to run a .dvl file. Read the Architecture Overview for the surrounding execution pipeline.

The parser represents source operations as typed statements. The table groups representative statement kinds; it is an introduction rather than an exhaustive inventory:

Category Statements
I/O Input, Output, FileWrite, FileRead, ReadXlsxCell, ReadPdfText, ReadDocxText
Assignment Assignment, ConditionalAssignment, SetField, Definition
Control flow Branch, ForEach, WhileLoop, UntilLoop, Break, Continue
Error handling TryRecover, Fail, Require
Validation Validate, Assertion, ExportAssertions
HTTP HttpRequest, HttpDownload
Filesystem FileCopy, FileMove, FileMkdir, FileDelete, FileExists, FileStat, FileList, FileGlob
Structure Import, Load, Extract, RespondWith, Checkpoint, Trigger
Collections Append, Pop
Services ServiceCall, Route, Bind

Each statement carries its source line number and original text for diagnostics.

An opcode names a VM operation. The following selection illustrates arithmetic, branching, and host operations; the instruction set also includes model, tool, clock, random, and exact-number operations.

CONST LOAD STORE ADD SUB
MUL DIV AND OR NOT
EQ NEQ LT LTE GT
GTE JUMP JUMP_IF_FALSE PRINT
ASK EXPORT RETURN VALIDATE ASSERT
EXPORT_ASSERTIONS REQUIRE FAIL TRY_PUSH
TRY_POP RESPOND SERVICE_CALL HTTP_REQUEST
READ_FILE WRITE_FILE CALL_BUILTIN SET_FIELD
FOR_EACH CHECKPOINT FILE_COPY FILE_MOVE FILE_MKDIR
FILE_DELETE FILE_EXISTS FILE_STAT FILE_LIST FILE_GLOB

Each instruction is a JSON object with an op field and operands:

{"op": "CONST", "dest": "r0", "const": 0}
{"op": "STORE", "name": "threshold", "src": "r0"}
{"op": "VALIDATE", "target": "amount", "rule": "minimum", "value": "r1"}
{"op": "JUMP_IF_FALSE", "cond": "r2", "target": 15}

Every instruction maps back to its source line for error reporting:

{
"source_map": [
{"line": 1, "source": "threshold equals 1000000"},
{"line": 2, "source": "amount must be at least threshold"},
{"line": 4, "source": "If status equals \"approved\":"}
]
}

When the VM encounters a runtime error, it uses the source map to report the original Devlish line, not a bytecode address.

If a program declares Permissions, Boundaries, or Callers, the compiler extracts them into a manifest field:

Permissions:
Read files from "/data/"
Write files to "/output/"
Callers:
Any MCP client

Compiles to:

{
"manifest": {
"permissions": [
{"kind": "read_file", "scope": "/data/"},
{"kind": "write_file", "scope": "/output/"}
],
"boundaries": [],
"callers": ["Any MCP client"]
}
}

The compiler produces structured diagnostics with line numbers:

line 8: If requires an indented body: If score > 70:

Each diagnostic includes the line number, error message, and the original source text.

Class programs compile each method into a separate instruction stream. The bytecode package includes a methods array. This abbreviated example leaves instruction and source-map arrays empty to show the method metadata:

{
"methods": [
{
"name": "calculate wages",
"ruby_name": "calculate_wages",
"params": ["hours_worked", "hourly_rate"],
"is_private": false,
"instructions": [],
"source_map": []
}
]
}

The --method flag at runtime selects which method to execute.

Inspect compiled bytecode with human-readable output:

Terminal window
devlish disassemble program.dvlc.json
0000 CONST r0, 10 ; line 2: base score equals 10
0001 STORE base_score, r0 ; line 2
0002 CONST r1, 5 ; line 3: bonus score equals 5
0003 STORE bonus_score, r1 ; line 3
0004 LOAD r2, base_score ; line 4
0005 LOAD r3, bonus_score ; line 4
0006 ADD r4, r2, r3 ; line 4: total equals base_score plus bonus_score