For example, with respect to the character f, the derivative
of the language for which [[L]] = {foo, frak, bar} is
Df (L) = {oo, rak}. Because foo and frak start with the
character f and bar does not, we keep only foo and frak
and then remove their initial characters, leaving oo and rak.
We repeat this process with each character in the input
until it is exhausted. If, after every derivative has been performed,
the resulting set of words contains the empty word,
then there is some word in the original language consisting
of exactly the input characters, and the language accepts
the input. All f this processing takes place at parse time, so
there is no parser-generation phase.
The specific algorithm they build off (Might 2011) has extended this to memoize repeated derivations of the same arguments and to allow for lazy evaluation in the case of recursion.But this algorithm was implemented in a mind-bendingly-slow way:
For example, they report
that a 31-line Python file took three minutes to parse!
They prove that the worst case is actually cubic time and provide a concise cubic-time implementation in Racket: https://bitbucket.org/ucombinator/derp-3Their implementation is orders of magnitude faster of prior PWD implementations, but still orders of magnitude slower than a Bison parser of the same grammar, which they attribute to language choice (C vs Racket).
Please correct me if I'm wrong about any of this! Summarizing research like this helps me understand it, but that doesn't mean I actually understand it.