Glindo is a functional parser-combinator library for Gleam that makes it easy to build powerful, composable parsers for context-free grammars: JSON, CSV, small DSLs, YAML, and more. Glindo uses familiar FP abstractions—functors, monads, lazy evaluation—to thread parsing state and errors in a purely functional way.
-
Core Combinators
map,bind(monadic sequencing)seq_of(sequence),chc_of(choice),chc_opt(greedy choice)mny_of,mny_chc(zero-or-more repetition)opt_of(optional),sep_by(separated lists),btwn(between),peek_fwd,lazy,tok(token with whitespace)
-
Rich Type Safety
Parser(a)wrapsfn(ParserState) -> Result(ParseResult(a), String)ParseResult(a)bundles(res, rem, idx)ParserState(str, idx)tracks the remaining input & position
-
Out-of-the-box Grammars
- JSON: full support for
null,boolean,number,string,array,object - CSV: handles quoted strings (with
""escapes), unquoted fields, line breaks, and trimming
- JSON: full support for
-
Pure-FP Style
- No mutable state—everything is threaded through combinators
- Lazy parsing for recursive grammars
- Distinct error messages
gleam add glindo[dependencies] glindo = ">= 1.0.0"
import glindo/parser.{
map,
btwn,
num,
sat_pred,
tok,
prefix_str,
chc_of,
sep_by
}
import glindo/types.{type Parser}
import glindo/csv
pub fn main() {
// A simple parser naive CSV parser
pub type CSVal {
CSVInt(Int)
CSVStr(String)
CSVBool(Bool)
}
pub type CSVRecord {
Field(line: Int, records: List(CSVal))
}
pub type CSV {
CSV(value: List(CSVRecord))
}
fn csv_quoted_string() -> Parser(CSVal) {
btwn(
tok(prefix_str("\"")),
chc_of([
sat_pred(chr_grab(), fn(x) { x != "\"" }),
map(prefix_str("\"\""), fn(x) { [x] }),
]),
tok(prefix_str("\"")),
)
|> map(string.concat)
|> map(string.trim)
|> map(fn(x) { CSVStr(x) })
}
fn csv_unquoted_string() -> Parser(CSVal) {
chr_grab()
|> sat_pred(fn(x) { x != "," && x != "\r\n" && x != "\n" })
|> map(string.concat)
|> map(string.trim)
|> map(fn(x) { CSVStr(x) })
}
fn csv_string() -> Parser(CSVal) {
[csv_quoted_string(), csv_unquoted_string()]
|> chc_of()
}
fn csv_num() -> Parser(CSVal) {
use num <- map(num())
CSVInt(num)
}
fn filter_csval(val: CSVal) -> Bool {
val != CSVStr("\r\n")
}
fn csv_record() -> Parser(CSVRecord) {
[csv_string(), csv_num()]
|> chc_of()
|> sep_by(prefix_str(","))
|> map(fn(x) { list.filter(x, filter_csval) })
|> map(fn(rec) { Field(1, rec) })
}
fn csv() -> Parser(CSV) {
csv_record()
|> sep_by(
[tok(prefix_str("\r\n")), tok(prefix_str("\n"))]
|> chc_of()
|> tok(),
)
|> map(fn(csv) { CSV(csv) })
}
}| Combinator | Type Signature | Description |
|---|---|---|
map |
(Parser(a), fn(a) -> b) -> Parser(b) |
Transform parsed result |
bind |
(Parser(a), fn(a) -> Parser(b)) -> Parser(b) |
Sequence two parsers |
seq_of |
List(Parser(a)) -> Parser(List(a)) |
Run parsers in order; fail if any fails |
chc_of |
List(Parser(a)) -> Parser(a) |
Try parsers until one succeeds |
chc_opt |
List(Parser(a)) -> Parser(a) |
Greedy choice: pick the one that consumes the most input |
mny_of |
Parser(a) -> Parser(List(a)) |
Zero-or-more repetition |
mny_chc |
List(Parser(a)) -> Parser(List(a)) |
Zero-or-more choice |
opt_of |
Parser(a) -> Parser(Option(a)) |
Optional parser |
sep_by |
(Parser(a), Parser(b)) -> Parser(List(a)) |
Parse a list separated by a delimiter |
btwn |
(Parser(a), Parser(b), Parser(c)) -> Parser(b) |
Between two delimiters |
peek_fwd |
Parser(a) -> Parser(a) |
Look ahead without consuming input |
lazy |
(fn() -> Parser(a)) -> Parser(a) |
Deferred parser for recursion |
tok |
Parser(a) -> Parser(a) |
Skip leading/trailing whitespace around a parser |
See the HexDocs API Reference for full details and examples.
Glindo is under active development. To run tests and play with the library locally:
git clone https://github.com/daniel-shunom/glindo.git
cd glindo
gleam run # Run examples or REPL
gleam test # Execute the test suite- Fork the repository
- Create a feature branch (
git checkout -b my-feature) - Write tests for new functionality
- Submit a pull request
Please follow the Gleam style guide and include documentation comments for any new public API.
If you’d like to reach out, feel free to connect via:
- Email: [email protected]
- LinkedIn: LinkedIn Profile
- GitHub: GitHub Profile
- Twitter: @shunom1
- Website: danielshunom.com