perlreguts (1) Linux Manual Page
NAME
perlreguts – Description of the Perl regular expression engine.
DESCRIPTION
This document is an attempt to shine some light on the guts of the regex engine and how it works. The regex engine represents a significant chunk of the perl codebase, but is relatively poorly understood. This document is a meagre attempt at addressing this situation. It is derived from the author’s experience, comments in the source code, other papers on the regex engine, feedback on the perl5-porters mail list, and no doubt other places as well.
NOTICE! It should be clearly understood that the behavior and structures discussed in this represents the state of the engine as the author understood it at the time of writing. It is NOT an API definition, it is purely an internals guide for those who want to hack the regex engine, or understand how the regex engine works. Readers of this document are expected to understand perl’s regex syntax and its usage in detail. If you want to learn about the basics of Perl’s regular expressions, see perlre. And if you want to replace the regex engine with your own, see perlreapi.
OVERVIEW
A quick note on terms
There is some debate as to whether to say “regexp” or “regex”. In this document we will use the term “regex” unless there is a special reason not to, in which case we will explain why.
When speaking about regexes we need to distinguish between their source code form and their internal form. In this document we will use the term “pattern” when we speak of their textual, source code form, and the term “program” when we speak of their internal representation. These correspond to the terms S-regex and B-regex that Mark Jason Dominus employs in his paper on “Rx” ([1] in “REFERENCES”).
What is a regular expression engine?
A regular expression engine is a program that takes a set of constraints specified in a mini-language, and then applies those constraints to a target string, and determines whether or not the string satisfies the constraints. See perlre for a full definition of the language.
In less grandiose terms, the first part of the job is to turn a pattern into something the computer can efficiently use to find the matching point in the string, and the second part is performing the search itself.
To do this we need to produce a program by parsing the text. We then need to execute the program to find the point in the string that matches. And we need to do the whole thing efficiently.
Structure of a Regexp Program
High Level
Although it is a bit confusing and some people object to the terminology, it is worth taking a look at a comment that has been in regexp.h for years:
This is essentially a linear encoding of a nondeterministic finite-state machine (aka syntax charts or “railroad normal form” in parsing technology).
The term “railroad normal form” is a bit esoteric, with “syntax diagram/charts”, or “railroad diagram/charts” being more common terms. Nevertheless it provides a useful mental image of a regex program: each node can be thought of as a unit of track, with a single entry and in most cases a single exit point (there are pieces of track that fork, but statistically not many), and the whole forms a layout with a single entry and single exit point. The matching process can be thought of as a car that moves along the track, with the particular route through the system being determined by the character read at each possible connector point. A car can fall off the track at any point but it may only proceed as long as it matches the track.
Thus the pattern "/foo(?:\w+|\d+|\s+)bar/" can be thought of as the following chart:
[start] |
<foo> |
+-- -- -+-- -- -+ | | |
<\w +><\d +><\s +> | | |
+-- -- -+-- -- -+ |
<bar> |
[end]
The truth of the matter is that perl’s regular expressions these days are much more complex than this kind of structure, but visualising it this way can help when trying to get your bearings, and it matches the current implementation pretty closely.
To be more precise, we will say that a regex program is an encoding of a graph. Each node in the graph corresponds to part of the original regex pattern, such as a literal string or a branch, and has a pointer to the nodes representing the next component to be matched. Since “node” and “opcode” already have other meanings in the perl source, we will call the nodes in a regex program “regops”.
The program is represented by an array of "regnode" structures, one or more of which represent a single regop of the program. Struct "regnode" is the smallest struct needed, and has a field structure which is shared with all the other larger structures.
The “next” pointers of all regops except "BRANCH" implement concatenation; a “next” pointer with a "BRANCH" on both ends of it is connecting two alternatives. [Here we have one of the subtle syntax dependencies: an individual "BRANCH" (as opposed to a collection of them) is never concatenated with anything because of operator precedence.]
The operand of some types of regop is a literal string; for others, it is a regop leading into a sub-program. In particular, the operand of a "BRANCH" node is the first regop of the branch.
NOTE: As the railroad metaphor suggests, this is not a tree structure: the tail of the branch connects to the thing following the set of "BRANCH"es. It is a like a single line of railway track that splits as it goes into a station or railway yard and rejoins as it comes out the other side.
Regops
The base structure of a regop is defined in regexp.h as follows:
struct regnode {
U8 flags; /* Various purposes, sometimes overridden */
U8 type; /* Opcode value as specified by regnodes.h */
U16 next_off; /* Offset in size regnode */
};
Other larger "regnode"-like structures are defined in regcomp.h. They are almost like subclasses in that they have the same fields as "regnode", with possibly additional fields following in the structure, and in some cases the specific meaning (and name) of some of base fields are overridden. The following is a more complete description.
- "regnode_1"
- "regnode_2"
- "regnode_1" structures have the same header, followed by a single four-byte argument; "regnode_2" structures contain two two-byte arguments instead:
regnode_1 U32 arg1; regnode_2 U16 arg1; U16 arg2; - "regnode_string"
- "regnode_string" structures, used for literal strings, follow the header with a one-byte length and then the string data. Strings are padded on the end with zero bytes so that the total length of the node is a multiple of four bytes:
regnode_string char string[1];
U8 str_len; /* overrides flags */ - "regnode_charclass"
- Bracketed character classes are represented by "regnode_charclass" structures, which have a four-byte argument and then a 32-byte (256-bit) bitmap indicating which characters in the Latin1 range are included in the class.
regnode_charclass U32 arg1;
char bitmap[ANYOF_BITMAP_SIZE];Various flags whose names begin with "ANYOF_" are used for special situations. Above Latin1 matches and things not known until run-time are stored in “Perl’s pprivate structure”.
- "regnode_charclass_posixl"
- There is also a larger form of a char class structure used to represent POSIX char classes under "/l" matching, called "regnode_charclass_posixl" which has an additional 32-bit bitmap indicating which POSIX char classes have been included.
regnode_charclass_posixl U32 arg1;
char bitmap[ANYOF_BITMAP_SIZE];
U32 classflags;
regnodes.h defines an array called "regarglen[]" which gives the size of each opcode in units of "size regnode" (4-byte). A macro is used to calculate the size of an "EXACT" node based on its "str_len" field.
The regops are defined in regnodes.h which is generated from regcomp.sym by regcomp.pl. Currently the maximum possible number of distinct regops is restricted to 256, with about a quarter already used.
A set of macros makes accessing the fields easier and more consistent. These include "OP()", which is used to determine the type of a "regnode"-like structure; "NEXT_OFF()", which is the offset to the next node (more on this later); "ARG()", "ARG1()", "ARG2()", "ARG_SET()", and equivalents for reading and setting the arguments; and "STR_LEN()", "STRING()" and "OPERAND()" for manipulating strings and regop bearing types.
What regop is next?
There are three distinct concepts of “next” in the regex engine, and it is important to keep them clear.
- •
- There is the “next regnode” from a given regnode, a value which is rarely useful except that sometimes it matches up in terms of value with one of the others, and that sometimes the code assumes this to always be so.
- •
- There is the “next regop” from a given regop/regnode. This is the regop physically located after the current one, as determined by the size of the current regop. This is often useful, such as when dumping the structure we use this order to traverse. Sometimes the code assumes that the “next regnode” is the same as the “next regop”, or in other words assumes that the sizeof a given regop type is always going to be one regnode large.
- •
- There is the “regnext” from a given regop. This is the regop which is reached by jumping forward by the value of "NEXT_OFF()", or in a few cases for longer jumps by the "arg1" field of the "regnode_1" structure. The subroutine "regnext()" handles this transparently. This is the logical successor of the node, which in some cases, like that of the "BRANCH" regop, has special meaning.
Process Overview
Broadly speaking, performing a match of a string against a pattern involves the following steps:
- A. Compilation
-
-
- 1. Parsing for size
- 2. Parsing for construction
- 3. Peep-hole optimisation and analysis
- B. Execution
-
-
- 4. Start position and no-match optimisations
- 5. Program execution
Where these steps occur in the actual execution of a perl program is determined by whether the pattern involves interpolating any string variables. If interpolation occurs, then compilation happens at run time. If it does not, then compilation is performed at compile time. (The "/o" modifier changes this, as does "qr//" to a certain extent.) The engine doesn’t really care that much.
Compilation
This code resides primarily in regcomp.c, along with the header files regcomp.h, regexp.h and regnodes.h.
Compilation starts with "pregcomp()", which is mostly an initialisation wrapper which farms work out to two other routines for the heavy lifting: the first is "reg()", which is the start point for parsing; the second, "study_chunk()", is responsible for optimisation.
Initialisation in "pregcomp()" mostly involves the creation and data-filling of a special structure, "RExC_state_t" (defined in regcomp.c). Almost all internally-used routines in regcomp.h take a pointer to one of these structures as their first argument, with the name "pRExC_state". This structure is used to store the compilation state and contains many fields. Likewise there are many macros which operate on this variable: anything that looks like "RExC_xxxx" is a macro that operates on this pointer/structure.
Parsing for size
In this pass the input pattern is parsed in order to calculate how much space is needed for each regop we would need to emit. The size is also used to determine whether long jumps will be required in the program.
This stage is controlled by the macro "SIZE_ONLY" being set.
The parse proceeds pretty much exactly as it does during the construction phase, except that most routines are short-circuited to change the size field "RExC_size" and not do anything else.
Parsing for construction
Once the size of the program has been determined, the pattern is parsed again, but this time for real. Now "SIZE_ONLY" will be false, and the actual construction can occur.
"reg()" is the start of the parse process. It is responsible for parsing an arbitrary chunk of pattern up to either the end of the string, or the first closing parenthesis it encounters in the pattern. This means it can be used to parse the top-level regex, or any section inside of a grouping parenthesis. It also handles the “special parens” that perl’s regexes have. For instance when parsing "/x(?:foo)y/" "reg()" will at one point be called to parse from the “?” symbol up to and including the “)”.
Additionally, "reg()" is responsible for parsing the one or more branches from the pattern, and for “finishing them off” by correctly setting their next pointers. In order to do the parsing, it repeatedly calls out to "regbranch()", which is responsible for handling up to the first "|" symbol it sees.
"regbranch()" in turn calls "regpiece()" which handles “things” followed by a quantifier. In order to parse the “things”, "regatom()" is called. This is the lowest level routine, which parses out constant strings, character classes, and the various special symbols like "$". If "regatom()" encounters a “(” character it in turn calls "reg()".
The routine "regtail()" is called by both "reg()" and "regbranch()" in order to “set the tail pointer” correctly. When executing and we get to the end of a branch, we need to go to the node following the grouping parens. When parsing, however, we don’t know where the end will be until we get there, so when we do we must go back and update the offsets as appropriate. "regtail" is used to make this easier.
A subtlety of the parsing process means that a regex like "/foo/" is originally parsed into an alternation with a single branch. It is only afterwards that the optimiser converts single branch alternations into the simpler form.
Parse Call Graph and a Grammar
The call graph looks like this:
reg() # parse a top level regex, or inside of
# parens
regbranch() # parse a single branch of an alternation
regpiece() # parse a pattern followed by a quantifier
regatom() # parse a simple pattern
regclass() # used to handle a class
reg() # used to handle a parenthesised
# subpattern
....
...
regtail() # finish off the branch
...
regtail() # finish off the branch sequence. Tie each
# branch's tail to the tail of the
# sequence
# (NEW) In Debug mode this is
# regtail_study().
A grammar form might be something like this:
atom : constant | class
quant : '*' | '+' | '?' | '{min,max}'
_branch: piece
| piece _branch
| nothing
branch: _branch
| _branch '|' branch
group : '(' branch ')'
_piece: atom | group
piece : _piece
| _piece quant
Parsing complications
The implication of the above description is that a pattern containing nested parentheses will result in a call graph which cycles through "reg()", "regbranch()", "regpiece()", "regatom()", "reg()", "regbranch()" etc multiple times, until the deepest level of nesting is reached. All the above routines return a pointer to a "regnode", which is usually the last regnode added to the program. However, one complication is that reg() returns NULL for parsing "(?:)" syntax for embedded modifiers, setting the flag "TRYAGAIN". The "TRYAGAIN" propagates upwards until it is captured, in some cases by "regatom()", but otherwise unconditionally by "regbranch()". Hence it will never be returned by "regbranch()" to "reg()". This flag permits patterns such as "(?i)+" to be detected as errors (Quantifier follows nothing in regex; marked by <– HERE in m/(?i)+ <– HERE /).
Another complication is that the representation used for the program differs if it needs to store Unicode, but it’s not always possible to know for sure whether it does until midway through parsing. The Unicode representation for the program is larger, and cannot be matched as efficiently. (See “Unicode and Localisation Support” below for more details as to why.) If the pattern contains literal Unicode, it’s obvious that the program needs to store Unicode. Otherwise, the parser optimistically assumes that the more efficient representation can be used, and starts sizing on this basis. However, if it then encounters something in the pattern which must be stored as Unicode, such as an "
