• No results found

(c,s) ⇒ t =⇒ (c,s) ⇒ t0 =⇒ t0 =t

proof (induction arbitrary : t0 rule : big_step.induct )

— the only interesting case, WhileTrue : fix b c s s1 t t0

— The assumptions of the rule:

assume bval b s and (c,s) ⇒ s1and (WHILE b DO c,s1) ⇒ t

— Ind.Hyp; note theV because of arbitrary:

assume IHc:Vt0. (c,s) ⇒ t0 =⇒ t0 =s1

qed blast + — prove the rest automatically

Fig. 7.4. IMP is deterministic

7.3 Small-Step Semantics

thy

The big-step semantics executes a program from an initial to the final state in one big step. Short of inspecting the derivation tree of big-step introduction rules, it does not allow us to explicitly observe intermediate execution states.

That is the purpose of a small-step semantics.

Small-step semantics lets us explicitly observe partial executions and make formal statements about them. This enables us, for instance, to talk about the interleaved, concurrent execution of multiple programs. The main idea for representing a partial execution is to introduce the concept of how far execution has progressed in the program. There are many ways of doing this. Traditionally, for a high-level language like IMP, we modify the type of the big-step judgement from com × state ⇒ state ⇒ bool to something like com × state ⇒ com × state ⇒ bool. The second com × state com-ponent of the judgement is the result state of one small, atomic execution step together with a modified command that represents what still has to be executed. We call a com × state pair a configuration of the program, and use the command SKIP to indicate that execution has terminated.

The idea is easiest to understand by looking at the set of rules. They define one atomic execution step. The execution of a command is then a sequence of such steps.

(x ::= a , s) → (SKIP , s(x := aval a s)) Assign

(SKIP ;; c2,s) → (c2,s) Seq1 (c1,s) → (c10,s0)

(c1;; c2,s) → (c10;; c2,s0) Seq2 bval b s

(IF b THEN c1 ELSE c2,s) → (c1,s) IfTrue

¬ bval b s

(IF b THEN c1 ELSE c2,s) → (c2,s) IfFalse

(WHILE b DO c, s) → (IF b THEN c;; WHILE b DO c ELSE SKIP , s)While

Fig. 7.5. The small-step rules of IMP

Going through the rules inFigure 7.5we see that:

 Variable assignment is an atomic step. As mentioned above, SKIP repre-sents the terminated program.

 There are two rules for semicolon: either the first part is fully executed already (signified by SKIP ), in which case we continue with the second part, or the first part can be executed further, in which case we perform the execution step and replace this first part with its reduced version.

 An IF reduces either to the command in the THEN branch or the ELSE branch, depending on the value of the condition.

 The final rule is for the WHILE loop: we define its semantics by merely unrolling the loop once. The subsequent execution steps will take care of testing the condition and possibly executing the body.

Note that we could have used the unrolling definition of WHILE in the big-step semantics as well. We were, after all, able to prove it as an equivalence in Section 7.2.4. However, such an unfolding is less natural in the big-step case, whereas in the small-step semantics the whole idea is to transform the command bit by bit to model execution.

Had we wanted to observe partial execution of arithmetic or boolean ex-pressions, we could have introduced a small-step semantics for these as well (seeExercise 7.4) and made the corresponding small-step rules for assignment, IF, and WHILE non-atomic in the same way as the semicolon rules.

We can now define the execution of a program as the reflexive transitive closure of the small_step judgement →, using the star operator defined in Section 4.5.2:

7.3 Small-Step Semantics 87 abbreviation op →∗ :: com × state ⇒ com × state ⇒ bool where x →∗ y ≡ star small_step x y

Example 7.10. To look at an example execution of a command in the small-step semantics, we again use the values command. This time, we will get multiple elements in the set that it returns — all partial executions of the program. Given the command c with

c = 0 0x0 0 ::= V 0 0z0 0;; 0 0y0 0 ::= V 0 0x0 0 and an initial state s with

s = <0 0x0 0 := 3, 0 0y0 0 := 7, 0 0z0 0 := 5>

we issue the following query to Isabelle

values{(c0,map t [0 0x0 0,0 0y0 0,0 0z0 0]) |c0 t . (c,s) →∗ (c0,t )}

The result contains four execution steps, starting with the original program in the initial state, proceeding through partial execution of the the two as-signments, and ending in the final state of the final program SKIP :

{(0 0x0 0 ::=V 0 0z0 0;; 0 0y0 0 ::=V 0 0x0 0, [3, 7, 5]), (SKIP ;; 0 0y0 0 ::= V 0 0x0 0, [5, 7, 5]),

(0 0y0 0 ::= V 0 0x0 0, [5, 7, 5]), (SKIP , [5, 5, 5])}

As a further test of whether our definition of the small-step semantics is useful, we prove that the rules still give us a deterministic language, like the big-step semantics.

Lemma 7.11. [[cs → cs0; cs → cs0 0]] =⇒ cs0 0 = cs0

Proof. After induction on the first premise (the small-step semantics), the proof is as automatic as for the big-step semantics. ut Recall that both sides of the small-step arrow → are configurations, that is, pairs of commands and states. If we don’t need to refer to the individual components, we refer to the configuration as a whole, such as cs in the lemma above.

We could conduct further tests like this, but since we already have a semantics for IMP, we can use it to show that our new semantics defines precisely the same behaviour. The next section does this.

7.3.1 Equivalence with Big-Step Semantics

Having defined an alternative semantics for the same language, the first in-teresting question is of course if our definitions are equivalent. This section

