Skip to content
Adrien Hubert

Regex, compiled to a state machine.

Type a regular expression and it is drawn as the automaton Ken Thompson described in 1968. Then feed it a string. Instead of trying one path and backing up when it fails, the machine keeps every possible position alive at once, and each character costs at most one pass over the states.

Examples

    live state free move, no character accepting state

    0
    states in the machine
    0
    state visits, NFA
    0
    calls, backtracker
    —
    whole string matches

    Supported: literals, ., |, *, +, ?, parentheses, and \ to escape. The pattern must match the whole string, as if it were wrapped in ^ and $. The backtracker gives up after 2,000,000 calls.

    Note

    Thompson's construction builds the machine from pieces, one per operator. A literal is two states and an arrow. Alternation adds a state that forks into both branches with free moves, and another where they rejoin. Star adds a loop back over its body and a skip around it. Every piece has one way in and one way out, so they snap together, and the machine has at most two states per symbol of the pattern. This drawing merges the exit of one piece with the entry of the next instead of linking them with a free move, which saves a state per concatenation and changes nothing else.

    To run it, start with the entry state plus everything reachable by free moves. For each character, follow every arrow labelled with it from every live state, then add the free moves again. The set can never hold more states than the machine has, so a string of length n against a pattern of length m costs on the order of n times m. The "state visits" counter adds up the size of the live set at each position.

    Most regex engines in common use do something else. Perl, PCRE, Python, Java and JavaScript try one alternative, recurse, and back up on failure. On ordinary patterns that is fast. On (a*)*b against a run of a's with no b, every way of splitting the run between the inner and outer star is a separate path, and there are 2n-1 of them. Twenty-five a's is already more than sixteen million. The NFA on this page handles the same input in 130 state visits. It never holds more than five states, and when the input runs out none of them is the accepting one.

    Backtracking survives because it supports features an automaton cannot express, backreferences above all. Engines that refuse those features, such as Google's RE2, Go's regexp and Rust's regex crate, guarantee linear time. Cloudflare's global outage on 2 July 2019 came from a single firewall rule containing .*(?:.*=.*), which sent a backtracking engine into this kind of blow-up on every core at once.

    Sources

    • Thompson, K. (1968). Programming Techniques: Regular expression search algorithm. Communications of the ACM, 11(6), pages 419-422.
    • Cox, R. (2007). Regular Expression Matching Can Be Simple And Fast. swtch.com/~rsc/regexp/regexp1.html
    • Aho, A. V., Lam, M. S., Sethi, R., Ullman, J. D. (2006). Compilers: Principles, Techniques, and Tools, 2nd edition, section 3.7. The (a|b)*abb example comes from there.
    • Graham-Cumming, J. (2019). Details of the Cloudflare outage on July 2, 2019. blog.cloudflare.com