-
Notifications
You must be signed in to change notification settings - Fork 43
Expand file tree
/
Copy pathArithEval.lean
More file actions
232 lines (193 loc) · 6.73 KB
/
ArithEval.lean
File metadata and controls
232 lines (193 loc) · 6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/-
Copyright Strata Contributors
SPDX-License-Identifier: Apache-2.0 OR MIT
-/
import StrataTest.DL.Imperative.ArithExpr
import Strata.DL.Imperative.CmdEval
namespace Arith
/-! ## Instantiate `Imperative`'s Symbolic Evaluator
We instantiate Imperative's `EvalContext` typeclass with `ArithPrograms`'
specific implementations to obtain an evaluator that generates
verification conditions on the fly (i.e., a Strongest-Postconditions
Verification Condition Generator).
-/
---------------------------------------------------------------------
namespace Eval
open Std (ToFormat Format format)
open Imperative
/--
Evaluation state for arithmetic expressions `Expr`. This contains components
necessary to specialize `Cmd.eval`.
-/
structure State where
/-- A counter to generate fresh variable names. -/
genNum : Nat := 0
/-- An environment mapping variables to expressions. -/
env : Env := []
/-- Error, if any, encountered during evaluation. -/
error : Option (EvalError PureExpr) := .none
/-- Any warnings encountered during evaluation. -/
warnings : List (EvalWarning PureExpr) := []
/-- Accrued path conditions. -/
pathConditions : PathConditions PureExpr := []
/-- Deferred proof obligations obtained during evaluation, intended to be
discharged by some other means (e.g., denotation into Lean or encoding
into SMTLIB). -/
deferred : ProofObligations PureExpr := #[]
def State.init : State := {}
instance : ToFormat State where
format s :=
f!"error: {s.error}{Format.line}\
warnings: {s.warnings}{Format.line}\
deferred: {s.deferred}{Format.line}\
pathConditions: {PathConditions.format' s.pathConditions}{Format.line}\
env: {s.env}{Format.line}\
genNum: {s.genNum}{Format.line}"
/--
Evaluator for arithmetic expressions `Expr`.
-/
def eval (s : State) (e : Expr) : Expr :=
match e with
| .Plus e1 e2 =>
match (eval s e1), (eval s e2) with
| .Num n1, .Num n2 => .Num (n1 + n2)
| e1', e2' => .Plus e1' e2'
| .Mul e1 e2 =>
match (eval s e1), (eval s e2) with
| .Num n1, .Num n2 => .Num (n1 * n2)
| e1', e2' => .Mul e1' e2'
| .Eq e1 e2 =>
match (eval s e1), (eval s e2) with
| .Num n1, .Num n2 =>
(if n1 == n2 then .Bool true else .Bool false)
| e1', e2' => .Eq e1' e2'
| .Num n => .Num n
| .Bool b => .Bool b
| .Var v ty => match s.env.find? v with | none => .Var v ty | some (_, e) => e
def updateError (s : State) (e : EvalError PureExpr) : State :=
{ s with error := e }
def lookupError (s : State) : Option (EvalError PureExpr) :=
s.error
def update (s : State) (v : String) (ty : Ty) (e : Expr) : State :=
{ s with env := s.env.insert v (ty, e) }
def lookup (s : State) (v : String) : Option Arith.PureExpr.TypedExpr :=
match s.env.find? v with
| some (ty, e) => some (e, ty)
| none => none
/--
Add free variables to the environment, where the free variable is mapped to
itself (i.e., `v ↦ .Var v ty`).
-/
def preprocess (s : State) (_c : Command) (e : Expr) : Expr × State :=
let freeVars := e.freeVars.filter (fun (v, _ty) => not (s.env.contains v))
let new_env := List.foldl (fun env (v, ty) => Map.insert env v (.Num, (Expr.Var v ty))) s.env freeVars
let s := { s with env := new_env }
(e, s)
def genFreeVar (s : State) (x : String) (ty : Ty) : Expr × State :=
let newVar := "$__" ++ x ++ toString s.genNum
let s := { s with genNum := s.genNum + 1 }
(.Var newVar ty, s)
def denoteBool (e : Expr) : Option Bool :=
match e with
| .Bool b => some b
| .Var _ _ | .Plus _ _ | .Mul _ _ | .Eq _ _ | .Num _ => none
def addWarning (s : State) (w : EvalWarning PureExpr) : State :=
{ s with warnings := w :: s.warnings }
def getPathConditions (s : State) : PathConditions PureExpr :=
s.pathConditions
def addPathCondition (s : State) (p : PathCondition PureExpr) : State :=
{ s with pathConditions := s.pathConditions.addInNewest p }
def deferObligation (s : State) (ob : ProofObligation PureExpr) : State :=
{ s with deferred := s.deferred.push ob }
def ProofObligation.freeVars (ob : ProofObligation PureExpr) : List String :=
let assum_typedvars :=
ob.assumptions.flatMap (fun e => e.filterMap (fun
| .assumption _ expr => some expr
| _ => none) |>.flatMap (fun i => i.freeVars))
(assum_typedvars.map (fun (v, _) => v)) ++
(ob.obligation.freeVars.map (fun (v, _) => v))
theorem lookupEval (s1 s2 : State) (h : ∀ x, lookup s1 x = lookup s2 x) :
∀ e, eval s1 e = eval s2 e := by
intro e; induction e <;> simp_all [eval]
case Var v _ =>
simp_all [lookup]
have hv := @h v; clear h
split <;> (split <;> simp_all)
done
---------------------------------------------------------------------
/--
Instantiation of `EvalContext` for `ArithPrograms`.
-/
instance : EvalContext PureExpr State where
eval := Arith.Eval.eval
updateError := Arith.Eval.updateError
lookupError := Arith.Eval.lookupError
update := Arith.Eval.update
lookup := Arith.Eval.lookup
preprocess := Arith.Eval.preprocess
genFreeVar := Arith.Eval.genFreeVar
denoteBool := Arith.Eval.denoteBool
addWarning := Arith.Eval.addWarning
getPathConditions := Arith.Eval.getPathConditions
addPathCondition := Arith.Eval.addPathCondition
deferObligation := Arith.Eval.deferObligation
-- lookupEval := Arith.lookupEval
instance : ToFormat (Cmds PureExpr × State) where
format arg :=
let fcs := Imperative.formatCmds PureExpr arg.fst
let fσ := format arg.snd
format f!"Commands:{Format.line}{fcs}{Format.line}\
State:{Format.line}{fσ}{Format.line}"
---------------------------------------------------------------------
/- Tests -/
private def testProgram1 : Cmds PureExpr :=
[.init "x" .Num (.det (.Num 0)) .empty,
.set "x" (.det (.Plus (.Var "x" .none) (.Num 100))) .empty,
.assert "x_value_eq" (.Eq (.Var "x" .none) (.Num 100)) .empty]
/--
info: Commands:
init (x : Num) := 0
x := 100
assert [x_value_eq] true
State:
error: none
warnings: []
deferred: #[Label: x_value_eq
Property : assert
Assumptions: ⏎
Obligation: true
Metadata: ⏎
]
pathConditions: ⏎
env: (x, (Num, 100))
genNum: 0
-/
#guard_msgs in
#eval format $ Cmds.eval State.init testProgram1
private def testProgram2 : Cmds PureExpr :=
[.init "x" .Num (.det (.Var "y" .none)) .empty,
.set "x" .nondet .empty,
.assert "x_value_eq" (.Eq (.Var "x" .none) (.Num 100)) .empty]
/--
info: Commands:
init (x : Num) := y
havoc x
assert [x_value_eq] ($__x0 : Num) = 100
State:
error: none
warnings: []
deferred: #[Label: x_value_eq
Property : assert
Assumptions: ⏎
Obligation: ($__x0 : Num) = 100
Metadata: ⏎
]
pathConditions: ⏎
env: (y, (Num, y)) (x, (Num, ($__x0 : Num)))
genNum: 1
-/
#guard_msgs in
#eval format $ Cmds.eval State.init testProgram2
---------------------------------------------------------------------
end Eval
end Arith