3 (*>*) |
3 (*>*) |
4 |
4 |
5 section{*Case Study: Compiling Expressions*} |
5 section{*Case Study: Compiling Expressions*} |
6 |
6 |
7 text{*\label{sec:ExprCompiler} |
7 text{*\label{sec:ExprCompiler} |
|
8 \index{compiling expressions example|(}% |
8 The task is to develop a compiler from a generic type of expressions (built |
9 The task is to develop a compiler from a generic type of expressions (built |
9 from variables, constants and binary operations) to a stack machine. This |
10 from variables, constants and binary operations) to a stack machine. This |
10 generic type of expressions is a generalization of the boolean expressions in |
11 generic type of expressions is a generalization of the boolean expressions in |
11 \S\ref{sec:boolex}. This time we do not commit ourselves to a particular |
12 \S\ref{sec:boolex}. This time we do not commit ourselves to a particular |
12 type of variables or values but make them type parameters. Neither is there |
13 type of variables or values but make them type parameters. Neither is there |
62 | Apply f \<Rightarrow> exec is s ((f (hd vs) (hd(tl vs)))#(tl(tl vs))))"; |
63 | Apply f \<Rightarrow> exec is s ((f (hd vs) (hd(tl vs)))#(tl(tl vs))))"; |
63 |
64 |
64 text{*\noindent |
65 text{*\noindent |
65 Recall that @{term"hd"} and @{term"tl"} |
66 Recall that @{term"hd"} and @{term"tl"} |
66 return the first element and the remainder of a list. |
67 return the first element and the remainder of a list. |
67 Because all functions are total, @{term"hd"} is defined even for the empty |
68 Because all functions are total, \cdx{hd} is defined even for the empty |
68 list, although we do not know what the result is. Thus our model of the |
69 list, although we do not know what the result is. Thus our model of the |
69 machine always terminates properly, although the definition above does not |
70 machine always terminates properly, although the definition above does not |
70 tell us much about the result in situations where @{term"Apply"} was executed |
71 tell us much about the result in situations where @{term"Apply"} was executed |
71 with fewer than two elements on the stack. |
72 with fewer than two elements on the stack. |
72 |
73 |
85 execution of a compiled expression results in the value of the expression: |
86 execution of a compiled expression results in the value of the expression: |
86 *} |
87 *} |
87 theorem "exec (comp e) s [] = [value e s]"; |
88 theorem "exec (comp e) s [] = [value e s]"; |
88 (*<*)oops;(*>*) |
89 (*<*)oops;(*>*) |
89 text{*\noindent |
90 text{*\noindent |
90 This theorem needs to be generalized to |
91 This theorem needs to be generalized: |
91 *} |
92 *} |
92 |
93 |
93 theorem "\<forall>vs. exec (comp e) s vs = (value e s) # vs"; |
94 theorem "\<forall>vs. exec (comp e) s vs = (value e s) # vs"; |
94 |
95 |
95 txt{*\noindent |
96 txt{*\noindent |
96 which is proved by induction on @{term"e"} followed by simplification, once |
97 It will be proved by induction on @{term"e"} followed by simplification. |
97 we have the following lemma about executing the concatenation of two |
98 First, we must prove a lemma about executing the concatenation of two |
98 instruction sequences: |
99 instruction sequences: |
99 *} |
100 *} |
100 (*<*)oops;(*>*) |
101 (*<*)oops;(*>*) |
101 lemma exec_app[simp]: |
102 lemma exec_app[simp]: |
102 "\<forall>vs. exec (xs@ys) s vs = exec ys s (exec xs s vs)"; |
103 "\<forall>vs. exec (xs@ys) s vs = exec ys s (exec xs s vs)"; |
103 |
104 |
104 txt{*\noindent |
105 txt{*\noindent |
105 This requires induction on @{term"xs"} and ordinary simplification for the |
106 This requires induction on @{term"xs"} and ordinary simplification for the |
106 base cases. In the induction step, simplification leaves us with a formula |
107 base cases. In the induction step, simplification leaves us with a formula |
107 that contains two @{text"case"}-expressions over instructions. Thus we add |
108 that contains two @{text"case"}-expressions over instructions. Thus we add |
108 automatic case splitting as well, which finishes the proof: |
109 automatic case splitting, which finishes the proof: |
109 *} |
110 *} |
110 apply(induct_tac xs, simp, simp split: instr.split); |
111 apply(induct_tac xs, simp, simp split: instr.split); |
111 (*<*)done(*>*) |
112 (*<*)done(*>*) |
112 text{*\noindent |
113 text{*\noindent |
113 Note that because both \methdx{simp_all} and \methdx{auto} perform simplification, they can |
114 Note that because both \methdx{simp_all} and \methdx{auto} perform simplification, they can |
124 Although this is more compact, it is less clear for the reader of the proof. |
125 Although this is more compact, it is less clear for the reader of the proof. |
125 |
126 |
126 We could now go back and prove \isa{exec (comp e) s [] = [value e s]} |
127 We could now go back and prove \isa{exec (comp e) s [] = [value e s]} |
127 merely by simplification with the generalized version we just proved. |
128 merely by simplification with the generalized version we just proved. |
128 However, this is unnecessary because the generalized version fully subsumes |
129 However, this is unnecessary because the generalized version fully subsumes |
129 its instance. |
130 its instance.% |
|
131 \index{compiling expressions example|)} |
130 *} |
132 *} |
131 (*<*) |
133 (*<*) |
132 theorem "\<forall>vs. exec (comp e) s vs = (value e s) # vs"; |
134 theorem "\<forall>vs. exec (comp e) s vs = (value e s) # vs"; |
133 by(induct_tac e, auto); |
135 by(induct_tac e, auto); |
134 end |
136 end |