AST Examples

Worked examples showing how source maps to the AST. For the IR shape of the same constructs, see Worked Examples in the IR reference.

Simple Struct

FormaLang source:

pub struct User {
    name: String,
    age: I32
}

AST structure:

File
└── statements[0]: Statement::Definition
    └── Definition::Struct
        ├── visibility: Public
        ├── name: "User"
        ├── generics: []
        └── fields:
            ├── [0] StructField
            │   ├── mutable: false
            │   ├── name: "name"
            │   ├── ty: Type::Primitive(String)
            │   ├── optional: false
            │   └── default: None
            └── [1] StructField
                ├── mutable: false
                ├── name: "age"
                ├── ty: Type::Primitive(I32)
                ├── optional: false
                └── default: None

Enum with Variants

FormaLang source:

pub enum Status {
    Active,
    Inactive,
    Pending(reason: String)
}

AST structure:

File
└── statements[0]: Statement::Definition
    └── Definition::Enum
        ├── visibility: Public
        ├── name: "Status"
        ├── generics: []
        └── variants:
            ├── [0] EnumVariant
            │   ├── name: "Active"
            │   └── fields: []
            ├── [1] EnumVariant
            │   ├── name: "Inactive"
            │   └── fields: []
            └── [2] EnumVariant
                ├── name: "Pending"
                └── fields:
                    └── [0] FieldDef
                        ├── name: "reason"
                        └── ty: Type::Primitive(String)

Generic Struct with Trait

FormaLang source:

pub trait Container {
    items: [String]
}

pub struct Box<T: Container> {
    content: T,
    label: String?
}

AST structure:

File
├── statements[0]: Statement::Definition
│   └── Definition::Trait
│       ├── visibility: Public
│       ├── name: "Container"
│       ├── generics: []
│       ├── traits: []
│       ├── fields:
│       │   └── [0] FieldDef
│       │       ├── name: "items"
│       │       └── ty: Type::Array(Type::Primitive(String))
│       └── methods: []
│
└── statements[1]: Statement::Definition
    └── Definition::Struct
        ├── visibility: Public
        ├── name: "Box"
        ├── generics:
        │   └── [0] GenericParam
        │       ├── name: "T"
        │       └── constraints:
        │           └── [0] GenericConstraint::Trait { name: "Container", args: [] }
        └── fields:
            ├── [0] StructField
            │   ├── name: "content"
            │   ├── ty: Type::TypeParameter("T")
            │   └── optional: false
            └── [1] StructField
                ├── name: "label"
                ├── ty: Type::Optional(Type::Primitive(String))
                └── optional: true

Impl Block with Functions

FormaLang source:

pub struct Counter {
    count: I32
}

impl Counter {
    fn increment(self) -> I32 {
        self.count + 1
    }

    fn display(self) -> String {
        if self.count > 10 {
            "High"
        } else {
            "Low"
        }
    }
}

AST structure:

File
├── statements[0]: Statement::Definition
│   └── Definition::Struct (Counter)
│
└── statements[1]: Statement::Definition
    └── Definition::Impl
        ├── trait_name: None
        ├── trait_args: []
        ├── name: "Counter"
        ├── generics: []
        └── functions:
            ├── [0] FnDef
            │   ├── name: "increment"
            │   ├── params: [FnParam { convention: Let, external_label: None, name: "self", ty: None }]
            │   ├── return_type: Some(Type::Primitive(I32))
            │   └── body: Expr::BinaryOp { ... }
            └── [1] FnDef
                ├── name: "display"
                ├── params: [FnParam { convention: Let, external_label: None, name: "self", ty: None }]
                ├── return_type: Some(Type::Primitive(String))
                └── body: Expr::IfExpr { ... }

Trait Implementation

FormaLang source:

pub trait Drawable {
    fn draw(self) -> String
}

impl Drawable for Counter {
    fn draw(self) -> String {
        "Counter: " + self.count
    }
}

AST structure:

File
└── statements[1]: Statement::Definition
    └── Definition::Impl
        ├── trait_name: Some("Drawable")
        ├── trait_args: []
        ├── name: "Counter"
        ├── generics: []
        └── functions:
            └── [0] FnDef
                ├── name: "draw"
                ├── params: [FnParam { name: "self", ty: None }]
                ├── return_type: Some(Type::Primitive(String))
                └── body: Expr::BinaryOp { ... }

Match Expression with Wildcard

FormaLang source:

match status {
    .active: Label(text: "Online"),
    .inactive: Label(text: "Offline"),
    _: Label(text: "Unknown")
}

AST structure:

Expr::MatchExpr
├── scrutinee: Expr::Reference { path: ["status"] }
└── arms:
    ├── [0] MatchArm
    │   ├── pattern: Pattern::Variant { name: "active", bindings: [] }
    │   └── body: Expr::Invocation { path: ["Label"], ... }
    ├── [1] MatchArm
    │   ├── pattern: Pattern::Variant { name: "inactive", bindings: [] }
    │   └── body: Expr::Invocation { path: ["Label"], ... }
    └── [2] MatchArm
        ├── pattern: Pattern::Wildcard
        └── body: Expr::Invocation { path: ["Label"], ... }

Block Expression

FormaLang source:

{
    let x = compute_value()
    let y = x * 2
    Result(value: y)
}

AST structure:

Expr::Block
├── statements:
│   ├── [0] BlockStatement::Let
│   │   ├── mutable: false
│   │   ├── pattern: BindingPattern::Simple("x")
│   │   └── value: Expr::Invocation { path: ["compute_value"], ... }
│   └── [1] BlockStatement::Let
│       ├── mutable: false
│       ├── pattern: BindingPattern::Simple("y")
│       └── value: Expr::BinaryOp { left: "x", op: Mul, right: 2 }
└── result: Expr::Invocation { path: ["Result"], args: [("value", "y")] }

For Expression

FormaLang source:

for item in items {
    ListItem(text: item)
}

AST structure:

Expr::ForExpr
├── var: "item"
├── collection: Expr::Reference { path: ["items"] }
└── body: Expr::Invocation
    ├── path: ["ListItem"]
    └── args: [(Some("text"), Expr::Reference { path: ["item"] })]

Closure Expression

FormaLang source:

let add = |x: I32, y: I32| x + y
let scale: mut I32 -> I32 = mut n -> n

AST structure:

Statement::Let                          // let add = ...
├── pattern: BindingPattern::Simple("add")
└── value: Expr::ClosureExpr
    ├── params:
    │   ├── [0] ClosureParam { convention: Let, name: "x", ty: Some(I32) }
    │   └── [1] ClosureParam { convention: Let, name: "y", ty: Some(I32) }
    └── body: Expr::BinaryOp { op: Add, ... }

Statement::Let                          // let scale: mut I32 -> I32 = ...
├── pattern: BindingPattern::Simple("scale")
├── type_annotation: Some(Type::Closure {
│       params: [(Mut, I32)],
│       ret: I32
│   })
└── value: Expr::ClosureExpr
    ├── params:
    │   └── [0] ClosureParam { convention: Mut, name: "n", ty: None }
    └── body: Expr::Reference { path: ["n"] }