Ramblings of an aging IT geek
← Ramblings of an aging IT geek
rust

parser combinators finally clicked, thanks to nom

Writing a small config-line parser with nom in Rust, and the moment combinators stopped feeling like magic and started feeling obvious.

Rust source code on a dark editor

I needed to parse a tiny line-based config format and, against my better judgement, reached for nom rather than a regex. Parser combinators had always felt slightly mystical to me, the sort of thing functional programmers got excited about whilst I quietly used split and went home. This week they clicked.

The trick is that a parser in nom is just a function: it takes some input and returns the rest of the input plus whatever it parsed, or an error. Combinators are functions that take parsers and return new, bigger parsers. That's the whole idea. Once I stopped looking for the magic and saw it as "small functions glued into bigger functions", it became almost boring, in the good way.

Here's a parser for key = value lines:

use nom::{
    bytes::complete::{tag, take_while1},
    character::complete::space0,
    sequence::separated_pair,
    IResult,
};

fn ident(input: &str) -> IResult<&str, &str> {
    take_while1(|c: char| c.is_alphanumeric() || c == '_')(input)
}

fn key_value(input: &str) -> IResult<&str, (&str, &str)> {
    separated_pair(
        ident,
        nom::sequence::tuple((space0, tag("="), space0)),
        take_while1(|c: char| c != '\n'),
    )(input)
}

separated_pair runs the key parser, throws away the separator, runs the value parser, and hands you the pair. I didn't write any state machine, any index arithmetic, any "now we're inside a value" boolean. I described the shape of a line and nom turned it into code.

What sold me wasn't the elegance, it was the errors. When my input didn't match, I got back exactly where it stopped and what it expected, not a sullen None. That's the bit a regex never gives you. I'll still reach for split when the format is genuinely two-fields-and-go, but for anything with structure, I'm a convert.