Source file eval.ml
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
open Types.Ast
open Types
open Pprint
open Err
type env = (string * (Ast.t * loc)) list
type extenv = (string, (Ast.t * loc)) Hashtbl.t
let get_loc (ast:Ast.t) : loc option = match ast with
| Loc (_,loc) -> Some loc
| _ -> None
let warning (ast:Ast.t) (message:string) =
warn (Warning,Eval,message,get_loc ast)
let ast_without_loc (ast:Ast.t) : Ast.t = match ast with
| Loc (ast,_) -> ast
| ast -> ast
let raise_with_loc (ast:Ast.t) (message:string) = match ast with
| Loc (ast,loc) -> fatal (Error,Eval,message,Some loc)
| _ -> fatal (Error,Eval,message,None)
let raise_type_error operator operand expanded (expected_types:string) =
raise_with_loc operator (
"'"^(string_of_ast_type operator)^"' expects "^expected_types^". "^
"The operand:\n"^
" "^(string_of_ast operand)^"\n"^
"has been expanded to something of type '"^(string_of_ast_type expanded)^"':\n"^
" "^(string_of_ast expanded)^"\n")
let raise_type_error2 operator op1 exp1 op2 exp2 (expected_types:string) =
raise_with_loc operator
("incorrect types with '"^(string_of_ast_type operator)^"'; expects "^expected_types^". "^
"In statement:\n"^
" "^(string_of_ast operator)^"\n"^
"Left-hand operand has type '"^(string_of_ast_type exp1)^"':\n"^
" "^(string_of_ast exp1)^"\n"^
"Right-hand operand has type '"^(string_of_ast_type exp2)^"':\n"^
" "^(string_of_ast exp2)^"\n")
let raise_set_decl ast elmt elmt_expanded set set_expanded (expected_types:string) =
raise_with_loc ast
("Ill-formed set declaration. It expects "^expected_types^". "^
"One of the elements is of type '"^(string_of_ast_type elmt_expanded)^"':\n"^
" "^(string_of_ast elmt)^"\n"^
"This element has been expanded to\n"^
" "^(string_of_ast elmt_expanded)^"\n"^
"Up to now, the set declaration\n"^
" "^(string_of_ast set)^"\n"^
"has been expanded to:\n"^
" "^(string_of_ast set_expanded)^"\n")
let check_nb_vars_same_as_nb_sets (ast:Ast.t) (vars:Ast.t list) (sets:Ast.t list) : unit =
let loc = match (List.nth vars 0), List.nth sets ((List.length sets)-1) with
| Loc (_,(startpos,_)), Loc (_,(_,endpos)) -> startpos,endpos
| _-> failwith "[shouldn't happen] missing locations in vars/sets"
in
match (List.length vars) = (List.length sets) with
| true -> ()
| false -> fatal (Error,Eval,
"Ill-formed '"^(string_of_ast_type ast)^"'. The number of variables and sets must be the same. "^
"You defined "^(string_of_int (List.length vars))^" variables:\n"^
" "^(string_of_ast_list "," vars)^"\n"^
"but you gave "^(string_of_int (List.length sets))^" sets:\n"^
" "^(string_of_ast_list "," sets)^"\n"
,Some loc)
let extenv = ref (Hashtbl.create 0)
let check_only = ref false
let check_only = ref false
let smt = ref false
let rec eval ?smt:(smt_mode=false) ?(onlychecktypes=false) ast : Ast.t =
check_only := onlychecktypes;
smt := smt_mode;
extenv := Hashtbl.create 50;
eval_touist_code [] ast
and eval_touist_code (env:env) ast :Ast.t =
let rec affect_vars = function
| [] -> []
| Loc (Affect (Loc (Var (p,i),var_loc),y),affect_loc)::xs ->
Hashtbl.replace !extenv (expand_var_name env (p,i)) (eval_ast env y, var_loc);
affect_vars xs
| x::xs -> x::(affect_vars xs)
in
let rec process_formulas = function
| [] -> raise_with_loc ast ("no formulas")
| x::[] -> x
| x::xs -> And (x, process_formulas xs)
in
match ast_without_loc ast with
| Touist_code (formulas) ->
eval_ast_formula env (process_formulas (affect_vars formulas))
| e -> raise_with_loc ast ("this does not seem to be a touist code structure: " ^ string_of_ast e ^"\n")
and eval_ast (env:env) (ast:Ast.t) : Ast.t =
let eval_ast = eval_ast env in
let expanded = match ast_without_loc ast with
| Int x -> Int x
| Float x -> Float x
| Bool x -> Bool x
| Var (p,i) ->
let name = expand_var_name env (p,i) in
begin
try let (content,loc) = List.assoc name env in content
with Not_found ->
try let (content,_) = Hashtbl.find !extenv name in content
with Not_found -> raise_with_loc ast
("variable '" ^ name ^"' does not seem to be known. Either you forgot "^
"to declare it globally or it has been previously declared locally "^
"(with bigand, bigor or let) and you are out of its scope."^"\n")
end
| Set x -> Set x
| Set_decl x -> eval_set_decl env ast
| Neg x -> (match eval_ast x with
| Int x' -> Int (- x')
| Float x' -> Float (-. x')
| x' -> raise_type_error ast x x' "'float' or 'int'")
| Add (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Int (x + y)
| Float x, Float y -> Float (x +. y)
| x',y' -> raise_type_error2 ast x x' y y' "'float' or 'int'")
| Sub (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Int (x - y)
| Float x, Float y -> Float (x -. y)
| x',y' -> raise_type_error2 ast x x' y y' "'float' or 'int'")
| Mul (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Int (x * y)
| Float x, Float y -> Float (x *. y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float' or 'int'")
| Div (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Int (x / y)
| Float x, Float y -> Float (x /. y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float' or 'int'")
| Mod (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Int (x mod y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float' or 'int'")
| Sqrt x -> (match eval_ast x with
| Float x -> Float (sqrt x)
| x' -> raise_type_error ast x x' "a float")
| To_int x -> (match eval_ast x with
| Float x -> Int (int_of_float x)
| Int x -> Int x
| x' -> raise_type_error ast x x' "a 'float' or 'int'")
| To_float x -> (match eval_ast x with
| Int x -> Float (float_of_int x)
| Float x -> Float x
| x' -> raise_type_error ast x x' "a 'float' or 'int'")
| Abs x -> (match eval_ast x with
| Int x -> Int (abs x)
| Float x -> Float (abs_float x)
| x' -> raise_type_error ast x x' "a 'float' or 'int'")
| Not x -> (match eval_ast x with
| Bool x -> Bool (not x)
| x' -> raise_type_error ast x x' "a 'bool'")
| And (x,y) -> (match eval_ast x, eval_ast y with
| Bool x,Bool y -> Bool (x && y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'bool'")
| Or (x,y) -> (match eval_ast x, eval_ast y with
| Bool x,Bool y -> Bool (x || y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'bool'")
| Xor (x,y) -> (match eval_ast x, eval_ast y with
| Bool x,Bool y -> Bool ((x || y) && (not (x && y)))
| x',y' -> raise_type_error2 ast x x' y y' "a 'bool'")
| Implies (x,y) -> (match eval_ast x, eval_ast y with
| Bool x,Bool y -> Bool (not x || y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'bool'")
| Equiv (x,y) -> (match eval_ast x, eval_ast y with
| Bool x,Bool y -> Bool ((not x || y) && (not x || y))
| x',y' -> raise_type_error2 ast x x' y y' "a 'bool'")
| If (x,y,z) ->
let test =
match eval_ast x with
| Bool true -> true
| Bool false -> false
| x' -> raise_type_error ast x x' "a 'bool'"
in
if test then eval_ast y else eval_ast z
| Union (x,y) -> begin
match eval_ast x, eval_ast y with
| Set a, Set b -> Set (AstSet.union a b)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float-set', 'int-set' or 'prop-set'"
end
| Inter (x,y) -> begin
match eval_ast x, eval_ast y with
| Set a, Set b -> Set (AstSet.inter a b)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float-set', 'int-set' or 'prop-set'"
end
| Diff (x,y) -> begin
match eval_ast x, eval_ast y with
| Set a, Set b -> Set (AstSet.diff a b)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float-set', 'int-set' or 'prop-set'"
end
| Range (x,y) ->
let irange min max step =
let rec loop acc = function i when i=max+1 -> acc | i -> loop ((Int i)::acc) (i+step)
in loop [] min |> List.rev
and frange min max step =
let rec loop acc = function i when i=max+.1. -> acc | i -> loop ((Float i)::acc) (i+.step)
in loop [] min |> List.rev
in begin
match eval_ast x, eval_ast y with
| Int x, Int y -> Set (AstSet.of_list (irange x (if !check_only then x else y) 1))
| Float x, Float y -> Set (AstSet.of_list (frange x (if !check_only then x else y) 1.))
| x',y' -> raise_type_error2 ast x x' y y' "two integers or two floats"
end
| Empty x -> begin
match eval_ast x with
| Set x -> Bool (AstSet.is_empty x)
| x' -> raise_type_error ast x x' "a 'float-set', 'int-set' or 'prop-set'"
end
| Card x -> begin
match eval_ast x with
| Set x -> Int (AstSet.cardinal x)
| x' -> raise_type_error ast x x' "a 'float-set', 'int-set' or 'prop-set'"
end
| Subset (x,y) -> begin
match eval_ast x, eval_ast y with
| Set a, Set b -> Bool (AstSet.subset a b)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float-set', int or prop"
end
| Powerset x -> begin
let combination_to_set k set =
List.fold_left (fun acc x -> AstSet.add (Set (AstSet.of_list x)) acc) AstSet.empty (AstSet.combinations k set)
in
let rec all_combinations_to_set k set = match k with
| 0 -> AstSet.of_list [Set (AstSet.empty)]
| _ -> AstSet.union (combination_to_set k set) (all_combinations_to_set (pred k) set)
in
match eval_ast x with
| Set s -> if !check_only then Set (AstSet.of_list [AstSet.choose s])
else Set (all_combinations_to_set (AstSet.cardinal s) s)
| x' -> raise_type_error ast x x' "a 'set'"
end
| In (x,y) ->
begin match eval_ast x, eval_ast y with
| x', Set y' -> Bool (AstSet.mem x' y')
| x',y' -> raise_type_error2 ast x x' y y' "an 'int', 'float' or 'prop' on the left-hand and a 'set' on the right-hand"
end
| Equal (x,y) -> begin match eval_ast x, eval_ast y with
| Int x, Int y -> Bool (x = y)
| Float x, Float y -> Bool (x = y)
| Prop x, Prop y -> Bool (x = y)
| Set a, Set b -> Bool (AstSet.equal a b)
| x',y' -> raise_type_error2 ast x x' y y' "an 'int', 'float', 'prop' or 'set'"
end
| Not_equal (x,y) -> eval_ast (Not (Equal (x,y)))
| Lesser_than (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Bool (x < y)
| Float x, Float y -> Bool (x < y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float' or 'int'")
| Lesser_or_equal (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Bool (x <= y)
| Float x, Float y -> Bool (x <= y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float' or 'int'")
| Greater_than (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Bool (x > y)
| Float x, Float y -> Bool (x > y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float' or 'int'")
| Greater_or_equal (x,y) -> (match eval_ast x, eval_ast y with
| Int x, Int y -> Bool (x >= y)
| Float x, Float y -> Bool (x >= y)
| x',y' -> raise_type_error2 ast x x' y y' "a 'float' or 'int'")
| UnexpProp (p,i) -> expand_prop_with_set env p i
| Prop x -> Prop x
| Loc (x,l) -> eval_ast x
| Paren x -> eval_ast x
| e -> failwith ("[shouldnt happen] this expression cannot be expanded: " ^ string_of_ast e ^"\n")
in expanded
and eval_set_decl (env:env) (set_decl:Ast.t) =
let sets = (match ast_without_loc set_decl with Set_decl sets -> sets | _ -> failwith "shoulnt happen: non-Set_decl in eval_set_decl") in
let sets_expanded = List.map (fun x -> eval_ast env x) sets in
let unwrap_set first_elmt elmt elmt_expanded = match first_elmt, elmt_expanded with
| Int _ , Int x -> Int x
| Float _, Float x -> Float x
| Prop _ , Prop x -> Prop x
| Set _ , Set x -> Set x
| _ -> raise_set_decl set_decl elmt elmt_expanded
(Set_decl sets) (Set_decl sets_expanded)
("at this point a comma-separated list of '"^string_of_ast_type first_elmt^"', \
because previous elements of the list had this type"^"\n")
in
match sets, sets_expanded with
| [],[] -> Set AstSet.empty
| x::_,first::_ -> begin
match first with
| Int _ | Float _ | Prop _ | Set _ -> Set (AstSet.of_list (List.map2 (unwrap_set first) sets sets_expanded))
| _ -> raise_set_decl set_decl x first
(Set_decl sets) (Set_decl sets_expanded)
"elements of type 'int', 'float', 'prop' or 'set'"
end
| [],x::_ | x::_,[] -> failwith "[shouldn't happen] len(sets)!=len(sets_expanded)"
and eval_ast_formula (env:env) (ast:Ast.t) : Ast.t =
let eval_ast_formula = eval_ast_formula env
and eval_ast_formula_env = eval_ast_formula
and eval_ast = eval_ast env in
match ast_without_loc ast with
| Int x -> Int x
| Float x -> Float x
| Neg x ->
begin
match eval_ast_formula x with
| Int x' -> Int (- x')
| Float x' -> Float (-. x')
| x' -> Neg x'
end
| Add (x,y) ->
begin
match eval_ast_formula x, eval_ast_formula y with
| Int x', Int y' -> Int (x' + y')
| Float x', Float y' -> Float (x' +. y')
| Int _, Prop _
| Prop _, Int _ -> Add (x,y)
| x', y' -> Add (x', y')
end
| Sub (x,y) ->
begin
match eval_ast_formula x, eval_ast_formula y with
| Int x', Int y' -> Int (x' - y')
| Float x', Float y' -> Float (x' -. y')
| x', y' -> Sub (x', y')
end
| Mul (x,y) ->
begin
match eval_ast_formula x, eval_ast_formula y with
| Int x', Int y' -> Int (x' * y')
| Float x', Float y' -> Float (x' *. y')
| x', y' -> Mul (x', y')
end
| Div (x,y) ->
begin
match eval_ast_formula x, eval_ast_formula y with
| Int x', Int y' -> Int (x' / y')
| Float x', Float y' -> Float (x' /. y')
| x', y' -> Div (x', y')
end
| Equal (x,y) -> Equal (eval_ast_formula x, eval_ast_formula y)
| Not_equal (x,y) -> Not_equal (eval_ast_formula x, eval_ast_formula y)
| Lesser_than (x,y) -> Lesser_than (eval_ast_formula x, eval_ast_formula y)
| Lesser_or_equal (x,y) -> Lesser_or_equal (eval_ast_formula x, eval_ast_formula y)
| Greater_than (x,y) -> Greater_than (eval_ast_formula x, eval_ast_formula y)
| Greater_or_equal (x,y) -> Greater_or_equal (eval_ast_formula x, eval_ast_formula y)
| Top -> Top
| Bottom -> Bottom
| UnexpProp (p,i) -> Prop (expand_var_name env (p,i))
| Prop x -> Prop x
| Var (p,i) ->
let name = expand_var_name env (p,i) in
begin
try let content,loc_affect = List.assoc name env in
match content with
| Prop x -> Prop x
| Int x when !smt -> Int x
| Float x when !smt -> Float x
| _ -> raise_with_loc ast
("local variable '" ^ name ^ "' (defined in bigand, bigor or let) "^
"cannot be expanded into a 'prop' because its content "^
"is of type '"^(string_of_ast_type content)^"' instead of "^
(if !smt then "'prop', 'int' or 'float'" else "'prop'") ^ ". "^
"Why? Because this variable is part of a formula, and thus is expected"^
"to be a proposition. Here is the content of '" ^name^"':\n"^
" "^(string_of_ast content)^"\n")
with Not_found ->
try let content,loc_affect = Hashtbl.find !extenv name in
match content with
| Prop x -> Prop x
| Int x when !smt -> Int x
| Float x when !smt -> Float x
| _ -> raise_with_loc ast
("global variable '" ^ name ^ "' cannot be expanded into a 'prop' "^
"because its content is of type '"^(string_of_ast_type content)^"' instead of "^
(if !smt then "'prop', 'int' or 'float'" else "'prop'") ^ ". "^
"Why? Because this variable is part of a formula, and thus is expected "^
"to be a proposition. Here is the content of '" ^name^"':\n"^
" "^(string_of_ast content)^"\n")
with Not_found ->
try
match (p,i) with
| prefix, None -> raise Not_found
| prefix, Some indices ->
let (content,loc_affect) = List.assoc prefix env in
let term = match content with
| Prop x -> Prop x
| wrong -> fatal (Error,Eval,
"the proposition '" ^ name ^ "' cannot be expanded because '"^prefix^"' is of type '"^(string_of_ast_type wrong)^"'. " ^
"In order to produce an expanded proposition of this kind, '"^prefix^"' must be a proposition. "^
"Why? Because this variable is part of a formula, and thus is expected "^
"to be a proposition. Here is the content of '" ^prefix^"':\n"^
" "^(string_of_ast content)^"\n",Some loc_affect)
in eval_ast_formula (UnexpProp ((string_of_ast term), Some indices))
with Not_found -> raise_with_loc ast ("'" ^ name ^ "' has not been declared"^"\n")
end
| Not Top -> Bottom
| Not Bottom -> Top
| Not x -> Not (eval_ast_formula x)
| And (Bottom, _) | And (_, Bottom) -> Bottom
| And (Top,x)
| And (x,Top) -> eval_ast_formula x
| And (x,y) -> And (eval_ast_formula x, eval_ast_formula y)
| Or (Top, _) | Or (_, Top) -> Top
| Or (Bottom,x)
| Or (x,Bottom) -> eval_ast_formula x
| Or (x,y) -> Or (eval_ast_formula x, eval_ast_formula y)
| Xor (x,y) -> Xor (eval_ast_formula x, eval_ast_formula y)
| Implies (_,Top)
| Implies (Bottom,_) -> Top
| Implies (x,Bottom) -> eval_ast_formula (Not x)
| Implies (Top,x) -> eval_ast_formula x
| Implies (x,y) -> Implies (eval_ast_formula x, eval_ast_formula y)
| Equiv (x,Top)
| Equiv (Top,x) -> eval_ast_formula x
| Equiv (x,Bottom)
| Equiv (Bottom,x) -> eval_ast_formula (Not x)
| Equiv (x,y) -> Equiv (eval_ast_formula x, eval_ast_formula y)
| Exact (x,y) -> rm_top_bot begin
match eval_ast x, eval_ast y with
| Int 0, Set s when AstSet.is_empty s -> Top
| Int k, Set s when AstSet.is_empty s -> Bottom
| Int x, Set s -> if !check_only then Prop "dummy" else exact_str (AstSet.exact x s)
| x',y' -> raise_type_error2 ast x x' y y' "'int' (left-hand) and a 'prop-set' (right-hand)"
end
| Atleast (x,y) -> rm_top_bot begin
match eval_ast x, eval_ast y with
| Int 0, Set s when AstSet.is_empty s -> Top
| Int k, Set s when k > 0 && AstSet.is_empty s -> Bottom
| Int x, Set s -> if !check_only then Prop "dummy" else atleast_str (AstSet.atleast x s)
| x',y' -> raise_type_error2 ast x x' y y' "'int' (left-hand) and a 'prop-set' (right-hand)"
end
| Atmost (x,y) -> rm_top_bot begin
match eval_ast x, eval_ast y with
| Int _, Set s when AstSet.is_empty s -> Top
| Int 0, Set _ -> Bottom
| Int x, Set s -> if !check_only then Prop "dummy" else atmost_str (AstSet.atmost x s)
| x',y' -> raise_type_error2 ast x x' y y' "'int' (left-hand) and a 'prop-set' (right-hand)"
end
| Bigand (vars,sets,when_optional,body) ->
let when_cond = match when_optional with Some x -> x | None -> Bool true in
begin check_nb_vars_same_as_nb_sets ast vars sets;
match vars,sets with
| [],[] | _,[] | [],_ -> failwith "shouln't happen: non-variable in big construct"
| [Loc (Var (name,_),loc)],[set] ->
let rec process_list_set env (set_list:Ast.t list) =
match set_list with
| [] -> Top
| x::xs ->
let env = (name,(x,loc))::env in
match ast_to_bool env when_cond with
| true when xs != [] -> And (eval_ast_formula_env env body, process_list_set env xs)
| true -> eval_ast_formula_env env body
| false -> process_list_set env xs
in
let list_ast_set = set_to_ast_list env set in
let evaluated_ast = process_list_set env list_ast_set in
rm_top_bot evaluated_ast
| x::xs,y::ys ->
eval_ast_formula (Bigand ([x],[y],None,(Bigand (xs,ys,when_optional,body))))
end
| Bigor (vars,sets,when_optional,body) ->
let when_cond = match when_optional with Some x -> x | None -> Bool true
in
begin check_nb_vars_same_as_nb_sets ast vars sets;
match vars,sets with
| [],[] | _,[] | [],_ -> failwith "shouln't happen: non-variable in big construct"
| [Loc (Var (name,_),loc)],[set] ->
let rec process_list_set env (set_list:Ast.t list) =
match set_list with
| [] -> Bottom
| x::xs ->
let env = (name,(x,loc))::env in
match ast_to_bool env when_cond with
| true when xs != [] -> Or (eval_ast_formula_env env body, process_list_set env xs)
| true -> eval_ast_formula_env env body
| false -> process_list_set env xs
in
let list_ast_set = set_to_ast_list env set in
let evaluated_ast = process_list_set env list_ast_set in
rm_top_bot evaluated_ast
| x::xs,y::ys ->
eval_ast_formula (Bigor ([x],[y],None,(Bigor (xs,ys,when_optional,body))))
end
| If (c,y,z) ->
let test = match eval_ast c with Bool c -> c | c' -> raise_type_error ast c c' "boolean"
in if test then eval_ast_formula y else eval_ast_formula z
| Let (Loc (Var (p,i),loc),content,formula) ->
let name = (expand_var_name env (p,i)) and desc = (eval_ast content,loc)
in eval_ast_formula_env ((name,desc)::env) formula
| Paren x -> eval_ast_formula x
| Exists (p,f) -> let p = match eval_ast_formula p with
| Prop p -> Prop p
| wrong -> raise_with_loc p ("'exists' only works on propositions. Instead, got a "
^"'"^string_of_ast_type wrong^"'.\n")
in Exists (p, eval_ast_formula f)
| Forall (p,f) -> let p = match eval_ast_formula p with
| Prop p -> Prop p
| wrong -> raise_with_loc p ("'forall' only works on propositions. Instead, got a "
^"'"^string_of_ast_type wrong^"'.\n")
in Forall (p, eval_ast_formula f)
| For (Loc (Var (p,i),loc), content, Loc (formula,_)) ->
let name = (expand_var_name env (p,i)) in begin
match formula, eval_ast content with
| Exists (x,f), Set s -> AstSet.fold (fun content acc -> Exists (eval_ast_formula_env ((name,(content,loc))::env) x, acc)) s (eval_ast_formula f)
| Forall (x,f), Set s -> AstSet.fold (fun content acc -> Forall (eval_ast_formula_env ((name,(content,loc))::env) x, acc)) s (eval_ast_formula f)
| _,content' -> raise_type_error ast content content' " 'prop-set'"
end
| NewlineBefore f | NewlineAfter f -> eval_ast_formula f
| e -> raise_with_loc ast ("this expression is not a formula: " ^ string_of_ast e ^"\n")
and exact_str lst =
let rec go = function
| [],[] -> Top
| t::ts,[] -> And ( t, go (ts,[]))
| [],f::fs -> And ( Not f, go ([],fs))
| t::ts,f::fs -> And (And (t, Not f), go (ts,fs))
in
match lst with
| [] -> Bottom
| x::xs -> Or (go x, exact_str xs)
and atleast_str lst =
List.fold_left (fun acc str -> Or (acc, formula_of_string_list str)) Bottom lst
and atmost_str lst =
List.fold_left (fun acc str ->
Or (acc, List.fold_left (fun acc' str' ->
And (acc', Not str')) Top str)) Bottom lst
and formula_of_string_list =
List.fold_left (fun acc str -> And (acc, str)) Top
and and_of_term_list =
List.fold_left (fun acc t -> And (acc, t)) Top
and expand_prop_with_set env name indices_optional =
let rec eval_indices env (l:Ast.t list) :Ast.t list = match l with
| [] -> []
| x::xs -> (eval_ast env x)::(eval_indices env xs)
in
let rec has_nonempty_set = function
| [] -> false
| (Set s)::_ when AstSet.is_empty s -> false
| (Set _)::_ -> true
| _::next -> has_nonempty_set next
in
let indices, generated_props = match indices_optional with
| None -> [], [UnexpProp (name,None)]
| Some x -> let indices = eval_indices env x in
indices, expand_prop_with_set' [UnexpProp (name,None)] indices env
in
let eval_unexpprop acc cur = match cur with
| UnexpProp (p,i) -> (Prop (expand_var_name env (p,i)))::acc | _->failwith "shouldnt happen"
in let props_evaluated = List.fold_left eval_unexpprop [] generated_props in
if (let x = has_nonempty_set indices in x) then Set (AstSet.of_list props_evaluated)
else List.nth props_evaluated 0
and expand_prop_with_set' proplist indices env =
match indices with
| [] -> proplist
| i::next ->
match i with
| Set s when AstSet.is_empty s -> expand_prop_with_set' proplist next env
| Set s -> let new_proplist = (expand_proplist proplist (set_to_ast_list env (Set s))) in
expand_prop_with_set' new_proplist next env
| x -> expand_prop_with_set' (expand_proplist proplist [x]) next env
and expand_proplist proplist ind = match proplist with
| [] -> []
| x::xs -> (expand_prop x ind) @ (expand_proplist xs ind)
and expand_prop prop ind = match prop with
| UnexpProp (name, None) -> List.fold_left (fun acc i -> (UnexpProp (name,Some ([i])))::acc) [] ind
| UnexpProp (name, Some cur) -> List.fold_left (fun acc i -> (UnexpProp (name,Some (cur @ [i])))::acc) [] ind
| x -> failwith ("[shouldnt happen] proplist contains smth that is not UnexpProp: "^string_of_ast_type x)
and expand_var_name (env:env) (prefix,indices:string * Ast.t list option) =
match (prefix,indices) with
| (x,None) -> x
| (x,Some y) ->
x ^ "("
^ (string_of_ast_list "," (List.map (fun e -> eval_ast env e) y))
^ ")"
and set_to_ast_list (env:env) (ast:Ast.t) :Ast.t list =
let lst = match ast_without_loc (eval_ast env ast) with
| Set s -> AstSet.elements s
| ast' -> raise_with_loc ast (
"after 'in', only sets are allowed, but got '"^(string_of_ast_type ast')^"':\n"^
" "^(string_of_ast ast')^"\n"^
"This element has been expanded to\n"^
" "^(string_of_ast ast')^"\n")
in match !check_only, lst with
| false, _ -> lst
| true, [] -> []
| true, x::xs -> [x]
and ast_to_bool env (ast:Ast.t) : bool =
match eval_ast env ast with
| Bool b -> b
| ast' -> raise_with_loc ast (
"'when' expects a 'bool' but got '"^(string_of_ast_type ast')^"':\n"^
" "^(string_of_ast ast')^"\n"^
"This element has been expanded to\n"^
" "^(string_of_ast ast')^"\n")
and has_top_or_bot = function
| Top | Bottom -> true
| Not x -> has_top_or_bot x
| And (x,y) -> has_top_or_bot x || has_top_or_bot y
| Or (x,y) -> has_top_or_bot x || has_top_or_bot y
| Xor (x,y) -> has_top_or_bot x || has_top_or_bot y
| Implies (x,y) -> has_top_or_bot x || has_top_or_bot y
| Equiv (x,y) -> has_top_or_bot x || has_top_or_bot y
| Neg x -> has_top_or_bot x
| Add (x,y) -> has_top_or_bot x || has_top_or_bot y
| Sub (x,y) -> has_top_or_bot x || has_top_or_bot y
| Mul (x,y) -> has_top_or_bot x || has_top_or_bot y
| Div (x,y) -> has_top_or_bot x || has_top_or_bot y
| Equal (x,y) -> has_top_or_bot x || has_top_or_bot y
| Not_equal (x,y) -> has_top_or_bot x || has_top_or_bot y
| Lesser_than (x,y) -> has_top_or_bot x || has_top_or_bot y
| Lesser_or_equal (x,y) -> has_top_or_bot x || has_top_or_bot y
| Greater_than (x,y) -> has_top_or_bot x || has_top_or_bot y
| Greater_or_equal (x,y) -> has_top_or_bot x || has_top_or_bot y
| Exists (_,y) -> has_top_or_bot y
| Forall (_,y) -> has_top_or_bot y
| _ -> false
and rm_top_bot ast =
if ast != Top && ast != Bottom && has_top_or_bot ast
then rm_top_bot (eval_ast_formula [] ast)
else ast