Re2c
re2c.org
re2c.org
On the other hand, for a small project I wanted to wire up a C library (adns) to a simple parser, but adns lacks bindings to higher-level languages. So I thought, why not go retro and use the classic Unix swiss army knife, lex [•]. And it was actually a lot nicer than I expected, and the results were great.
More recently I used re2c for a C/C++ lexer. Its user interface is fairly similar to lex, but cleaned up for modern tastes – in fact, re2c is easier and more flexible than lex, I think. And like ragel, re2c produces a directly-coded state machine, instead of a table-driven state machine. So the resulting code is (generally speaking) bigger but faster than lex.
I think re2c is a great tool, and its developers are helpful and responsive.
[•] perl was referred to as a unix swiss army chainsaw, because where lex is regexes + C, perl is regexes + scripting, and perl allows you to go from idea to huge mess much faster
But now that I look at the example on the front page: http://www.colm.net/open-source/ragel/
action dgt { printf("DGT: %c\n", fc); }
action dec { printf("DEC: .\n"); }
number = (
[0-9]+ $dgt ( '.' @dec [0-9]+ $dgt )?
I don't like how the dgt and dec actions are "inline" in the regex.This seems to be breaking the key abstraction of regexes, which in my mind is "NON-DETERMINISM for free".
The better mechanism is submatch extraction, which re2c supports, and they even wrote a 2017 paper about - https://arxiv.org/abs/1907.08837 , also see https://re2c.org/
(I don't use re2c submatch extraction in https://www.oilshell.org, but I just used in a brand new program, and it works great. Very easy and natural.)
So I think Ragel is doing something Perl-ish and recasting regular languages as imperative. But that can be asymptotically slower and breaks reasoning by sets, forcing you to reason by stepping through code.
The compiler translates regular grammars with embedded semantic actions to fast C code.
Full disclosure: I am one of the authors.
The aha moment comes when you see how regex syntax compiles down to various configurations of automata. Couple that with the fact that automata are made to be composed together well, and the result is beautiful in a way that you rarely see in production code.
Here's my stab at it in Rust: https://github.com/mattgreen/lexer/tree/master/src/nfa
https://github.com/mike-french/myrex
It is concurrent in all senses: a single match is split into many concurrent traversals of the network; multiple inputs can be matched concurrently within the same network; and string generators can also run concurrently in the network.
That's possible because all state is in the traversal messages, not in the process nodes, and the whole thing runs asynch (non-blocking) in parallel, automatically using all cores in the machine.
> you see how regex syntax compiles down to various configurations of automata
That is Thompson's Construction [1]. The myrex README contains a long description of how regex structures map to small process networks, and how they glue together. The final process network is almost a direct 1-1 representation of the NFA.
[1] Russ Cox has a nice explanation https://swtch.com/~rsc/regexp/regexp1.html
I feel like parsing is common enough that this could be a language feature or a core library making use of higher-level language functionality.
I used an earlier version of re2c for one project since it was a far easier to get started with compared to lex/flex (that iirc also wasn't object local/thread safe in the past).
Perl, Javascript, Java, C#,etc all include regexp engines these days so you can quickly build an abstraction that provides roughly what re2c gives.
One huge benefit of re2c style generators still have however is that they're precompiled and quite efficient compared to generic regexp libraries if performance matters.
If you want something like re2c with the same performance the target language would first require macro or precompiled facilities (iirc there are C++ regexp systems now that are compile-time compiled and should have similar performance to re2c).
Today Windows is the biggest non-POSIX system remaning afaik (apart from Android and consoles) but back in the 80s and 90s there was MacOS, STOS, AmigaOS,etc so saying it was in Unix/POSIX has little connection to the language at large.
Yeah, I think you could build something like that in Zig with comptime.
re2 is Google's safe regexp engine that works as a state-machine and isn't suspectible to exponential runtime attacks that can affect backtracking engines, it trades some performance for guaranteed non-fatal worst case scenarios.
the C# compiled regexps is backtracking (fast by default) JIT compiled searchers so it's extremely fast (on the level of source-compiled matchers).
I implemented an basic DFA based engine like re2 once as an exercise and there was a performance cost (and building it cost a bit) so I wonder if they JIT non backtracking variant also?
There is more to the perf of a regex engine than whether it uses a JIT or not. :)
Many of the curated rebar benchmarks (in the README) do not just list numbers. They also try to contextualize those numbers. (I am only one person though with limited expertise, so they tend to bias toward what I know. But still, it's miles better than what you'll see in most benchmarks IMO.)
And of course, make sure to read the BIAS document.
- Because Ken Thompson was co-inventor of C, and he ALSO brought regular languages to computing. IIRC he basically wrote the first version of grep, and compilers like re2c use the same automata-based methods as the original grep.
https://www.oilshell.org/archive/Thompson-1968.pdf
- Because C is really bad at string processing. Regular languages would be a huge improvement. Looking through the implementation of most shells should convince of you that.
Thompson also wrote the very first Unix shell, of course! https://www.oilshell.org/blog/2021/08/history-trivia.html
--
There are so many weird edge cases for lexing and parsing that I would never dare to do it without machine verification.
On the upside, it generated functions too long to decompile, so that’s a handy feature I guess?
You can definitely use re2c to lex them, including indentation, but it's not the whole solution. You need a parser too.
I use it for everything possible in https://www.oilshell.org, and it's amazing. It really reduces the amount of fiddly C code you need to parse languages, and it drops in anywhere.
Parser generators usually have some downside, but there's not much downside to the lexer generators IMO. It just saves you work. And the regex syntax is better than Perl/Python because literals are consistently quoted.
Lexers can use re2c, but it's not even the whole story. This is good because it means it's "policy free" and you can use it anywhere.
It's of course not beyond the intelectual capabities of users, but just looks kind of silly. Could have been rebranded IMO when new languages were introduced