shows that this is the case. Both directions are proved separately: for any big-step execution, there is an equivalent small-step execution and vice versa.

We start by showing that any big-step execution can be simulated by a sequence of small steps ending in SKIP :

Lemma 7.12. cs ⇒ t =⇒ cs →∗ (SKIP , t )

This is proved in the canonical fashion by rule induction on the big-step judgement. Most cases follow directly. As an example we look at rule IfTrue:

bval b s (c1, s) ⇒ t (IF b THEN c1 ELSE c2, s) ⇒ t

By IH we know that (c1,s) →∗ (SKIP , t ). This yields the required small-step derivation:

bval b s

(IF b THEN c1 ELSE c2, s) → (c1,s) (c1, s) →∗ (SKIP , t ) (IF b THEN c1 ELSE c2, s) →∗ (SKIP , t )

Only rule Seq does not go through directly:

(c1, s1)⇒ s2 (c2, s2)⇒ s3

(c1;; c2,s1)⇒ s3

The IHs are (c1, s1) →∗ (SKIP , s2)and (c2, s2) →∗ (SKIP , s3) but we need a reduction (c1;;c2, s1) →∗ ... . The following lemma bridges the gap:

it lifts a →∗ derivation into the context of a semicolon:

Lemma 7.13.

(c1,s1)→∗ (c, s2) =⇒ (c1;; c2, s1)→∗ (c;; c2, s2)

Proof. The proof is by induction on the reflexive transitive closure star. The base case is trivial and the step is not much harder: If (c1, s1) → (c10, s10) and (c10, s10) →∗ (c, s2), we have (c10;; c2, s10)→∗ (c;; c2, s2)by IH. Rule Seq2 and the step rule for star do the rest:

(c1, s1) → (c10, s10)

(c1;; c2, s1) → (c10;; c2, s10) (c10;; c2, s10)→∗ (c;; c2, s2) (c1;; c2, s1) →∗ (c;; c2, s2)

u t Returning to the proof of the Seq case, Lemma 7.13 turns the first IH into (c1;; c2, s1)→∗ (SKIP ;; c2,s2). From rule Seq1 and the second IH we have (SKIP ;; c2, s2) →∗ (SKIP , s3):

(SKIP ;; c2, s2) → (c2, s2) (c2, s2) →∗ (SKIP , s3) (SKIP ;; c2, s2)→∗ (SKIP , s3)

7.3 Small-Step Semantics 89 By transitivity of star we finally arrive at (c1;; c2,s1)→∗ (SKIP , s3), thus finishing the Seq case and the proof ofLemma 7.12.

Let us now consider the other direction:

Lemma 7.14 (Small-step implies big-step).

cs →∗ (SKIP , t ) =⇒ cs ⇒ t

The proof is again canonical, namely by rule induction on the premise, the reflexive transitive closure. The base case cs = (SKIP , t ) is trivial. In the induction step we have cs → cs0 and cs0 →∗ (SKIP , t ) and the IH cs0 ⇒ t. That this implies cs ⇒ t is proved as a separate lemma:

Lemma 7.15 (Step case). [[cs → cs0; cs0 ⇒ t ]] =⇒ cs ⇒ t

Proof. The proof is automatic after rule induction on the small-step

seman-tics. ut

This concludes the proof ofLemma 7.14. Both directions together (Lemma 7.12 andLemma 7.14) let us derive the equivalence we were aiming for in the first place:

Corollary 7.16. (c, s) ⇒ t ←→ (c, s) →∗ (SKIP , t )

This concludes our proof that the small-step and big-step semantics of IMP are equivalent. Such equivalence proofs are useful whenever there are different formal descriptions of the same artefact. The reason one might want different descriptions of the same thing is that they differ in what they can be used for. For instance, big-step semantics are relatively intuitive to define, while small-step semantics allow us to make more fine-grained formal observations.

The next section exploits the fine-grained nature of the small-step semantics to elucidate the big-step semantics.

7.3.2 Final Configurations, Infinite Reductions, and Termination In contrast to the big-step semantics, in the small-step semantics it is possible to speak about non-terminating executions directly. We can easily distinguish final configurations from those that can make further progress:

definition final :: com × state ⇒ bool where final cs ←→ ¬ (∃ cs0. cs → cs0)

In our semantics, these happen to be exactly the configurations that have SKIP as their command.

Lemma 7.17. final (c, s) = (c = SKIP )

Proof. One direction is easy: clearly, if the command c is SKIP, the configura-tion is final. The other direcconfigura-tion is not much harder. It is proved automatically

after inducting on c. ut

With this we can show that ⇒ yields a final state iff → terminates:

Lemma 7.18. (∃ t . cs ⇒ t ) ←→ (∃ cs0. cs →∗ cs0 ∧ final cs0)

Proof. UsingLemma 7.17we can replace final with configurations that have SKIP as the command. The rest follows from the equivalence of small and

big-step semantics. ut

This lemma says that in IMP the absence of a big-step result is equivalent to non-termination. This is not necessarily the case for any language. Another reason for the absence of a big-step result may be a runtime error in the execution of the program that leads to no rule being applicable. In the big-step semantics this is often indistinguishable from non-termination. In the small-step semantics the concept of final configurations neatly distinguishes the two causes.

Since IMP is deterministic, there is no difference between “may” and “must”

termination. Consider a language with non-determinism.

In such a language,Lemma 7.18is still valid and both sides speak about possible (may ) termination. In fact, the big-step semantics cannot speak about necessary (must ) termination at all, whereas the small-step semantics can: there must not be an infinite reduction cs0 → cs1 → . . ..