Glindo
A parser-combinator library I built to learn functional programming and Gleam.
I started Glindo after watching Scott Wlaschin's talk on parser combinators on YouTube. I wanted to learn functional programming and Gleam, and building a library seemed like a good way to give those ideas somewhere to go. As for the name glindo, it's a combination of Gleam, the language I was learning to build parser combinators with, and Lindo, my partner's last name.
The idea is simple enough: build small parsers, then combine them into bigger ones. Working through that meant getting familiar with mapping, monadic sequencing, and lazy evaluation. Glindo now includes JSON and CSV parsers built from those same pieces, and it's published on Hex with examples on HexDocs.
For a small example, here's the boolean parser from my JSON demo, with just the type it needs:
import gleam/io
import glindo/parsers as p
import glindo/types as t
pub type JsonValue {
JsonBool(Bool)
}
pub fn json_bool() -> t.Parser(JsonValue) {
p.chc_of([
p.map(p.tok(p.prefix_str("true")), fn(_) { JsonBool(True) }),
p.map(p.tok(p.prefix_str("false")), fn(_) { JsonBool(False) }),
])
}
pub fn main() {
p.run(json_bool(), "true") |> io.debug()
}
It tries true, then false, and maps whichever matches into a JsonBool. The result is:
Ok(ParseResult(JsonBool(True), "", 4))
That's the value, the text left over (nothing here), and how far the parser got. The full demo combines these small parsers to handle arrays and objects too. I'm using the current glindo/parsers import here; the demo files still call it glindo/parser.
The number parser in my CSV demo is even smaller. With the same imports, this is the relevant piece:
pub type CSVal {
CSVInt(Int)
}
fn csv_num() -> t.Parser(CSVal) {
use num <- p.map(p.num())
CSVInt(num)
}
Running p.run(csv_num(), "42,hello") gives:
Ok(ParseResult(CSVInt(42), ",hello", 2))
It takes the number and leaves the comma and next field for another parser. That's the bit I like about this approach. Each piece does a small job, and you build up from there.
And here's the talk that got me started: