AST Visualization
AST visualization
The Glither compiler pipeline parses policy DSL source into a typed core IR. Below are interactive parse-tree visualizations for three languages in the stack.
Mailguard rule (glither.mail dialect)
The canonical mailguard.glith ruleset — a flat mail-policy DSL that drops DMARC-reject mail and quarantines suspicious senders.
#pragma dialect glither.mail ; fold first-match
rule drop_dmarc_reject =
msg | from.domain != dkim.domain
| not sender.allowlisted
| dmarc.policy == reject
=> into dropped (reason = dmarc_reject)
rule quarantine_spoof =
msg | from.domain != dkim.domain
| not sender.allowlisted
=> hold into quarantine
on admin.release into delivered
after 72h into dropped (reason = quarantine_timeout)
The AST shows two top-level children under ruleset: the pragma directive (dialect selection + fold strategy) and the rule_def with its comprehension (collection + predicates) and disposition (terminal action). Each predicate filters the message; when all match, the disposition fires.
Glither rule (Roux core grammar)
A richer rule with a group definition, regex predicates, and a conditional arm. This exercises more of the Roux PEG grammar.
#pragma dialect glither.core
group high_risk = msg | dmarc.policy == reject
| spf.result == fail
rule route_high_risk =
msg | from.domain ~ /(bank|pay|sso)\.(com|org|io)$/
| not recipient.org == from.domain
=> raise clearance to restricted
for recipient.org
, route to admin_hold
when dmarc.policy == reject
after 24h into dropped (reason = unclaimed)
Key differences from the mailguard example:
- A
group_defnode captures the reusablehigh_riskpredicate group regexnodes appear under predicates using the~match operator- Two dispositions are chained: a
graded(ladder climb) and averb_app(route) - A conditional
armattacheswhenandaftertriggers with a fallback terminal
Gleam — the auditable oracle
Gleam functions serve as the "auditable oracle" — a human-readable rendering of the compiled IR that is checked for conformance against the WASM output.
pub type Clearance {
Unrestricted
Restricted
Confidential
Secret
TopSecret
}
pub type Receipt {
Receipt(outcome: Outcome, by: String, ts: Int)
}
pub fn dispose(seg: Segment, lig: Ligands) -> Receipt {
case seg.scope, seg.detail, lig.clearance, lig.lineage.verified {
"security/" <> _, Full, c, True if c >= Restricted ->
seg
|> wrap_to(SecurityCleared)
|> seed
|> receipt(Shadowed, by: "restricted_security_full")
_, _, _, _ ->
receipt(Denied, by: "default_deny")
}
}
The Gleam AST shows the fn_decl for dispose with typed parameters, return type annotation, and a case_expr body with guard patterns and pipeline bodies. Two clauses are always produced: the authorized path and the default-deny fallback — guaranteeing total coverage.
How the AST maps to the compiler pipeline
The parse trees above correspond to the first stage of the Glither compiler pipeline:
source (.glith / .gleam) │ ▼ PEG parse (roux.pest / gleam parser) │ ◀── the AST trees above ▼ lower → typed core IR (Roux IR nodes) │ ▼ infer → phase-annotated IR │ ▼ codegen → WASM component (via Rust) / Gleam package (oracle) │ │ ▼ ▼ component.wasm target/glither-gen/*.gleam
The AST is produced by the PEG parse stage (roux.pest for Glither, or the Gleam parser for oracle code). The lower pass transforms the concrete parse tree into the typed core IR — discarding whitespace and comment nodes, resolving references, and annotating each node with WIT types.