Parser combinators in TypeScript, shown by parsing real ping output
Regex gets unreadable on nested formats and tree-sitter is heavy. A short blog series builds a typed middle path from plain functions.
A parser here is a function from a string to a value and the unread remainder. Small ones chain into larger ones with pSeq, pMap and pRepeat, and the typed result is a tuple or object rather than a match array.
The finished example parses the header, per-packet lines and summary of a real ping run into one typed object.
A parser is a function that returns the rest

The article defines Parser<T> as a function from a string to [T, rest]. pInteger matches digits with a regex; pHello checks a literal prefix. Chaining means feeding each parser the leftover string of the previous one.
The generic pieces are small:
pSeqchains any number of parsers and collects results into a typed tuple, using TypeScript generics and variadic types.pMaptransforms a result, for example turning a tuple into a named object.pRepeatapplies a parser until it fails, like regex*.
Because every combinator takes parsers and returns a parser, grammars are built from nested functions. The worked example parses real ping output: header, per-packet lines and summary statistics, into one typed object. The author's argument is that this is easier to read, test and maintain than a large regex for nested formats.
The signature hides failure, and the limits went unmentioned
A commenter who argues elsewhere that parsers should return EitherT for every input:
"…the function isn't well defined on all inputs."
The article's author, who replied in-thread and whose profile links t3.at, confirmed the gap:
"Since we're working in typescript a partial parser can always throw an exception (It's what the parsimmon library does aswell I believe). I decided not to get into those technicalities too much!"
A 52,000-karma regular wanted the costs stated up front:
"They should at least point out the limitations of the approach. For example that they cannot handle left-recursive grammars, and that they tend to backtrack a lot on ambiguous grammars leading to exponentially bad performance."
contificate, whose profile links compiler.club, named a benefit the series skips: continuation-passing parsers get streaming for free.
"So, many of these parsers work well on streams (e.g. TCP sockets) without manually maintaining a state machine - as you may do in C."
Ping output is flat and line-oriented, so it is a safe fit. Before using this on a recursive or ambiguous grammar, decide how failures surface (the article throws) and test the backtracking cost; when a regex passes about 100 characters on a flat format, this pattern is the one to try.
Get breakdowns like this every day
The top Hacker News stories, unpacked — by email, Telegram, or RSS.
A hobbyist 3D printer that positions to 1 micron and deposits 4-micron resin droplets
The µRepRap project wants micron-scale fabrication to be as accessible as RepRap made desktop 3D printing, using common parts and a UV LED under the bed.