The verification of a while loop has three parts: proving termination; the negative case (when the loop condition is initially false); and the positive or closure case.
Arguing termination is pretty straightforward in this case. The loop always advances the position of <stdin>:
while line != "":
#-- 2 body --
# [ if <stdin> is at end of file ->
# <stdout> := <stdout> + line
# line := ""
# else ->
# <stdout> := <stdout> + line
# <stdin> := <stdin> advanced past the current line
# line := rest of the current line of <stdin> ]
...
Assuming that <stdin> is finite, the program will terminate.
In the negative case, the loop test (line != "") is false, so nothing changes. Here's the overall intended function of the loop:
#-- 2 -- # [ if (line is "" and <stdin> is at EOF) or # (line is nonempty) -> # <stdout> := <stdout> + line + (characters from <stdin> # through end of file) # <stdin> := <stdin> advanced to end of file # line := "" ]
As for <stdin>, we know that it is at end of file by the precondition (either line is nonempty, or <stdin> is at EOF).
As for <stdout>, line is empty by assumption, and since <stdin> is empty, the expression ``<stdout> + line + (characters from <stdin> through end of file)'' simplifies to ``<stdout>''.
So, not changing <stdin> or <stdout> satisfies the overall intended function.
Recall that the proof rule for ``while a do b'' is: ``Does doing b, followed by doing the entire loop, do the overall intended function?''
So, to build the trace table for the closure case, we start with the I/F for the loop body, followed by the I/F for the entire loop (starting from the new state):
#-- 2 body -- # [ if <stdin> is at end of file -> # <stdout> := <stdout> + line # line := "" # else -> # <stdout> := <stdout> + line # <stdin> := <stdin> advanced past the current line # line := rest of the current line of <stdin> ] | ||
| line | <stdin> | <stdout> |
|---|---|---|
| "" | At EOF | @(<stdout>) + line |
#-- 2 -- # [ if (line is "" and <stdin> is at EOF) or # (line is nonempty) -> # <stdout> := <stdout> + line + (characters from <stdin>) # through end of file) # <stdin> := <stdin> advanced to end of file # line := "" ] | ||
| "" | at EOF | @(<stdout>) + line |
Compare this with the overall I/F of the loop (above). Because <stdin> is empty, the expression <stdout> + line + (characters from <stdin>) simplifies to @(<stdout>) + line, which is what we have for <stdout>. And <stdin> is at EOF, and line is "".
#-- 2 body -- # [ if <stdin> is at end of file -> # <stdout> := <stdout> + line # line := "" # else -> # <stdout> := <stdout> + line # <stdin> := <stdin> advanced past the current line # line := rest of the current line of <stdin> ] | ||
| line | <stdin> | <stdout> |
|---|---|---|
| remainder of the current line from <stdin> | @(<stdin>) advanced to the next line | @(<stdout>) + line |
#-- 2 -- # [ if (line is "" and <stdin> is at EOF) or # (line is nonempty) -> # <stdout> := <stdout> + line + (characters from <stdin>) # through end of file) # <stdin> := <stdin> advanced to end of file # line := "" ] | ||
| "" | at EOF | @(<stdout>) + line + (characters from next line of <stdin> through EOF) |
Compare those three values with the loop's overall I/F (shown above under #-- 2 --): line is "" as it should be; <stdin> is at EOF; and <stdout> has had line and the rest of <stdin> appended to it. QED.