HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
Engelberg
instaparse
Clojure2013–2026
Engelberg / instaparse

instaparse

Discover this repository through its Hacker News conversations.

28Distinct people
34Mentions
0Explicit recommenders
2.8KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

A public description has not been verified.

Where it is used

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

What people are saying

No recommendation phrase was found in the indexed comments.

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

Also in these discussions

No other repository is repeatedly linked in the same discussions.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 3:56:07 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 year34 dated mentions

Observed mentions: 2013-04-09 16:03:49 UTC to 2026-10-18 18:34:36 UTC.

02462013-01-01 00:00:00 UTC to 2014-01-01 00:00:00 UTC (exclusive): 4 mentions; 2 submissions, 2 comments20132014-01-01 00:00:00 UTC to 2015-01-01 00:00:00 UTC (exclusive): 3 mentions; 1 submissions, 2 comments2015-01-01 00:00:00 UTC to 2016-01-01 00:00:00 UTC (exclusive): 2 mentions; 1 submissions, 1 comments2016-01-01 00:00:00 UTC to 2017-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 4 mentions; 1 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): 6 mentions; 3 submissions, 3 comments20192020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 1 mentions; 0 submissions, 1 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 3 mentions; 0 submissions, 3 comments2024-01-01 00:00:00 UTC to 2025-01-01 00:00:00 UTC (exclusive): 2 mentions; 0 submissions, 2 comments2025-01-01 00:00:00 UTC to 2026-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 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
20132013-01-01 00:00:00 UTC2014-01-01 00:00:00 UTC422
20142014-01-01 00:00:00 UTC2015-01-01 00:00:00 UTC312
20152015-01-01 00:00:00 UTC2016-01-01 00:00:00 UTC211
20162016-01-01 00:00:00 UTC2017-01-01 00:00:00 UTC202
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC413
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 UTC633
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 UTC202
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC101
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC303
20242024-01-01 00:00:00 UTC2025-01-01 00:00:00 UTC202
20252025-01-01 00:00:00 UTC2026-01-01 00:00:00 UTC000
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
Instaparse 1.5.0 What if context-free grammars were as easy to use as regular expressions? Features Instaparse aims to be the simplest way to build parsers in Clojure. Turns standard EBNF or ABNF notation for context-free grammars into an executable parser that takes a string as an input and produces a parse tree for that string. No Grammar Left Behind: Works for any context-free grammar, including left-recursive, right-recursive, and ambiguous grammars. Extends the power of context-free grammars with PEG-like syntax for lookahead and negative lookahead. Supports both of Clojure's most popular tree formats (hiccup and enlive) as output targets. Detailed reporting of parse errors. Optionally produces lazy sequence of all parses (especially useful for diagnosing and debugging ambiguous grammars). "Total parsing" mode where leftover string is embedded in the parse tree. Optional combinator library for building grammars programmatically. Performant. Quickstart Instaparse requires Clojure v1.5.1 or later, or ClojureScript v1.7.28 or later. Add the following line to your leiningen dependencies: [instaparse "1.5.0"] Require instaparse in your namespace header: (ns example.core (:require [instaparse.core :as insta])) Creating your first parser Here's a typical example of a context-free grammar one might see in a textbook on automata and/or parsing. It is a common convention in many textbooks to use the capital letter S to indicate the starting rule, so for this example, we'll follow that convention: S = AB* AB = A B A = 'a'+ B = 'b'+ This looks for alternating runs of 'a' followed by runs of 'b'. So for example "aaaaabbaaabbb" satisfies this grammar. On the other hand, "aaabbbbaa" does not (because the grammar specifies that each run of 'a' must be followed by a run of 'b'). With instaparse, turning this grammar into an executable parser is as simple as typing the grammar in: (def as-and-bs (insta/parser "S = AB* AB = A B A = 'a'+ B = 'b'+")) => (as-and-bs "aaaaabbbaaaabb") [:S [:AB [:A "a" "a" "a" "a" "a"] [:B "b" "b" "b"]] [:AB [:A "a" "a" "a" "a"] [:B "b" "b"]]] At this point, if you know EBNF notation for context-free grammars, you probably know enough to dive in and start playing around. However, instaparse is rich with features, so if you want to know the full scope of what it can do, read on... Tutorial Notation Instaparse supports most of the common notations for context-free grammars. For example, a popular alternative to * is to surround the term with curly braces {}, and a popular alternative to ? is to surround the term with square brackets []. Rules can be specified with =, :, :=, or ::=. Rules can optionally end with ;. Instaparse is very flexible in terms of how you use whitespace (as in Clojure, , is treated as whitespace) and you can liberally use parentheses for grouping. Terminal strings can be enclosed in either single quotes or double quotes (however, since you are writing the grammar specification inside of a Clojure double-quoted string, any use of double-quotes would have to be escaped, therefore single-quotes are easier to read). Newlines are optional; you can put the entire grammar on one line if you desire. In fact, all these notations can be mixed up in the same specification if you want. So here is an equally valid (but messier) way to write out the exact same grammar, just to illustrate the flexibility that you have: (def as-and-bs-alternative (insta/parser "S:={AB} ; AB ::= (A, B) A : \"a\" + ; B ='b' + ;")) Note that regardless of the notation you use in your specification, when you evaluate the parser at the REPL, the rules will be pretty-printed: => as-and-bs-alternative S = AB* AB = A B A = "a"+ B = "b"+ Here's a quick guide to the syntax for defining context-free grammars: CategoryNotationsExample Rule: := ::= =S = A End of rule; . (optional)S = A; Alternation|A | B Concatenationwhitespace or ,A B Grouping()(A | B) C Optional? []A? [A] One or more+A+ Zero or more* {}A* {A} String terminal"" '''a' "a" Regex terminal#"" #''#'a' #"a" EpsilonEpsilon epsilon EPSILON eps ε "" ''S = 'a' S | Epsilon Comment(* *)(* This is a comment *) As is the norm in EBNF notation, concatenation has a higher precedence than alternation, so in the absence of parentheses, something like A B | C D means (A B) | (C D). Input from resource file Parsers can also be built from a specification contained in a file, either locally or on the web. For example, I stored on github a file with a simple grammar to parse text containing a single 'a' surrounded optionally by whitespace. The specification in the file looks like this: S = #"\s*" "a" #"\s*" Building the parser from the URI is easy: (insta/parser "https://gist.github.com/Engelberg/5283346/raw/77e0b1d0cd7388a7ddf43e307804861f49082eb6/SingleA") This provides a convenienent way to share parser specifications over the Internet. You can also use a specification contained in a local resource in your classpath: (insta/parser (clojure.java.io/resource "myparser.bnf")) defparser On ClojureScript, the (def my-parser (insta/parser "...")) use case has the following disadvantages: ClojureScript does not support slurp, so parser cannot automatically read from file paths / URLs. Having to parse a grammar string at runtime can impact the startup performance of an application or webpage. To solve those problems, a macro instaparse.core/defparser is provided that, if given a string for a grammar specification, will parse that as a grammar up front and emit more performant code. ;; Clojure (all versions) and ClojureScript (1.9.198 and above) (:require [instaparse.core :as insta :refer [defparser]]) ;; ClojureScript (prior to 1.9.198) (:require [instaparse.core :as insta :refer-macros [defparser]]) => (time (def p (insta/parser "S = A B; A = 'a'+; B = 'b'+"))) "Elapsed time: 4.368179 msecs" #'user/p => (time (defparser p "S = A B; A = 'a'+; B = 'b'+")) ; the meat of the work happen
Read the full README on GitHub

What Hacker News says

34 sources
My first contact with parser combinators was in a parser generator library: https://github.com/engelberg/instaparse This is (probably weirdly, since I've just learned that the approach in the OP is the original one that introduced parser combinators) the first time I've seen parser combinators where the parsers are just functions like this. If you want to be able to combine parsers and you don't want to write a bunch of mapping/unwrapping code, why not just use combinators over parsers assembled by a parser generator?
In Parser Combinators
This is great, JQ is brilliant. I love JQ so much we implemented a subset of JQ in Clojure so that our users could use it to munge/filter data in our product (JVM and browser based Kafka tooling). One of the most fun coding pieces I've done, though I am a bit odd and I love writing grammars (big shoutout to Instaparse![1]). I learned through my implementation that JQ is a LISP-2[2] which surprised me as it didn't feel obvious from the grammar. [1] https://github.com/Engelberg/instaparse [2] https://github.com/jqlang/jq/wiki/jq-Language-Description#:~....
In jq 1.7
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
You might enjoy checking out Instaparse, a Clojure library. Its project page reads, “What if context-free grammars were as easy to use as regular expressions?” It’s not over-promising, either. I went from never having heard of it before to getting complete and correct parse trees of some ancient JSP Expression Language in about 20 minutes. Most of that time was spent typing in the BNF description that I could find only in an image. https://github.com/Engelberg/instaparse
In Casual Parsing in JavaScript
It's a Clojure(Script) library, but it's still so good it's worth mentioning: Instaparse[0] generates a parser for you from a BNF-y grammar specification. Sample from the readme: (def as-and-bs (insta/parser "S = AB* AB = A B A = 'a'+ B = 'b'+")) => (as-and-bs "aaaaabbbaaaabb") [:S [:AB [:A "a" "a" "a" "a" "a"] [:B "b" "b" "b"]] [:AB [:A "a" "a" "a" "a"] [:B "b" "b"]]] [0]: https://github.com/Engelberg/instaparse
In Parsing in JavaScript: all the tools and libraries you can use
May I ask what kind of criteria you have in mind for being “more generally useful”? Parsers are something I know next to nothing about and hardly ever have to reach for. Is it mostly speed and power? Is the current crop not sufficient to parse any BNF, or is it that people want something to handle more permissive grammars? The only parsing library I’ve played with recently is Instaparse in Clojure, which is apparently GLL. It was a delight to use. I found a PDF of the old version of Apache Expression Language that I needed to interpret, typed it almost verbatim into a quite readable text file, and Instaparse did the rest. It was an exciting experience to have unlocked this embedded DSL with about three lines of code, not counting the grammar definition. https://github.com/Engelberg/instaparse
In Marpa will parse anything that can be written in BNF
A lot of Clojure libraries will be described as Clojure(Script) libraries which means they run on both Java and JS usually with no interface changes here are some examples: https://github.com/tonsky/datascript https://github.com/Engelberg/instaparse https://github.com/wilkerlucio/pathom Interop with hosts is a core feature of the language so it feels natural to reach for it when you need it and it's easy to write code that handles multiple runtimes but 95% of the time maybe more you don't need explicit runtime features
In Clojure: A Lisp that wants to spread
Some of my favorite Clojure libraries in this space: - instaparse: takes EBNF (and other formats) as a string and gives you a parser instantly (!!!) https://github.com/Engelberg/instaparse - re-frame: React-Redux alternative that IMO is much easier to work with https://github.com/day8/re-frame - reagent: React wrapper https://reagent-project.github.io/ - ring https://github.com/ring-clojure/ring - fulcro https://github.com/fulcrologic/fulcro More than any library, though, what keeps bringing me back to Clojure is its incredibly blissful set of persistent data structures. It's a pernicious accident of history that we are taught to accept references to mutable objects as normal, inevitable, and desirable in most CS curricula. It is much easier to develop, debug, and maintain immutable data structures instead, and I consider my good fortune
Read the rest
every time I get to debug something in Clojure rather than in some reference-laden OOP soup.
In Using Clojure for Web Apps

Showing the 15 most recent of 34 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.