gbf/src/gbf/parser.gleam(view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
import gbf/lexer.{type Position, Position}
import gbf/token.{type Token}
import gleam/list
import gleam/pair
import gleam/result
pub type AST {
/// A single command
///
Leaf(Command)
/// A block with nested children (used for loops)
///
Node(Block)
}
pub type Command =
#(Token, Position)
pub type Block {
Block(children: List(AST), position: Position)
}
pub type Error {
FinishedTooEarly
UnexpectedCommand
UnexpectedBlock
}
/// Parses a list of tokens into an Abstract Syntax Tree.
///
/// Takes a flat list of tokens with their positions and constructs
/// a hierarchical tree structure where loop blocks become nested nodes.
/// All tokens must be consumed for successful parsing.
///
pub fn parse(tokens: List(#(Token, Position))) -> Result(AST, Error) {
let root = Node(Block(children: [], position: Position(0)))
use #(ast, remaining_tokens) <- result.try(parse_tokens(tokens, root))
case remaining_tokens {
[] -> Ok(ast)
_ -> Error(FinishedTooEarly)
}
}
fn parse_tokens(tokens: List(#(Token, Position)), node: AST) {
case tokens {
[] -> Ok(#(node, []))
[token, ..rest] -> {
case token {
#(token.StartBlock, _) -> parse_block(token, rest, node)
#(token.EndBlock, _) -> parse_block_end(rest, node)
_ -> parse_command(token, rest, node)
}
}
}
}
fn parse_block_end(tokens: List(#(Token, Position)), node: AST) {
Ok(#(node, tokens))
}
fn parse_block(token, tokens, node) {
case node {
Leaf(_) -> Error(UnexpectedCommand)
Node(block) -> {
let child_block = Node(Block(children: [], position: pair.second(token)))
use #(parsed_child_block, remaining_tokens) <- result.try(parse_tokens(
tokens,
child_block,
))
let new_children = list.append(block.children, [parsed_child_block])
let new_node =
Node(Block(children: new_children, position: block.position))
parse_tokens(remaining_tokens, new_node)
}
}
}
fn parse_command(
token: #(Token, Position),
tokens: List(#(Token, Position)),
node: AST,
) {
case node {
Leaf(_) -> Error(UnexpectedBlock)
Node(block) -> {
let command = Leaf(token)
let node =
Node(Block(
children: list.append(block.children, [command]),
position: block.position,
))
parse_tokens(tokens, node)
}
}
}
|