Why not to use (f)lex, yacc or bison
tomassetti.me
tomassetti.me
[0] https://github.com/apache/spark/blob/master/sql/catalyst/src...
[1] https://github.com/prestodb/presto/blob/master/presto-parser...
[2] https://github.com/microsoft/TypeScript/blob/master/src/comp...
I wonder if this is relevant to how bad the error messages generally are in Typescript.
- Grammar documentation: https://golang.org/pkg/text/template/
- Code: https://golang.org/src/text/template/parse/lex.go
Edit: His name is Rob Pike
One of the more memorable parsers I’ve worked on was a parser for SPICE netlists. I started out believing that it wasn’t going to be too big of a deal, and ended up sinking a ton of time into it. Ultimately the company (as far as I know) ended up just buying a $40k license to some obscure company that had one, because there was a constant cat-and-mouse game of getting it working right and then discovering a customer who did even more strange stuff that was somehow accepted by the 3rd-party SPICE sim, but wouldn’t be accepted by ours.
I’m assuming that PHP has evolved a ton since I encountered this, but IIRC at one point you couldn’t do `$foo[$baz]($zap)` to call an anonymous function stored in an array, rather you had to `$t = $foo[$baz]; $t($zap)`. At the time (young and naive), I just couldn’t comprehend how a sane parser wouldn’t parse the first form, and then I started looking at how it was implemented...
Basically the key insight is that you can use the parsing automaton state to classify syntax errors, which makes it as easy as falling off a log to produce good messages. The paper originating this technique is Clinton Jeffery's TOPLAS 2003 paper “Generating LR Syntax Error Messages from Examples.” This is what go uses, and OCaml uses a more advanced version of this technique introduced by Francois Pottier in his CC 2016 paper "Reachability and Error Diagnosis in LR(1) Parsers".
https://github.com/golang/go/blob/master/src/go/parser/parse...
https://github.com/golang/go/blob/master/src/go/scanner/scan...
Why did you think it was generated?
Edit: I forked the compiler a while ago to add maybe types (a la Rust result). Looks like the compiler code has changed quite a bit from when I was playing w/ it.
I think it does make sense to write manually parsers for performance and error messages, but it should be clear that this means raising cost by ~10 times. It is worth the effort if you are building a compiler for Java, for example, probably not if you want to process some DSL you developed.
Disclaimer: I am the brother of the author of this article
I have plenty. Publish contact info on your HN profile and I can send some.
* https://github.com/antlr/antlr4/blob/master/doc/faq/general....
* See: "What do you think are the problems people will try to solve with ANTLR4?" question
I've used several different parser generators in the past. But I've also transitioned to hand-rolled recursive decent parsers, being Lazy I've created a library to assist in hand-rolling a recursive decent parser: https://github.com/SAP/chevrotain
To put it another way, I'm rarely parsing data for it to be directly optimized into a machine language. I'm parsing data to extract parts I care about and then work with those parts. The more consistent this process is the easier it is to debug and fix. If my whole parsing/using-the-parsed-data pipeline is in the same language, say Javascript, then I am still only ever debugging the same language and runtime environment (eg: node v12 on whatever \*nix).
In a practical example, YARA[0] is (confusingly) used as both a format[1] and specific implementation[2] for sharing malware detection rules. ClamAV[3] is a popular open source antivirus engine that added YARA-the-format support a few years ago[4]. If you look at their grammar[5] file as well, you can see that one uses GNU Bison 3.0.4 the other uses 3.0.5. One is 3754 lines long, the other is 1849. We can expect these to behave differently. At a certain point, say because of how regular expressions are handled[6], it becomes easier to maintain your own parser than to deal with quirks/whims of someone else's implementation (generated or not).
[0]: https://en.wikipedia.org/wiki/YARA
[1]: https://yara.readthedocs.io/en/latest/writingrules.html
[2]: https://github.com/VirusTotal/yara/blob/master/libyara/hex_g...
[4]: https://blog.clamav.net/2015/06/clamav-099b-meets-yara.html
[5]: https://github.com/Cisco-Talos/clamav-devel/blob/898c08f08b5...
[6]: "In previous versions of YARA, external libraries like PCRE and RE2 were used to perform regular expression matching, but starting with version 2.0 YARA uses its own regular expression engine. This new engine implements most features found in PCRE, except a few of them" from https://yara.readthedocs.io/en/latest/writingrules.html#regu... ; if the regular expression grammar and/or symbol set isn't consistent the things parsing the files won't necessarily be either
https://github.com/cockroachdb/cockroach/tree/master/pkg/sql...
Be sure that your language will parse. It seems stupid to sit down and start designing constructs and not worry how they will fit together. You can get a language that's difficult if not impossible to parse, not only for a computer, but for a person. I use YACC constantly as a check of all my language designs, but I very seldom use YACC in the implementation. I use it as a tester, to be sure that it's LR(1) ... because if a language is LR(1) it's more likely that a person can deal with it.
I would argue that parser-generators are what made those projects to be started and prosper in the first place. Then once one is successful a custom solution could make sense but in my experience it is more expensive and potentially less maintanable, unless you know very welll your way around parsers and language tooling
You know, there are over 2M persons who read our articles. A few hundred also bought a book or a video-course from us, but the vast majority just got some information from free, and we like it in this way. I do not think we got so many persons interested in what we do by lying.
If you have a different professional experience I would happy to learn from it.
Let's start the professional experience with another person's view:
https://research.swtch.com/yyerror
"Seibel: And are there development tools that just make you happy to program?
Thompson: I love yacc. I just love yacc. It just does exactly what you want done. Its complement, lex, is horrible. It does nothing you want done.
Seibel: Do you use it anyway or do you write your lexers by hand?
Thompson: I write my lexers by hand. Much easier."
I happen to like both bison and flex, which are relatively easy to use and bug-free in my experience. Yet your article spreads hundreds of lines of FUD about these tools, a strategy that many ANTLR people use.
I have used ANTLR. It is not intuitive, the documentation is horrible, if you happen to find some advice on Stackoverflow it is likely to be for another one of the incompatible versions.
I suppose if you use ANTLR long enough, these problems go away. But bison or Menhir don't have these problems in the first place.
I do have the ANTLR book, but to be honest, if I would have the task of doing something more involved and where integration isn't as important (i.e. it could be an isolated command, not a module for huge Java/C# app), I'd probably be more inclined to get deeper into SML/Ocaml than ANTLR for this.
Generally parser generator adds a layer of complexity/constraint which may be significant if you need full control of lexing, parsing, semantic processing, error recovery of your language.
After working on a few language projects (including core language, web-based editor with syntax coloring and auto-completion) myself I would strongly suggest hand-rolled parser for any serous language endeavour(general purpose or DSL).
From trying to understand parsing and RDPs I think I don't have the part of the brain required to understand it.
Not that it isn't simple, it is. But it seems examples (as usual) overexplain the simple things then overlook something that seems obvious but isn't.
The only time I managed to write a parser for simple math that wasn't an example was through the use of 'reverse production' parsing. Yes, it's the worse way, but it worked for me (this was a long time ago though)
I manged to follow it when i was 14, when i found it on one of the newsgroups.
https://compilers.iecc.com/crenshaw/
here is port of code to C https://github.com/lotabout/Let-s-build-a-compiler
The weak point is error recovering, in my opinion. While ANTLR offers a sort-of-decent error recovery strategy for free, one has to customize it if they want to get great error messages.
I think that many maintainability issues are due to poor usage of ANTLR. What we do is to: 1) Limit semantic actions 2) For complex languages do tree-transformations, after parsing
With this approach we got pretty decent results.
Personally I find hand-rolled parsers too costly to build and harder to maintain, but I have limited experience with them. I guess it also depends on the context: if you are designing a DSL while writing the parser you want to be able to evolve it very quickly and in that context I think that a parser generator is very useful. If instead I would need to build an industrial grade parser for a general purpose language, having a large budget, then I would go for an hand-rolled parser
Recently we had requirements to come up with a way of providing on-the-fly validation in a web editor. This was only possible by ditching the old implementation and re-writing the grammar using ANTLR. While the old implementation is unmaintainable and (probably, who knows?) buggy, the ANTLR implementation is trivial to work on, test and add new features to.
If you're working with a limited time budget, which is common when your main job isn't to maintain the language, then parser generators such as ANTLR are a godsend. ANTLR even enables you to generate parsers in different languages depending on where it needs to be executed. Need something to run client-side, in the users browser? Generate a JavaScript parser and you're done. Need it to run in the Java backend? Generate it in Java and call it a day.
While it's true that error handling isn't the best, it's already better than nothing, and, as you say, can probably be improved with a bit of customization.
Total nonsense. Bison is very reliable, Flex can be a bit finicky to set up but is also reliable.
PostgreSQL uses bison ...
... rewrites improve code quality ... right ... right???
But every time I take a new look at Antlr there's a huge amount of issues related to the fact that they have a huge new rewrite deprecating old versions while the new version is not ready: entirely new ways of doing things with missing documentation and examples, incomplete or missing language backends, and missing packaging for various Linux distributions.
What I get from it is: Antlr is a very powerful project... if you're in the Java ecosystem and if you're willing to use old no longer supported versions.
Otherwise, questionable choice.
I fire it up, play around a bit, and did a calculator example or something like that. The next step, I figure, is to get it to generate some C code so I can test it on the device. Turns out ANTLR4 has dropped the C backend entirely! C++ is, currently, a no-go for the project, so... I guess I’m out of luck there.
Pleasantly, in a discussion with a friend, he asked the silly question: “couldn’t you parse those strings with sscanf()?”. I blinked in disbelief, wrote the tiniest parser to split the input on newlines, and sscanf() did the trick.
So after years of rolling my own, I finally decided I would never do that again. All the off-by-one errors, difficult-to-diagnose failures and endless fiddling is out of my life now. I just use a parser tool.
https://www.gnu.org/software/bison/manual/html_node/Pure-Cal...
A lot of these are things that Google Docs or Microsoft Word will notice and ask you to change. I highly recommend using one of those to write (especially if English is not your first language) and trying to understand the suggestions it makes. Your articles will come out much more readable to others.
How ironic, right?
(The worth/validity of the underlying ideas is a separate matter.)
Counterpoint 2: Automated grammar corrections are risky unless you're a native speaker. Ironic.
If anything, some American accents are closer to the English of the late 18th century than many British ones are, and the "official" received pronunciation is not without its fair share of critics.
lack of "proper" grammar
or bad grammar
How ironic x 2!- no way to specify operator precedence explicitly (instead, it’s based on ordering of alternatives)
- no explanation of conflicts / ambiguities in the grammar (again, ambiguities are silently resolved based on ordering of alternatives)
- might, or might not, properly handle left recursion (by “handle” I mean, that the parser always halts)
TL;DR: LL parsing is a dead end, don’t use it.
Not sure how maintained it is, spent an afternoon getting the Python version ported over to py3 (which wasn't really all that hard) to learn how it works.
As TFA states you can't reuse grammars for multiple languages because the actions are declared inline but a few of the other complaints aren't an issue due to the way the generated parser does it thing -- quite well designed IMHO.
It comes across very strangely that your brother writes as if y'all are unaware of the existence of parser generators other than ANTLR. This makes me sad. In a fair comparison, ANTLR really is not a great tool; its capabilities are eclipsed by its marketing.
However, to build a realistic and comprehensive compiler, I believe to build it manually is a much better option because it is less error prone, more flexible to tweak around and favours unit testing. Perhaps parser generator is better? I am investigating these kind of tools for work because we need to make our own unique formula implementation. I did use yacc for a school project, and I know its limitation. Since I am busy at my staff so I just let my colleague to make their decision and they decide to use jison. It turns out the product owners want see a much better error message in invalidating the formula, also we need to define our own function so we have to switch to another implementation in the next phrase.
https://www.antlr.org/papers/allstar-techreport.pdf
Has the important detail why it scored good in benchmarks against other parsers:
"7.3 Effect of lookahead DFA on performance
The lookahead DFA cache is critical to ALL(*) performance. To demonstrate the cache’s effect on parsing speed, we disabled the DFA and repeated our Java experiments. Consider the 3.73s parse time from Figure 9 to reparse the Java corpus with pure cache hits. With the lookahead DFA cache disabled completely, the parser took 12 minutes (717.6s)."
I'd still most probably hand-roll, at least when making something for a long-term project, and not some "demo" -- there the stability and ease of maintenance is more important than fast availability of initial results.
(And, really, returning something of type “Any”?)
Why is the tool called ANTLR if it uses an LL algorithm? Did earlier versions use LR, or is it just a confusing name?