Skip to main content

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.

AST node legend:root / modulekeyword / pragmastructure / rulevalue / identaction / dispositionpredicate / guardmetadata / comment

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)
Parse tree: mailguard ruleset
#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)
ruleset
pragmadialect glither.mail ; fold first-matchcompiler directive: dialect + fold strategy
rule_defdrop_dmarc_rejectrule name
comprehensioncollection: msg
predicatefrom.domain != dkim.domainfilter: domain mismatch
predicatenot sender.allowlistednegation: sender not trusted
predicatedmarc.policy == rejectDMARC disposition check
disposition
terminalinto dropped
argsreason = dmarc_rejecttagged argument
click ▼/▶ to collapse/expand AST nodes

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)
Parse tree: Roux-core rule with conditions + arm
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)
ruleset
pragmadialect glither.corebase dialect
group_defhigh_risknamed predicate group
comprehensioncollection: msg
predicatedmarc.policy == reject
predicatespf.result == fail
rule_defroute_high_risk
comprehensioncollection: msg
predicatefrom.domain ~ regex
regex/(bank|pay|sso)\.(com|org|io)$/regex match operator ~
predicatenot recipient.org == from.domainnegated equality
disposition
gradedraise clearance to restricted for recipient.orgladder climb: clearance level
verb_approute to admin_holdsecondary disposition
armconditional trigger
triggerwhen dmarc.policy == rejectguard condition
triggerafter 24hduration timeout
terminalinto dropped (reason = unclaimed)expiration fallback
click ▼/▶ to collapse/expand AST nodes

Key differences from the mailguard example:

  • A group_def node captures the reusable high_risk predicate group
  • regex nodes appear under predicates using the ~ match operator
  • Two dispositions are chained: a graded (ladder climb) and a verb_app (route)
  • A conditional arm attaches when and after triggers 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")
}
}
Parse tree: Gleam oracle function
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")
}
}
module
type_declClearancecustom type (enum)
variantUnrestricted
variantRestricted
variantConfidential
variantSecret
variantTopSecret
fn_decldisposepublic function
paramsseg: Segment, lig: Ligandstyped parameters
return_typeReceipt-> return type
case_exprpattern match on 4-tuple
pattern"security/" <> _, Full, c, True if c >= Restrictedguard: clearance >= Restricted + verified lineage
bodypipeline: wrap_to -> seed -> receiptwraps segment, seeds ascent, emits Shadowed receipt
pattern_, _, _, _fallback wildcard
bodyreceipt(Denied, by: "default_deny")default deny outcome
click ▼/▶ to collapse/expand AST nodes

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 abovelower → 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.