Martin

John C. Martin · Fourth edition

Regular Languages and Regular Expressions

Three of the languages over {a,b}\{a,b\} that we considered in Chapter 2 are:

  • L1L_1, the language of strings ending in aaaa;
  • L2L_2, the language of strings containing either the substring abab or the substring bbabba; and
  • L3L_3, the language {aa,aab}∗{b}\{aa,aab\}^*\{b\}.

Like L3L_3, both L1L_1 and L2L_2 can be expressed by a formula involving the operations of union, concatenation, and Kleene ∗*:

L1={a,b}∗{aa},L2={a,b}∗({ab}∪{bba}){a,b}∗.\begin{aligned} L_1 &= \{a,b\}^*\{aa\}, \\ L_2 &= \{a,b\}^*(\{ab\}\cup\{bba\})\{a,b\}^*. \end{aligned}

Languages that have formulas like these are called regular languages. In this section we give a recursive definition of the set of regular languages over an alphabet Σ\Sigma, and later in this chapter we show that these are precisely the languages that can be accepted by a finite automaton.

Definition 3.1Regular Languages over an Alphabet Σ\Sigma

If Σ\Sigma is an alphabet, the set R\mathcal R of regular languages over Σ\Sigma is defined as follows.

  1. The language ∅\varnothing is an element of R\mathcal R, and for every a∈Σa\in\Sigma, the language {a}\{a\} is in R\mathcal R.
  2. For any two languages L1L_1 and L2L_2 in R\mathcal R, the three languages
L1∪L2,L1L2,L1∗L_1\cup L_2,\qquad L_1L_2,\qquad L_1^*

are elements of R\mathcal R.

The language {Λ}\{\Lambda\} is a regular language over Σ\Sigma, because ∅∗={Λ}\varnothing^*=\{\Lambda\}. If Σ={a,b}\Sigma=\{a,b\}, then L1={a,b}∗{aa}L_1=\{a,b\}^*\{aa\} can be obtained from the definition by starting with the two languages {a}\{a\} and {b}\{b\} and then using the recursive statement in the definition four times: The language {a,b}\{a,b\} is the union {a}∪{b}\{a\}\cup\{b\}; {aa}\{aa\} is the concatenation {a}{a}\{a\}\{a\}; {a,b}∗\{a,b\}^* is obtained by applying the Kleene star operation to {a,b}\{a,b\}; and the final language is the concatenation of {a,b}∗\{a,b\}^* and {aa}\{aa\}.

A regular language over Σ\Sigma has an explicit formula. A regular expression for the language is a slightly more user-friendly formula. The only differences are that in a regular expression, parentheses replace {}\{\} and are omitted whenever the rules of precedence allow it, and the union symbol ∪\cup is replaced by ++. Here are a few examples (see Example 3.5 for a discussion of the last one):

Regular LanguageCorresponding Regular Expression
∅\varnothing∅\varnothing
{Λ}\{\Lambda\}Λ\Lambda
{a,b}∗\{a,b\}^*(a+b)∗(a+b)^*
{aab}∗{a,ab}\{aab\}^*\{a,ab\}(aab)∗(a+ab)(aab)^*(a+ab)
({aa,bb}∪{ab,ba}{aa,bb}∗{ab,ba})∗(\{aa,bb\}\cup\{ab,ba\}\{aa,bb\}^*\{ab,ba\})^*(aa+bb+(ab+ba)(aa+bb)∗(ab+ba))∗(aa+bb+(ab+ba)(aa+bb)^*(ab+ba))^*

When we write a regular expression like Λ\Lambda or aabaab, which contains neither ++ nor ∗* and corresponds to a one-element language, the regular expression looks just like the string it represents. A more general regular expression involving one or both of these operations can't be mistaken for a string; we can think of it as representing the general form of strings in the language. A regular expression describes a regular language, and a regular language can be described by a regular expression.

We say that two regular expressions are equal if the languages they describe are equal. Some regular-expression identities are more obvious than others. The formula

is true because the language corresponding to a∗b∗a^*b^* contains both aa and bb. The formula

is true because the first term on the left side corresponds to the strings in {a,b}∗\{a,b\}^* that contain the substring abab and the second term, b∗a∗b^*a^*, corresponds to the strings that don't.

Example 3.2The Language of Strings in {a,b}∗\{a,b\}^* with an Odd Number of aa's

A string with an odd number of aa's has at least one aa, and the additional aa's can be grouped into pairs. There can be arbitrarily many bb's before the first aa, between any two consecutive aa's, and after the last aa. The expression

is not correct, because it doesn't allow bb's between the second aa in one of the repeating pairs ab∗aab^*a and the first aa in the next pair. One correct regular expression describing the language is

The expression

is also not correct, because it doesn't allow strings with just one aa to end with bb, and the expression

corrects the mistake. Another correct expression is

All of these could also be written with the single aa on the right, as in

