The Glushkov automaton accepts exactly the language of the expression full
This proof shows: correctness, termination.
Let r be a regular expression with m nodes and symbol leaves numbered 1,…,k from left to right, and let rˉ be the marked expression, in which leaf p is the letter p. For a node x, xˉ is the marked subexpression, and pos(x) is the set of positions of the leaves below x. Let h be the homomorphism with h(p)=sym(p).
Termination. The postorder walk visits each node of the finite tree once, and each visit does finitely many set operations on subsets of {1,…,k}. Lines L15 to L18 are finite. So the algorithm stops.
The computed sets. For a node x, let N(x), First(x) and Last(x) be the values set at lines L3 to L14, and let Fol(x) be the set of pairs (p,q) added at line L8 or line L13 while visiting x or a node below x. By the lines of the algorithm,
- Fol(x)=∅ for a leaf x;
- Fol(s∣t)=Fol(s)∪Fol(t);
- Fol(st)=Fol(s)∪Fol(t)∪Last(s)×First(t);
- Fol(s∗)=Fol(s)∪Last(s)×First(s).
Also First(x),Last(x)⊆pos(x) and Fol(x)⊆pos(x)×pos(x), by induction. At the end, Follow(p)={q:(p,q)∈Fol(r)} for p≥1.
Lemma 1. N(x) is true if and only if ε∈L(x).
Proof. By induction on x. Leaves: L(∅) and L(a) do not contain ε, and L(ε) does. Then ε∈L(s∣t) if and only if ε∈L(s) or ε∈L(t); ε∈L(st) if and only if ε∈L(s) and ε∈L(t); and ε∈L(s∗) always. These are lines L6, L9 and L14. □
Lemma 2 (locality). For every node x and every nonempty word u=p1⋯pn over the positions, u∈L(xˉ) if and only if (a) p1∈First(x), (b) pn∈Last(x) and (c) (pi,pi+1)∈Fol(x) for 1≤i<n.
Proof. By induction on x. Call u good for x if (a), (b) and (c) hold.
- x=∅ or x=ε: L(xˉ) has no nonempty word, and First(x)=∅, so no word is good.
- x is the position p: L(xˉ)={p}. A good word has p1=pn=p and, since Fol(x)=∅, length 1; so it is p.
- x=s∣t. If u∈L(sˉ), then u is good for s by induction, hence good for x, since each set of x contains that of s; the same for t. Conversely, let u be good for x. Every pair of Fol(x) lies in pos(s)2 or in pos(t)2, and these sets are disjoint, so by (c) all letters of u lie in pos(s), or all in pos(t). In the first case p1∈First(x)∩pos(s)=First(s), pn∈Last(s), and the pairs lie in Fol(x)∩pos(s)2=Fol(s); so u is good for s and u∈L(sˉ)⊆L(xˉ). The second case is the same.
- x=st. Let u∈L(xˉ), so u=vv′ with v∈L(sˉ) and v′∈L(tˉ). If v=ε, then ε∈L(s), so N(s) holds by Lemma 1 and First(t)⊆First(x); u=v′ is good for t, and hence for x. If v′=ε, the argument is symmetric with N(t) and Last(s)⊆Last(x). If both are nonempty, v is good for s and v′ for t, so p1∈First(s)⊆First(x), pn∈Last(t)⊆Last(x), the pairs inside v and v′ are in Fol(x), and the pair at the junction is in Last(s)×First(t)⊆Fol(x). Conversely, let u be good for x. No pair of Fol(x) goes from pos(t) to pos(s), so u=vv′ where v has its letters in pos(s) and v′ in pos(t). If v=ε: p1∈First(x)∩pos(t), which is not empty only if N(s) holds, and then it is a subset of First(t); also pn∈Last(x)∩pos(t)=Last(t) and the pairs are in Fol(t). So u is good for t, u∈L(tˉ), and ε∈L(s) by Lemma 1, so u∈L(xˉ). If v′=ε, the argument is symmetric. If both are nonempty, v starts in First(x)∩pos(s)=First(s), its pairs are in Fol(x)∩pos(s)2=Fol(s), and its last letter is in Last(s) because the junction pair lies in Fol(x) and goes from pos(s) to pos(t), so it is in Last(s)×First(t). So v is good for s, and in the same way v′ is good for t. By induction, u=vv′∈L(sˉ)L(tˉ)=L(xˉ).
- x=s∗. Let u∈L(xˉ), nonempty. Then u=v1⋯vj with j≥1 and each vl a nonempty word of L(sˉ) (drop the empty factors). Each vl is good for s; the pairs inside the factors are in Fol(s), and each pair at a junction is in Last(s)×First(s). So u is good for x, since First(x)=First(s) and Last(x)=Last(s). Conversely, let u be good for x; all its letters are in pos(s). Cut u between pi and pi+1 whenever (pi,pi+1)∈/Fol(s). Such a pair is in Fol(x)∖Fol(s)⊆Last(s)×First(s), so each piece starts in First(s) (the first piece by (a), the others by the cut), ends in Last(s) (the last piece by (b), the others by the cut), and has all its pairs in Fol(s). So each piece is good for s, is in L(sˉ) by induction, and u∈L(sˉ)+⊆L(xˉ). □
Lemma 3. L(x)=h(L(xˉ)) for every node x.
Proof. h is a homomorphism, so it commutes with union, concatenation and star of languages, and h({p})={sym(p)} for a leaf. Induction on x. □
Theorem. The automaton G returned at line L18 accepts exactly L(r).
Proof. The empty word is accepted if and only if 0∈F, that is, if and only if N(r) holds, which by Lemma 1 means ε∈L(r).
Let w=a1⋯an with n≥1. Every move into a state q reads sym(q), and no move enters state 0. So an accepting run of G on w is a sequence of states 0,p1,…,pn with p1∈Follow(0)=First(r), pi+1∈Follow(pi), that is (pi,pi+1)∈Fol(r), sym(pi)=ai for each i, and pn∈F; since pn=0, this means pn∈Last(r). By Lemma 2, such a sequence exists if and only if some word p1⋯pn∈L(rˉ) has h(p1⋯pn)=w. By Lemma 3, this holds if and only if w∈L(r). ■