4
4
import sys
5
5
from argparse import ArgumentParser , Namespace
6
6
from pathlib import Path
7
- from typing import Any , Dict , Final , Iterable , List , Optional , TextIO , Tuple
7
+ from typing import Any , Dict , Final , Iterable , List , Optional , Tuple
8
8
9
9
from pathos .pools import ProcessPool # type: ignore
10
10
from pyk .cli_utils import dir_path , file_path
11
- from pyk .kast import KApply , KClaim , KDefinition , KFlatModule , KImport , KInner , KRequire , KSort
11
+ from pyk .kast import KApply , KAtt , KClaim , KDefinition , KFlatModule , KImport , KRequire , KRule , KSort , KToken
12
+ from pyk .kastManip import minimize_term
12
13
from pyk .kcfg import KCFG
13
14
from pyk .ktool .krun import _krun
14
15
from pyk .prelude import mlTop
15
16
16
17
from .gst_to_kore import gst_to_kore
17
18
from .kevm import KEVM
18
19
from .solc_to_k import Contract , contract_to_k , solc_compile
19
- from .utils import KCFG_from_claim , KPrint_make_unparsing , KProve_prove_claim , add_include_arg , read_kast_flatmodulelist
20
+ from .utils import KCFG_from_claim , KPrint_make_unparsing , KProve_prove_claim , add_include_arg
20
21
21
22
_LOGGER : Final = logging .getLogger (__name__ )
22
23
_LOG_FORMAT : Final = '%(levelname)s %(asctime)s %(name)s - %(message)s'
@@ -118,17 +119,15 @@ def exec_foundry_to_k(
118
119
profile : bool ,
119
120
foundry_out : Path ,
120
121
main_module : Optional [str ],
121
- spec_module : Optional [str ],
122
122
requires : List [str ],
123
123
imports : List [str ],
124
- output : Optional [TextIO ],
125
124
** kwargs ,
126
- ) -> None :
125
+ ) -> Tuple [ KDefinition , List [ Tuple [ str , KClaim ]]] :
127
126
kevm = KEVM (definition_dir , profile = profile )
128
127
empty_config = kevm .definition .empty_config (KSort ('KevmCell' ))
129
128
path_glob = str (foundry_out ) + '/*.t.sol/*.json'
130
- modules : List [ KFlatModule ] = []
131
- claims_modules : List [KFlatModule ] = []
129
+ modules = []
130
+ claims : List [Tuple [ str , KClaim ] ] = []
132
131
# Must sort to get consistent output order on different platforms.
133
132
for json_file in sorted (glob .glob (path_glob )):
134
133
if json_file .endswith ('.metadata.json' ):
@@ -150,26 +149,17 @@ def exec_foundry_to_k(
150
149
modules .append (module )
151
150
if claims_module :
152
151
_LOGGER .info (f'Produced claim module: { claims_module .name } ' )
153
- claims_modules . append ( claims_module )
152
+ claims . extend (( claims_module . name , claim ) for claim in claims_module . claims )
154
153
_main_module = KFlatModule (
155
154
main_module if main_module else 'MAIN' , [], [KImport (mname ) for mname in [_m .name for _m in modules ] + imports ]
156
155
)
157
- _spec_module = KFlatModule (
158
- spec_module if spec_module else 'SPEC' , [], [KImport (mname ) for mname in [_m .name for _m in claims_modules ]]
159
- )
160
156
modules .append (_main_module )
161
- modules .append (_spec_module )
162
157
bin_runtime_definition = KDefinition (
163
158
_main_module .name ,
164
- modules + claims_modules ,
159
+ modules ,
165
160
requires = [KRequire (req ) for req in ['edsl.md' ] + requires ],
166
161
)
167
- _kprint = KPrint_make_unparsing (kevm , extra_modules = modules )
168
- KEVM ._patch_symbol_table (_kprint .symbol_table )
169
- if not output :
170
- output = sys .stdout
171
- output .write (_kprint .pretty_print (bin_runtime_definition ) + '\n ' )
172
- output .flush ()
162
+ return bin_runtime_definition , claims
173
163
174
164
175
165
def exec_foundry_kompile (
@@ -180,7 +170,6 @@ def exec_foundry_kompile(
180
170
md_selector : Optional [str ],
181
171
regen : bool = False ,
182
172
rekompile : bool = False ,
183
- reparse : bool = False ,
184
173
reinit : bool = False ,
185
174
requires : Iterable [str ] = (),
186
175
imports : Iterable [str ] = (),
@@ -191,29 +180,33 @@ def exec_foundry_kompile(
191
180
_ignore_arg (kwargs , 'spec_module' , f'--spec-module { kwargs ["spec_module" ]} ' )
192
181
main_module = 'FOUNDRY-MAIN'
193
182
syntax_module = 'FOUNDRY-MAIN'
194
- spec_module = 'FOUNDRY-SPEC'
195
183
foundry_definition_dir = foundry_out / 'kompiled'
196
184
foundry_main_file = foundry_definition_dir / 'foundry.k'
197
185
kompiled_timestamp = foundry_definition_dir / 'timestamp'
198
- parsed_spec = foundry_definition_dir / 'spec.json'
199
186
kcfgs_file = foundry_definition_dir / 'kcfgs.json'
200
187
requires = ['lemmas/lemmas.k' , 'lemmas/int-simplification.k' ] + list (requires )
201
188
imports = ['LEMMAS' , 'INT-SIMPLIFICATION' ] + list (imports )
189
+
202
190
if not foundry_definition_dir .exists ():
203
191
foundry_definition_dir .mkdir ()
192
+
193
+ bin_runtime_definition , claims = exec_foundry_to_k (
194
+ definition_dir = definition_dir ,
195
+ profile = profile ,
196
+ foundry_out = foundry_out ,
197
+ main_module = main_module ,
198
+ requires = list (requires ),
199
+ imports = list (imports ),
200
+ )
201
+
204
202
if regen or not foundry_main_file .exists ():
205
- _LOGGER .info (f'Generating K: { foundry_main_file } ' )
206
203
with open (foundry_main_file , 'w' ) as fmf :
207
- exec_foundry_to_k (
208
- definition_dir = definition_dir ,
209
- profile = profile ,
210
- foundry_out = foundry_out ,
211
- main_module = main_module ,
212
- spec_module = spec_module ,
213
- requires = list (requires ),
214
- imports = list (imports ),
215
- output = fmf ,
216
- )
204
+ _LOGGER .info (f'Writing file: { foundry_main_file } ' )
205
+ _kevm = KEVM (definition_dir = definition_dir )
206
+ _kprint = KPrint_make_unparsing (_kevm , extra_modules = bin_runtime_definition .modules )
207
+ KEVM ._patch_symbol_table (_kprint .symbol_table )
208
+ fmf .write (_kprint .pretty_print (bin_runtime_definition ) + '\n ' )
209
+
217
210
if regen or rekompile or not kompiled_timestamp .exists ():
218
211
_LOGGER .info (f'Kompiling definition: { foundry_main_file } ' )
219
212
KEVM .kompile (
@@ -226,24 +219,15 @@ def exec_foundry_kompile(
226
219
md_selector = md_selector ,
227
220
profile = profile ,
228
221
)
222
+
229
223
kevm = KEVM (foundry_definition_dir , main_file = foundry_main_file , profile = profile )
230
- if regen or rekompile or reparse or not parsed_spec .exists ():
231
- _LOGGER .info (f'Parsing specs: { foundry_main_file } ' )
232
- prove_args = add_include_arg (includes )
233
- kevm .prove (
234
- foundry_main_file ,
235
- spec_module_name = spec_module ,
236
- dry_run = True ,
237
- args = (['--emit-json-spec' , str (parsed_spec )] + prove_args ),
238
- )
239
- if regen or rekompile or reparse or reinit or not kcfgs_file .exists ():
224
+ if reinit or not kcfgs_file .exists ():
240
225
_LOGGER .info (f'Initializing KCFGs: { kcfgs_file } ' )
241
226
cfgs : Dict [str , Dict ] = {}
242
- for module in read_kast_flatmodulelist (parsed_spec ).modules :
243
- for claim in module .claims :
244
- cfg_label = claim .att ["label" ]
245
- _LOGGER .info (f'Producing KCFG: { cfg_label } ' )
246
- cfgs [cfg_label ] = KCFG_from_claim (kevm .definition , claim ).to_dict ()
227
+ for module_name , claim in claims :
228
+ cfg_label = f'{ module_name } .{ claim .att ["label" ]} '
229
+ _LOGGER .info (f'Producing KCFG: { cfg_label } ' )
230
+ cfgs [cfg_label ] = KCFG_from_claim (kevm .definition , claim ).to_dict ()
247
231
with open (kcfgs_file , 'w' ) as kf :
248
232
kf .write (json .dumps (cfgs ))
249
233
kf .close ()
@@ -261,8 +245,10 @@ def exec_prove(
261
245
depth : Optional [int ],
262
246
claims : Iterable [str ] = (),
263
247
exclude_claims : Iterable [str ] = (),
248
+ minimize : bool = True ,
264
249
** kwargs ,
265
250
) -> None :
251
+ _ignore_arg (kwargs , 'lemmas' , '--lemma' )
266
252
kevm = KEVM (definition_dir , profile = profile )
267
253
prove_args = add_include_arg (includes )
268
254
haskell_args = []
@@ -277,6 +263,8 @@ def exec_prove(
277
263
if exclude_claims :
278
264
prove_args += ['--exclude' , ',' .join (exclude_claims )]
279
265
final_state = kevm .prove (spec_file , spec_module_name = spec_module , args = prove_args , haskell_args = haskell_args )
266
+ if minimize :
267
+ final_state = minimize_term (final_state )
280
268
print (kevm .pretty_print (final_state ) + '\n ' )
281
269
if not (type (final_state ) is KApply and final_state .label .name == '#Top' ):
282
270
_LOGGER .error ('Proof failed!' )
@@ -293,6 +281,8 @@ def exec_foundry_prove(
293
281
tests : Iterable [str ] = (),
294
282
exclude_tests : Iterable [str ] = (),
295
283
workers : int = 1 ,
284
+ minimize : bool = True ,
285
+ lemmas : Iterable [str ] = (),
296
286
** kwargs ,
297
287
) -> None :
298
288
_ignore_arg (kwargs , 'main_module' , f'--main-module: { kwargs ["main_module" ]} ' )
@@ -336,21 +326,25 @@ def _kcfg_unproven_to_claim(_kcfg: KCFG) -> KClaim:
336
326
337
327
kevm = KEVM (definition_dir , profile = profile , use_directory = use_directory )
338
328
339
- def prove_it (_id_and_claim : Tuple [str , KClaim ]) -> Tuple [bool , KInner ]:
329
+ lemma_rules = [KRule (KToken (lr , 'K' ), att = KAtt ({'simplification' : '' })) for lr in lemmas ]
330
+
331
+ def prove_it (_id_and_claim : Tuple [str , KClaim ]) -> bool :
340
332
_claim_id , _claim = _id_and_claim
341
- return KProve_prove_claim (kevm , _claim , _claim_id , _LOGGER )
333
+ ret , result = KProve_prove_claim (kevm , _claim , _claim_id , _LOGGER , depth = depth , lemmas = lemma_rules )
334
+ if minimize :
335
+ result = minimize_term (result )
336
+ print (f'Result for { _claim_id } :\n { kevm .pretty_print (result )} \n ' )
337
+ return ret
342
338
343
339
with ProcessPool (ncpus = workers ) as process_pool :
344
340
results = process_pool .map (prove_it , claims )
345
341
process_pool .close ()
346
342
347
- failed_claims = [(cid , result ) for ((cid , _ ), (failed , result )) in zip (claims , results ) if failed ]
348
- _failed_claim_ids = [cid for cid , _ in failed_claims ]
349
-
350
- if _failed_claim_ids :
351
- print (f'Failed to prove KCFGs: { _failed_claim_ids } \n ' )
343
+ failed_claims = [cid for ((cid , _ ), failed ) in zip (claims , results ) if failed ]
344
+ if failed_claims :
345
+ print (f'Failed to prove KCFGs: { failed_claims } \n ' )
352
346
353
- sys .exit (len (_failed_claim_ids ))
347
+ sys .exit (len (failed_claims ))
354
348
355
349
356
350
def exec_run (
@@ -415,6 +409,19 @@ def parse(s):
415
409
action = 'store_true' ,
416
410
help = 'Generate a haskell-backend bug report for the execution.' ,
417
411
)
412
+ kprove_args .add_argument (
413
+ '--lemma' ,
414
+ dest = 'lemmas' ,
415
+ default = [],
416
+ action = 'append' ,
417
+ help = 'Additional lemmas to include as simplification rules during execution.' ,
418
+ )
419
+ kprove_args .add_argument (
420
+ '--minimize' , dest = 'minimize' , default = True , action = 'store_true' , help = 'Minimize prover output.'
421
+ )
422
+ kprove_args .add_argument (
423
+ '--no-minimize' , dest = 'minimize' , action = 'store_false' , help = 'Do not minimize prover output.'
424
+ )
418
425
419
426
k_kompile_args = ArgumentParser (add_help = False )
420
427
k_kompile_args .add_argument (
@@ -550,13 +557,6 @@ def parse(s):
550
557
action = 'store_true' ,
551
558
help = 'Rekompile foundry.k even if kompiled definition already exists.' ,
552
559
)
553
- foundry_kompile .add_argument (
554
- '--reparse' ,
555
- dest = 'reparse' ,
556
- default = False ,
557
- action = 'store_true' ,
558
- help = 'Reparse K specifications even if the parsed spec already exists.' ,
559
- )
560
560
foundry_kompile .add_argument (
561
561
'--reinit' ,
562
562
dest = 'reinit' ,
0 commit comments