AST Design

Luma’s Abstract Syntax Tree (AST) is the backbone of its compiler and interpreter, designed with clarity, extensibility, and simplicity in mind. Every source construct in Luma - variables, expressions, functions, etc. - is represented as a distinct, structured node in the AST.

This chapter dives into the architecture, node types, and design decisions that shape Luma’s internal representation of code.

Design Goals

  • Minimalist and expressive: Only essential node types are included.
  • Easy to walk and transform: Great for compiling to Go or interpreting directly.
  • Readable and debuggable: Useful for REPLs, tools, or static analysis.
  • Modular and composable: Easy to add new language constructs.

AST Node Interface

Every AST node implements the Node interface:

type Node interface {
    NodeType() string
}

This enables polymorphic traversal over all nodes - whether they’re statements, expressions, types, or literals.

Core Node Types

Variable Declaration

type VarDecl struct {
    Immutable bool
    Name      string
    TypeExpr  TypeExpr
    Value     Expression
}

Represents:

lock name: str = "Alice"

Expressions

All expressions implement:

type Expression interface {
    Node
    ExprString() string
}

Key expression types:

Node Type Example
LiteralExpr "hello", 42, true
FunctionCallExpr print(name)
BinaryExpr x + y, a == b
InterpolatedExpr “Hello ${name}”
RangeExpr 1..5, 1..=5
WalkExpr list.walk(i, x) ->
CollectionExpr [1, 2, 3], { "a": 1 }
IndexExpr list[0]

Type Expressions

These describe the declared types:

type TypeExpr interface {
    Node
    TypeName() string
}

Concrete types:

AST Type Luma Syntax Notes
SimpleType int, str Primitive or custom types
OptionalType ?int Nullable
ListType [int] Homogeneous arrays
MapType Maps or sets ({int})

Control Flow Nodes

  • IfStmt - classic conditional logic
  • ReturnStmt - returns from functions
  • AssignStmt - reassigns existing variables

Special Constructs

WalkExpr - Collection iteration

list.walk(index, value) -> {
    print("${index}: ${value}")
}

AST captures:

  • Source expression (list)
  • Parameter names (index, value)
  • Block or inline body

Compiler-Friendly

The AST is designed to be:

  • Easy to compile to Go (direct mapping)
  • Friendly for optimizations and static checks
  • Extendable: new nodes like match, try, or defer can be added without major refactoring

Sample AST Output

For:

age: int = 42
print("Age is ${age}")

AST:

VarDecl: age : int = Literal(42)
ExprStmt: print(Interpolated("Age is ", Expr(age)))

Future Enhancements

  • Position info for better error messages
  • AST annotations (types, scope)
  • Macro or transpiler support
Last updated on