Proofs: grep
These are the human proofs of grep, at full length.
GREP prints exactly the lines that contain a substring in the language of the expression full
This proof shows: correctness, termination.
Let be the NFA built at line L1, with start state and accepting state , and assume , which is the correctness of the Thompson construction. Fix a line and write for .
A run of on a string is a path of moves that starts in and whose symbol labels, in order, spell , with any number of -moves before, between and after them. Let be the set of the last states of the runs on . Then if and only if . For a set of states, is the set of states that a path of zero or more -moves reaches from ; the procedure with an explicit stack computes exactly this set, as proved for NFA simulation.
Lemma 1. For every string and symbol , .
Proof. A run on reads in exactly one symbol move, from some to some , after the part that reads . That part is a run on ending in , so , and the rest is a path of -moves from . Conversely, a run on ending in , a move on from to and a path of -moves from form a run on .
Lemma 2 (invariant). When the test of line L6 is evaluated after passes of the loop, with
Proof. By induction on . Before the loop, line L3 sets . Assume before pass . Line L8 sets , where is the set of states such that some has a move on to . CLOSE distributes over union, so line L9 gives
and by Lemma 1 the union over is , while . So .
Lemma 3. After line L4 or line L10 in pass (with for line L4), matched is true if and only if some substring with is in .
Proof. By Lemma 2, if and only if for some , that is, if and only if for some .
Theorem. For each line, the loop of lines L6 to L10 stops, and line L11 prints the line if and only if some substring of it is in .
Proof. Each pass increases by 1, and the loop stops when at the latest, so it makes at most passes. Each CLOSE stops because each state is pushed at most once.
If the loop stops with matched true after pass , then by Lemma 3 some substring is in , and the line is printed.
If it stops with matched false, then and matched was false after every pass . By Lemma 3, no substring with is in . These are all the substrings of , so the line is correctly not printed. The lines are handled one after another in order, so the output is the selected lines in order.
Cost. The NFA has at most states and each state has at most two moves. Each pass of the loop handles each state of once at line L8 and pushes each state at most once in CLOSE, so a pass costs , and a line of length costs .