The Parser
Building the syntax tree
The parser takes tokens from the lexer and figures out how they relate to each other. It builds a tree structure called an Abstract Syntax Tree (AST) that represents the structure of your program.
Why Trees?
Code has hierarchy. In 1 + 2 * 3, the multiplication happens before addition. A flat list of tokens doesn't capture this — we need a tree.
The tree shows: first multiply 2*3, then add 1
The tree structure naturally encodes operator precedence. When we evaluate, we start from the leaves and work up: 2 * 3 = 6, then 1 + 6 = 7.
AST Node Types
The AST has different node types for different language constructs:
Expression Nodes
Things that produce values:
Statement Nodes
Things that do actions:
Parsing a Function
Let's see how this code becomes an AST:
fn greet(name) {
return "Hello " + name
}
How Parsing Works
Bloom uses recursive descent parsing. The parser has a method for each grammar rule, and these methods call each other recursively.
Here's the conceptual flow for parsing an expression like 1 + 2 * 3:
expression()
Entry point
term()
handles + -
factor()
handles * /
primary()
numbers, ids, (expr)
Each level handles operators with the same precedence. Lower precedence operators (+ -) are parsed at higher levels. Higher precedence (* /) is parsed at lower levels. Primaries are the "atoms" at the bottom of recursion.
Operator Precedence
Bloom follows standard mathematical precedence (highest binds tightest):
Parsing a For Loop
Let's trace through parsing this for loop:
for i in 0..5 {
circle(i * 20, 50, 10)
}
Resulting AST:
Error Recovery
When the parser finds an error, it tries to recover and keep going. This lets Bloom show you multiple errors at once instead of stopping at the first one.
let x = // Missing value
let y = 10 // This still parses fine
The parser reports the first error, then synchronizes by skipping to the next statement boundary (usually a newline or semicolon).
Helpful Error Messages
Bloom's parser tries to guess what you meant:
fr i in 0..5 {Error: Unexpected token 'fr'. Did you mean 'for'?
This is done by checking if unknown identifiers are similar to keywords (using edit distance).
In the Source Code
The parser implementation lives in src/lang/parser.ts. Key methods:
parse()— Entry point, returns Program ASTdeclaration()— Parses fn, let, or statementsstatement()— Parses if, for, while, return, etc.expression()— Entry point for expression parsingcall()— Handles function calls and member accessprimary()— Handles literals and identifiers