Parsing in JavaScript: all the tools and libraries you can use
tomassetti.me
tomassetti.me
(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/instaparsefunction as_and_bs() { return parse_text("aaaaabbbaaaabb", make_grammar( parse_text("root : (\"a\" SEQ \"b\" SEQ)SEQ OPT", iparse_grammar))) }
The code can be found at https://github.com/FransFaase/ParserWorkshop/
It's based on Haskell's Parsec parser combinator library, and is zero-dependency.
I was convinced to give it a try based on watching the author's YouTube videos "Parser Combinators From Scratch" on "Low Level JavaScript". Enjoyable series - recommended.
Unfortunately my project was not big enough that I'd be able to say anything about performace in scale, though. Being JS based has the advantage that you can (depending on your application needs) sometimes move some of that workload to the client :)
As far as I know, Nearley implements all optimizations published in the literature. Worst case time complexity is cubic for ambiguous grammars, quadratic for unambiguous grammars and linear for grammars suitable for deterministic algorithms. Performance is still going to be worse than constrained parsers that can handle only a subset of context-free grammars such as deterministic LL(1) parsers. Here's what the Parsing Techniques book says:
> If one has the luxury of being in a position to design the grammar oneself, the choice is simple:
> design the grammar to be LL(1) and use a predictive recursive descent parser.
> This can be summarized as: parsing is a problem only if someone else is in charge of the grammar.
If you ever want to parse binary data using JavaScript, I will always recommend the excellent Kaitai Struct project.
- Clojure with ClojureScript
- F# with Fable
- Haskell with ghcjs
- OCaml with js_of_ocaml and ReScript
- Racket with RacketScript
- Scala with Scala.js