HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
rust-bakery
nom
Rust2015–2026
rust-bakery / nom

nom

Rust parser combinator framework

31Distinct people
33Mentions
1Explicit recommenders
10.5KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Rust parser combinator framework

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

I now use nom to write my parsers. Once you understand it, it’s simple and parsing complex data becomes a _fun_ puzzle. I recommend it. https://github.com/rust-bakery/nom

speedgoose used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 12:54:37 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year33 dated mentions

Observed mentions: 2015-02-26 19:14:56 UTC to 2026-08-07 09:05:28 UTC.

02462015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 7 mentions; 1 submissions, 6 comments20152016-01-01 00:00:00 UTC to 2017-01-01 00:00:00 UTC (exclusive): 5 mentions; 0 submissions, 5 comments2017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments2018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments20202021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 5 mentions; 0 submissions, 5 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 3 mentions; 1 submissions, 2 comments2026-01-01 00:00:00 UTC to 2027-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments. Incomplete calendar period.2026*

* An incomplete calendar period is marked with an outlined bar. Its count is not directly comparable with a full period.

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC716
20162016-01-01 00:00:00 UTC2017-01-01 00:00:00 UTC505
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC303
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC202
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC101
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC202
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC211
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC505
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC211
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC000
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC312
2026 (partial)2026-01-01 00:00:00 UTC2027-01-01 00:00:00 UTC101

From the project README

An excerpt from the repository, fetched Nov 13, 2026.

Read the project’s introduction
nom, eating data byte by byte nom is a parser combinators library written in Rust. Its goal is to provide tools to build safe parsers without compromising the speed or memory consumption. To that end, it uses extensively Rust's strong typing and memory safety to produce fast and correct parsers, and provides functions, macros and traits to abstract most of the error prone plumbing. nom will happily take a byte out of your files :) Example Documentation Why use nom? Binary format parsers Text format parsers Programming language parsers Streaming formats Parser combinators Technical features Rust version requirements Installation Related projects Parsers written with nom Contributors Example Hexadecimal color parser: use nom::{ bytes::complete::{tag, take_while_m_n}, combinator::map_res, sequence::Tuple, IResult, Parser, }; #[derive(Debug, PartialEq)] pub struct Color { pub red: u8, pub green: u8, pub blue: u8, } fn from_hex(input: &str) -> Result<u8, std::num::ParseIntError> { u8::from_str_radix(input, 16) } fn is_hex_digit(c: char) -> bool { c.is_digit(16) } fn hex_primary(input: &str) -> IResult<&str, u8> { map_res( take_while_m_n(2, 2, is_hex_digit), from_hex ).parse(input) } fn hex_color(input: &str) -> IResult<&str, Color> { let (input, _) = tag("#")(input)?; let (input, (red, green, blue)) = (hex_primary, hex_primary, hex_primary).parse(input)?; Ok((input, Color { red, green, blue })) } fn main() { println!("{:?}", hex_color("#2F14DF")) } #[test] fn parse_color() { assert_eq!( hex_color("#2F14DF"), Ok(( "", Color { red: 47, green: 20, blue: 223, } )) ); } Documentation Reference documentation The Nominomicon: A Guide To Using Nom Various design documents and tutorials List of combinators and their behaviour If you need any help developing your parsers, please ping geal on IRC (Libera, Geeknode, OFTC), go to #nom-parsers on Libera IRC, or on the Gitter chat room. Why use nom If you want to write: Binary format parsers nom was designed to properly parse binary formats from the beginning. Compared to the usual handwritten C parsers, nom parsers are just as fast, free from buffer overflow vulnerabilities, and handle common patterns for you: TLV Bit level parsing Hexadecimal viewer in the debugging macros for easy data analysis Streaming parsers for network formats and huge files Example projects: FLV parser Matroska parser tar parser Text format parsers While nom was made for binary format at first, it soon grew to work just as well with text formats. From line based formats like CSV, to more complex, nested formats such as JSON, nom can manage it, and provides you with useful tools: Fast case insensitive comparison Recognizers for escaped strings Regular expressions can be embedded in nom parsers to represent complex character patterns succinctly Special care has been given to managing non ASCII characters properly Example projects: HTTP proxy TOML parser Programming language parsers While programming language parsers are usually written manually for more flexibility and performance, nom can be (and has been successfully) used as a prototyping parser for a language. nom will get you started quickly with powerful custom error types, that you can leverage with nom_locate to pinpoint the exact line and column of the error. No need for separate tokenizing, lexing and parsing phases: nom can automatically handle whitespace parsing, and construct an AST in place. Example projects: PHP VM xshade shading language Streaming formats While a lot of formats (and the code handling them) assume that they can fit the complete data in memory, there are formats for which we only get a part of the data at once, like network formats, or huge files. nom has been designed for a correct behaviour with partial data: If there is not enough data to decide, nom will tell you it needs more instead of silently returning a wrong result. Whether your data comes entirely or in chunks, the result should be the same. It allows you to build powerful, deterministic state machines for your protocols. Example projects: HTTP proxy Using nom with generators Parser combinators Parser combinators are an approach to parsers that is very different from software like lex and yacc. Instead of writing the grammar in a separate file and generating the corresponding code, you use very small functions with very specific purpose, like "take 5 bytes", or "recognize the word 'HTTP'", and assemble them in meaningful patterns like "recognize 'HTTP', then a space, then a version". The resulting code is small, and looks like the grammar you would have written with other parser approaches. This has a few advantages: The parsers are small and easy to write The parsers components are easy to reuse (if they're general enough, please add them to nom!) The parsers components are easy to test separately (unit tests and property-based tests) The parser combination code looks close to the grammar you would have written You can build partial parsers, specific to the data you need at the moment, and ignore the rest Technical features nom parsers are for: byte-oriented: The basic type is &[u8] and parsers will work as much as possible on byte array slices (but are not limited to them) bit-oriented: nom can address a byte slice as a bit stream string-oriented: The same kind of combinators can apply on UTF-8 strings as well zero-copy: If a parser returns a subset of its input data, it will return a slice of that input, without copying streaming: nom can work on partial data and detect when it needs more data to produce a correct result descriptive errors: The parsers can aggregate a list of error codes with pointers to the incriminated input slice. Those error lists can be pattern matched to provide useful messages. custom error types: You can provide a specific type to improve errors returned by parsers safe par
Read the full README on GitHub