Example 3.3The Language of Strings in {a,b}∗\{a,b\}^* Ending with bb and Not Containing aaaa

If a string does not contain the substring aaaa, then every aa in the string either is followed immediately by bb or is the last symbol in the string. If the string ends with bb, then every aa is followed immediately by bb. Therefore, every string in the language LL of strings that end with bb and do not contain aaaa matches the regular expression (b+ab)∗(b+ab)^*. This regular expression does not describe LL, however, because it allows the null string, which does not end with bb. At least one of the two strings bb and abab must occur, and so a regular expression for LL is

Example 3.4Strings in {a,b}∗\{a,b\}^* in Which Both the Number of aa's and the Number of bb's Are Even

One of the regular expressions given in Example 3.2, b∗a(b+ab∗a)∗b^*a(b+ab^*a)^*, describes the language of strings with an odd number of aa's, and the final portion of it, (b+ab∗a)∗(b+ab^*a)^*, describes the language of strings with an even number of aa's. We can interpret the two terms inside the parentheses as representing the two possible ways of adding to the string without changing the parity (the evenness or oddness) of the number of aa's: adding a string that has no aa's, and adding a string that has two aa's. Every string xx with an even number of aa's has a prefix matching one of these two terms, and xx can be decomposed into nonoverlapping substrings that match one of these terms.

Let LL be the subset of {a,b}∗\{a,b\}^* containing the strings xx for which both na(x)n_a(x) and nb(x)n_b(x) are even. Every element of LL has even length. We can use the same approach to find a regular expression for LL, but this time it's sufficient to consider substrings of even length. The easiest way to add a string of even length without changing the parity of the number of aa's or the number of bb's is to add aaaa or bbbb. If a nonnull string x∈Lx\in L does not begin with one of these, then it starts with either abab or baba, and the shortest substring following this that restores the evenness of nan_a and nbn_b must also end with abab or baba, because its length is even and strings of the form aaaa and bbbb don't change the parity of nan_a or nbn_b.

The conclusion is that every nonnull string in LL has a prefix that matches the regular expression

and that a regular expression for LL is

Example 3.5Regular Expressions and Programming Languages

In Example 2.9 we built a finite automaton to carry out a very simple version of lexical analysis: breaking up a part of a computer program into tokens, which are the basic building blocks from which the expressions or statements are constructed. The last two sections of this chapter are devoted to proving that finite automata can accept exactly the same languages that regular expressions can describe, and in this example we construct regular expressions for two classes of tokens.

An identifier in the C programming language is a string of length 1 or more that contains only letters, digits, and underscores ("_\_") and does not begin with a digit. If we use the abbreviations ll for "letter," either uppercase or lowercase, and dd for "digit," then ll stands for the regular expression

and dd for the regular expression

(which has nothing to do with the integer 45), and a regular expression for the language of C identifiers is

Next we look for a regular expression to describe the language of numeric "literals," which typically includes strings such as 1414, +1+1, −12-12, 14.314.3, −.99-.99, 16.16., 3E143\mathrm E14, −1.00E2-1.00\mathrm E2, 4.1E−14.1\mathrm E{-1}, and .3E+2.3\mathrm E{+2}. Let us assume that such an expression may or may not begin with a plus sign or a minus sign; it will contain one or more decimal digits, and possibly a decimal point, and it may or may not end with a subexpression starting with E\mathrm E. If there is such a subexpression, the portion after E\mathrm E may or may not begin with a sign and will contain one or more decimal digits.

Our regular expression will involve the abbreviations dd and ll introduced above, and we will use ss to stand for "sign" (either Λ\Lambda or a plus sign or a minus sign) and pp for a decimal point. It is not hard to convince yourself that a regular expression covering all the possibilities is

In some programming languages, numeric literals are not allowed to contain a decimal point unless there is at least one digit on both sides. A regular expression incorporating this requirement is

Other tokens in a high-level language can also be described by regular expressions, in most cases even simpler than the ones in this example. Lexical analysis is the first phase in compiling a high-level-language program. There are programs called lexical-analyzer generators; the input provided to such a program is a set of regular expressions describing the structure of tokens, and the output produced by the program is a software version of an FA that can be incorporated as a token-recognizing module in a compiler. One of the most widely used lexical-analyzer generators is lex, a tool provided in the Unix operating system. It can be used in many situations that require the processing of structured input, but it is often used together with yacc, another Unix tool. The lexical analyzer produced by lex creates a string of tokens; and the parser produced by yacc, on the basis of grammar rules provided as input, is able to determine the syntactic structure of the token string. (yacc stands for yet another compiler compiler.)

Regular expressions come up in Unix in other ways as well. The Unix text editor allows the user to specify a regular expression and searches for patterns in the text that match it. Other commands such as grep (global regular expression print) and egrep (extended global regular expression print) allow a user to search a file for strings that match a specified regular expression.