The 1960's elegance behind Go's regexp
docs.google.com
docs.google.com
No surprises.
The finite state automata can potentially be exponential in the size of the regular expression you feed it. (Its states are set of states in the NFA.) Subexpression matching is more complex to implement and can make that exponential superexponential. (You have to go from sets of states to ordered sets of states.)
And these catastrophic expressions get much easier when you add in lookaheads, look behinds, and so on.
Mainly, it means it's super awkward — arguably so awkward it's completely impractical, because the result ends up being unreadable and probably slow — to do negative matching, i.e. exclusion.
Regular expression implementation is fun and many interesting things have been done in this area. We are partial to the work of Gonzalo Navarro (in terms of summary papers) and the Glushkov construction, which predates the Thompson NFA construction and IMO is better in a number of ways for fast implementation.
I quite enjoyed the Russ Cox posts, but they are a very partial and idiosyncratic picture of regular expression implementation. RE2 is one point in the automata-style regex implementation space; Hyperscan is another, and there a bunch of other distinct and interesting approaches (e.g. the work from the Parabix guys, various hardware and GPGPU implementations, etc).
Perl 5.8.x was EOL in 2008. 5.10 was EOL in 2009.
The currently maintained versions are 5.22.3 and 5.24.1 and major redesign, refactoring, and rework has been done in the system including the regex parser. It went in the meantime from being a primarily recursive, backtracking NFA to using a DFA when possible, being primarily iterative, and only recursing when necessary.
Anyone intentionally picking a more than decade old version whose major version line was end-of-life nearly a decade ago to compare to their new hotness has completely invalidated the evidence for their arguments. All the great things the slides might say may still be true, but they are unsupported by these charts.
Edit: https://swtch.com/~rsc/regexp/regexp1.html
All the graphics come from the above, and no mention of the original article it was taken from. Nice.
[1] https://github.com/BurntSushi/rure-go [2] https://github.com/rust-lang/regex
Shouldn't that be "if a regex successfully describes a string"? I'm confused.
Consider:
(a+)(a+)=\1
With an input like:
aaaaa=aaThe matcher will find these strings by the time it gets to the '=':
(a)(aaaa)=
(aa)(aaa)=
(aaa)(aa)=
(aaaa)(a)=
Each match will end up as a separate still-existing thread in the Thompson matcher. Instead of pruning them as soon as possible, keep them and recursively match the rest of the string with the back-reference set in turn to each until a match is found.Nothing wrong in making the tradeoff but I didn't see it in the text.
The entire existence of Go is predicated on fetishizing old approaches, unfortunately.
You write that as if Lisp isn't the Philosopher's Stone. :)
Personally, I'd rather read about regexps. We have a word for reading about old techniques where I am from -- we call it "learning".
If our community has a problem in this area, it's that we don't pay enough attention to the old ways.