What Hacker News says

33 sources
> Unfortunately there is not much info available [.] Parser combinators are a bit hard to get into, the most helpful resource for me was `nom`'s "Choosing a Combinator" document [1], which is dense but gives you an overview of all the Lego bricks which you can then start imagining how to fit together. I've not really read it, but there's also the original paper on the subject [2] (as linked to by the `parsec` documentation [3]) which describes the nuts and bolts theory behind it. > [Can] you not just rescan the string for the escape sequences, after grabbing the full string? Absolutely, this is just a convenience around that pattern that allows you to express that like: let string = quoted_string.then(escaped(json_string_escapes)).parse(&input)?; Where `escaped` does the rescanning using the parser `json_string_escapes` (which consum
Read the rest
es all the input up to the next escape, if it doesn't start with an escape sequence, or else consumes an escape sequence and returns the transformed text - this API is a little awkward, it may change). And also more generally for any parsers `foo`, `bar`, and `baz` as: let quux = (foo, bar, baz).map().parse(&input)?; [1] https://github.com/rust-bakery/nom/blob/main/doc/choosing_a_... [2] https://web.archive.org/web/20140528151730/http://legacy.cs.... [3] https://hackage.haskell.org/package/parsec
In Ask HN: What are you working on? (February 2025)
And on a related note, this project was for educational purposes, but if the author wants to do more parsing in Rust, there's the excellent `nom` crate [1], which provides a JSON parser as an example [2]. It uses a very similar paradigm to what the author used in the article, and provides a lot of helper utilities. I used it to parse a (very, very small) subset of Markdown recently [3] and enjoyed the experience. [1] https://github.com/rust-bakery/nom [2] https://github.com/rust-bakery/nom/blob/main/examples/json.r... [3] https://git.sr.ht/~bsprague/logseq-to-linkwarden
In Parsing JSON in 500 lines of Rust
Caveats: I've used nom in anger, chumsky hardly at all, and tree-sitter only for prototyping. I'm using it for parsing a DSL, essentially a small programming language. The essential difference between nom/chomsky and tree-sitter is that the former are libraries for constructing parsers out of smaller parsers, whereas tree-sitter takes a grammar specification and produces a parser. This may seem small at first, but is a massive difference in practice. As far as ergonomics go, that's a rather subjective question. On the surface, the parser combinator libraries seem easier to use. They integrate well with the the host language, so you can stay in the same environment. But this comes with a caveat: parser combinators are a functional programming pattern, and Rust is only kind of a functional language, if you treat it juuuuust right. This will
Read the rest
make itself known when your program isn't quite right; I've seen type errors that take up an entire terminal window or more. It's also very difficult to decompose a parser into functions. In the best case, you need to write your functions to be generic over type constraints that are subtle and hard to write. (again, if you get this wrong, the errors are overwhelming) I often give up and just copy the code. I have at times believed that some of these types are impossible to write down in a program (and can only exist in the type inferencer), but I don't know if that's actually true. deep breath Tree-sitter's user interface is rather different. You write your grammar in a javascript internal dsl, which gets run and produces a json file, and then a code generator reads that and produces C source code (I think the codegen is now written in rust). This is a much more roundabout way of getting to a parser, but it's worth it because: (1) tree-sitter was designed for parsing programming languages while nom very clearly was not, and (2) the parsers it generates are REALLY GOOD. Tree-sitter knows operator precedence, where nom cannot do this natively (there's a PR open for the next version: https://github.com/Geal/nom/pull/1362) Tree-sitter's parsing algorithm (GLR) is tolerant to recursion patterns that will send a parser combinator library off into the weeds, unless it uses special transformations to accommodate them. It might sound like I'm shitting on nom here, but that's not the goal. It's a fantastic piece of work, and I've gotten a lot of value from it. But it's not for parsing programming languages. Reach for nom when you want to parse a binary file or protocol. As for chumsky: the fact that it's a parser combinator library in Rust means that it's going to be subject to a lot of the same issues as nom, fundamentally. That's why I'm targeting tree-sitter next. There's no reason tree-sitter grammars couldn't be written in an internal DSL, perhaps in parser-combinator style (https://github.com/engelberg/instaparse does this). That could smooth over a lot of the rough edges.
In Chumsky, a Rust parser-combinator library with error recovery
Interesting! The quote "sometimes, nom can be almost as fast as regex" has been removed from the Nom docs, so it's probably become faster since 2018 when that Cloudcity article was written. In fact, Nom now claims to "outperform many parser combinators library like Parsec and attoparsec, some regular expression engines and even handwritten C parsers" [1]. Someone used Nom for Advent of Code last year and found "The regex approach benchmarked at about 1ms while the parser approach benchmarked at 145 nanoseconds." [2] Maybe I'll try benchmarking Nom vs. regex for a follow-up post. I find regexes are easier than parser combinators for simple tasks. If the problem is small, parser combinators are overkill. But if the problem gets complicated, I think parser combinators are more readable. You can break a parser combinator into small, well-docu
Read the rest
mented, well-tested parts more easily than a big regex. But I still use regex more in my work, because most complex parsing I just throw into serde. [1] https://github.com/Geal/nom [2] https://www.christopherbiscardi.com/advent-of-code-2020-in-r...
In Parsing Text with Nom
Rust has a parser combinator library called Nom [^1] that is really a joy to use. I know a little Rust—enough to know what a lifetime is but not so much that I don’t have to keep referring to the documentation when building something. It took me just a few hours with the docs and examples to get a decent parser together. So nice to work with combinators! [^1]: https://github.com/Geal/nom
In Parser Combinators in Haskell
They are also very similar (basically the Go version was a direct port of the Rust version) so the performance should be very comparable. Sure, but different approaches are going to be more optimal for different languages. I assume by zero-copy you mean that identifiers in the AST are slices of the input file instead of copies? Yes. From the README: zero-copy: if a parser returns a subset of its input data, it will return a slice of that input, without copying Geal also makes claims that nom is faster than hand-written C parsers. It's somewhat complicated because some JavaScript identifiers can technically have escape sequences (e.g. "\u0061bc" is the identifier "abc"), which require dynamic memory allocation anyway. Nom comes with 'escaped' and 'escaped_transform' combinators. In theory it should be possible, with relative ease, to
Read the rest
return a slice if there are no escape characters and an allocated string if expansion is required. Presumably you'd have to use a Cow<str> though. Note that strings aren't slices of the input file because JavaScript strings are UTF-16, not UTF-8, and can have unpaired surrogates. So I represent string contents as arrays of 16-bit integers instead of 8-bit slices (in both Go and Rust). Of course it is. My opinion (which is worth what you've paid for it) is that I'd just go for UTF-8 support. I can't remember the last time I've seen UTF-16 in the wild (thankfully). Performance-wise the other thing that I'd keep in mind with rust is that in debug mode string handling is painfully slow. Edit: here's the URL for nom: https://github.com/Geal/nom
In ESbuild – A fast JavaScript bundler and minifier in Go

Showing the 15 most recent of 33 sources. Enable JavaScript to search and browse every indexed mention.

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.