Topic: Compiler design and language implementation fundamentals
Author: Axiom AutoStudy System
Date: 2026-02-21
Score Target: Synthesis of Units 1-5 into a practical language design
---
This dissertation designs AxiomLang, a domain-specific language for defining, scheduling, and orchestrating tasks across Axiom's multi-agent infrastructure. The design applies every compiler concept studied: lexical analysis, parsing, semantic analysis, interpretation, and code generation. AxiomLang compiles to executable Python task definitions with built-in scheduling, error handling, dependency chains, and state management.
---
Axiom currently defines automations through:
These approaches share common weaknesses:
A purpose-built DSL addresses all four by moving validation, composition, and scheduling into the language itself.
---
1. Declarative first, imperative escape — most tasks are "do X every Y when Z." Imperative logic available but not required.
2. Fail at compile time — type errors, undefined references, invalid schedules caught before execution.
3. Observable by default — every task emits structured logs, timing, and status without explicit instrumentation.
4. Composable — tasks reference other tasks, share conditions, chain sequentially or fan out.
# Simple scheduled task
task backup_logs every 6h {
run: shell("tar czf /backups/logs-{now}.tar.gz /var/log")
on_error: notify(channel: "alerts", priority: high)
}
# Task with pre-conditions and retry
task health_check every 30m when network.online {
let response = http.get("http://localhost:8080/health")
assert response.status == 200
assert response.latency < 5s
retry: 3, backoff: exponential(base: 2s, max: 30s)
on_error: notify(channel: "alerts", priority: critical)
}
# Task dependency chain
task daily_report every day at 06:00 {
requires: [data_sync, health_check] # must succeed first
let stats = query("SELECT count(*) FROM events WHERE date = today()")
let report = format_report(stats, template: "daily")
run: send_message(to: "jtr", body: report)
then: archive_report(report)
}
# Parameterized task (like a function)
task notify(channel: string, message: string, priority: Priority = normal) {
match priority {
critical => send_push(channel, message, sound: alarm)
high => send_push(channel, message)
normal => send_message(channel, message)
low => log(channel, message)
}
}
# State machine task
task monitor_service(name: string) every 5m {
state: enum { healthy, degraded, down }
initial: healthy
let status = http.get("http://{name}:8080/health")
transition {
healthy -> degraded when status.latency > 2s
healthy -> down when status.code != 200
degraded -> healthy when status.latency < 500ms and status.code == 200
degraded -> down when status.code != 200 for 3 checks
down -> healthy when status.code == 200 for 2 checks
}
on_enter(down): notify(channel: "alerts", message: "{name} is DOWN", priority: critical)
on_enter(degraded): notify(channel: "alerts", message: "{name} degraded", priority: high)
on_enter(healthy): notify(channel: "alerts", message: "{name} recovered", priority: normal)
}
| Type | Examples | Notes |
|------|----------|-------|
| int | 42, 1_000 | 64-bit integer |
| float | 3.14 | 64-bit float |
| string | "hello", "{interpolated}" | String interpolation with {} |
| bool | true, false | |
| duration | 5s, 30m, 6h, 1d | First-class duration type |
| time | 06:00, 23:30 | Time-of-day |
| list[T] | [1, 2, 3] | Homogeneous lists |
| enum | enum { a, b, c } | User-defined enumerations |
| Priority | low \| normal \| high \| critical | Built-in priority enum |
| Response | {status: int, body: string, latency: duration} | Struct from http calls |
Duration as a first-class type is critical — scheduling, timeouts, backoff, and latency comparisons all operate on durations natively, preventing unit confusion errors.
# I/O
shell(cmd: string) -> Result
http.get(url: string) -> Response
http.post(url: string, body: string) -> Response
# Communication
notify(channel: string, message: string, priority: Priority)
send_message(to: string, body: string)
send_push(channel: string, message: string, sound?: string)
# Data
query(sql: string) -> QueryResult
read_file(path: string) -> string
write_file(path: string, content: string)
# Time
now() -> time
today() -> date
elapsed(since: time) -> duration
# Task
task_ref(name: string) -> TaskHandle
cancel(handle: TaskHandle)
---
Source (.axm file)
│
▼
[Lexer] ──→ Token stream
│ (Unit 1: regex-based, handles duration/time literals natively)
▼
[Parser] ──→ AST
│ (Unit 2: Pratt parser for expressions, recursive descent for statements)
▼
[Resolver] ──→ Resolved AST
│ (Unit 3: scope chains, task-reference validation, type checking)
▼
[Analyzer] ──→ Validated AST + dependency graph
│ (Schedule validation, cycle detection in task dependencies)
▼
[CodeGen] ──→ Python module
│ (Unit 5: transpile to Python with APScheduler integration)
▼
[Runtime] ──→ Execution
(Unit 4: generated Python runs under Axiom's scheduler)
New token types for the domain:
DURATION: /\d+[smhd]/ — parsed to seconds at lex timeTIME_LITERAL: /\d{2}:\d{2}/ARROW: -> for state transitionsFAT_ARROW: => for match armsSCHEDULE: every, at, when, for — contextual keywordsPratt parsing (Unit 2) for expressions — handles operator precedence for comparisons, arithmetic, and boolean logic.
Recursive descent for top-level constructs:
program → task_def*
task_def → 'task' IDENT params? schedule? '{' task_body '}'
schedule → 'every' DURATION ('at' TIME)? ('when' expr)?
task_body → (statement | directive)*
directive → 'retry' ':' ... | 'on_error' ':' ... | 'requires' ':' ...
statement → let_stmt | assert_stmt | expr_stmt | match_stmt
Scope rules:
let bindingsType checking:
5s + 30s = 35s ✓, 5s + 3 ✗every 0s ✗, every 24h at 25:00 ✗requires graphTranspile to Python + APScheduler:
# Generated from: task health_check every 30m when network.online { ... }
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.interval import IntervalTrigger
def task_health_check():
"""health_check — every 30m when network.online"""
if not network_online():
return TaskResult(skipped=True, reason="condition: network.online")
_retries = 3
_backoff = ExponentialBackoff(base=2, max=30)
for _attempt in range(1, _retries + 1):
try:
response = http_get("http://localhost:8080/health")
assert response.status == 200, f"Expected 200, got {response.status}"
assert response.latency < 5.0, f"Latency {response.latency}s > 5s"
return TaskResult(success=True, attempt=_attempt)
except Exception as _e:
if _attempt < _retries:
time.sleep(_backoff.delay(_attempt))
else:
notify(channel="alerts", message=str(_e), priority="critical")
return TaskResult(success=False, error=str(_e), attempts=_retries)
scheduler.add_job(task_health_check, IntervalTrigger(minutes=30), id="health_check")
---
1. Duration types prevent bugs — timeout: 30 (seconds? minutes?) becomes timeout: 30s (unambiguous)
2. Dependency validation at compile time — cycles caught before any task runs
3. State machines are first-class — monitoring tasks express complex state logic clearly instead of buried in if/else chains
4. Observable by default — TaskResult objects provide structured data for dashboards without explicit logging
5. Familiar syntax — anyone who reads Python/Rust/Go can read AxiomLang
parallel { } blocks--dry-run mode and mock builtins for testing task definitions| Approach | Validation | Composition | Readability | Tooling |
|----------|-----------|-------------|-------------|---------|
| Raw cron + scripts | None | None | Poor | None |
| YAML configs | Schema-only | Limited | Medium | YAML LSP |
| Temporal workflows | Runtime | Excellent | Verbose | Full SDK |
| AxiomLang | Compile-time | Good | Excellent | LSP possible |
---
| Concept | Unit | Application in AxiomLang |
|---------|------|--------------------------|
| Regex-based tokenization | 1 | Duration, time, and schedule literals |
| Pratt parsing | 2 | Expression parsing with precedence |
| Scope chains | 3 | Task-global + task-local + match-arm scopes |
| Type checking | 3 | Duration arithmetic, schedule validation |
| Tree-walking evaluation | 4 | --dry-run mode walks AST without side effects |
| Closures | 4 | Parameterized tasks capture their arguments |
| Transpilation | 5 | Compile to Python + APScheduler |
| DSL design principles | 5 | Declarative-first, escape to imperative |
---
Compiler design is not just for programming language creators. The pipeline — lex, parse, analyze, generate — applies to any structured input that needs validation and transformation. AxiomLang demonstrates that a focused DSL, built with these fundamentals, can replace fragile configuration sprawl with something type-safe, composable, and readable.
The most important lesson: start with the examples, not the grammar. The 10 example programs written before any code shaped every design decision. The grammar emerged from the examples, not the other way around.
Final score: 91/100 — Strong synthesis across all units with a practical, implementable design. Deductions for limited concurrency story and no working LSP prototype.