Skip to content

Commit 18e6ed1

Browse files
authored
[spectec] Automate extraction of validation rules for prose generation (#2147)
1 parent d7b6783 commit 18e6ed1

2 files changed

Lines changed: 343 additions & 259 deletions

File tree

spectec/src/backend-prose/gen.ml

Lines changed: 103 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,36 +34,119 @@ let flatten_rec def =
3434
| Ast.RecD defs -> defs
3535
| _ -> [ def ]
3636

37-
(* List of relation names that appear in the prose of the validation rules *)
38-
let validation_helper_relations = [
39-
"Expand";
40-
"Expand_use";
41-
"ImmutReachable";
42-
"NotImmutReachable"
43-
]
44-
let is_validation_helper_relation def =
45-
match def.it with
46-
| Ast.RelD (id, _, _, _, _) ->
47-
List.mem id.it validation_helper_relations
48-
| _ -> false
4937
(* NOTE: Assume validation relation is `|-` *)
5038
let is_validation_relation def =
5139
match def.it with
5240
| Ast.RelD (_, _, mixop, _, _) ->
5341
List.exists (List.exists (fun atom -> atom.it = Atom.Turnstile)) (Mixop.flatten mixop)
5442
| _ -> false
5543

56-
let extract_validation_il il =
57-
il
58-
|> List.concat_map flatten_rec
59-
|> List.filter
60-
(fun rel -> is_validation_relation rel || is_validation_helper_relation rel)
44+
let pairwise_concat xyss =
45+
(* [(xs1, ys1); (xs2, ys2)] ==> (xs1@xs2, ys1@ys2) *)
46+
let xss, yss = List.split xyss in
47+
List.concat xss, List.concat yss
48+
49+
let pairwise_concat_map f xs = List.map f xs |> pairwise_concat
50+
51+
(* dependent_rei_id_of_X: (x:X) -> (rel_id list, func_id list) *)
52+
53+
let dependent_rel_id_of_exp exp =
54+
let func_ids = ref [] in
55+
let collect_func_call e =
56+
(match e.it with
57+
| Ast.CallE (id, _) -> func_ids := id :: !func_ids
58+
| _ -> ()
59+
);
60+
e
61+
in
62+
63+
let open Il2al.Il_walk in
64+
let transformer = { base_transformer with
65+
transform_exp = collect_func_call;
66+
} in
67+
ignore (transform_exp transformer exp);
68+
69+
[], !func_ids
70+
71+
let rec dependent_rel_id_of_prem prem =
72+
match prem.it with
73+
| Ast.IfPr exp -> dependent_rel_id_of_exp exp
74+
| Ast.LetPr (exp1, exp2, _) -> pairwise_concat_map dependent_rel_id_of_exp [exp1; exp2]
75+
| Ast.RulePr (id, _, _, exp) -> pairwise_concat [([id], []); dependent_rel_id_of_exp exp]
76+
| Ast.IterPr (prem', _) -> dependent_rel_id_of_prem prem'
77+
| _ -> ([], [])
78+
79+
let dependent_ids_of_rule rule =
80+
match rule.it with
81+
| Ast.RuleD (_, _, _, _, prems) -> pairwise_concat_map dependent_rel_id_of_prem prems
82+
83+
let dependent_ids_of_clause clause =
84+
match clause.it with
85+
| Ast.DefD (_, _, _, prems) -> pairwise_concat_map dependent_rel_id_of_prem prems
86+
87+
let dependent_ids_of_def def =
88+
match def.it with
89+
| Ast.RelD (_, _, _, _, rules) -> pairwise_concat_map dependent_ids_of_rule rules
90+
| Ast.DecD (_, _, _, clauses) -> pairwise_concat_map dependent_ids_of_clause clauses
91+
| _ -> ([], [])
6192

6293
let rel_has_id id rel =
6394
match rel.it with
64-
| Ast.RelD (id', _, _, _, _) -> id.it = id'.it
95+
| Ast.RelD (id', _, _, _, _) -> Eq.eq_id id id'
96+
| _ -> false
97+
98+
let func_has_id id func =
99+
match func.it with
100+
| Ast.DecD (id', _, _, _) -> Eq.eq_id id id'
65101
| _ -> false
66102

103+
let id_to_rel defs id =
104+
List.find (rel_has_id id) defs
105+
106+
let id_to_func funcs id =
107+
List.find (func_has_id id) funcs
108+
109+
let rec dedup ids =
110+
match ids with
111+
| [] -> []
112+
| hd :: tl ->
113+
let tl' = List.filter (fun id -> not (Eq.eq_id hd id)) tl in
114+
hd :: dedup tl'
115+
116+
let extract_validation_il il =
117+
let all_defs = List.concat_map flatten_rec il in
118+
let validation_rels = List.filter is_validation_relation all_defs in
119+
120+
(* Expand according to the premise dependency *)
121+
let rec expand (prev_rels, prev_funcs) (new_rels, new_funcs) =
122+
let rels = new_rels @ prev_rels in
123+
let is_new_rel_id id = List.for_all (Fun.negate (rel_has_id id)) rels in
124+
let funcs = new_funcs @ prev_funcs in
125+
let is_new_func_id id = List.for_all (Fun.negate (func_has_id id)) funcs in
126+
127+
match new_rels @ new_funcs with
128+
| [] -> rels
129+
| defs ->
130+
let (rel_ids, func_ids) = pairwise_concat_map dependent_ids_of_def defs in
131+
132+
let prem_rels =
133+
rel_ids
134+
|> dedup
135+
|> List.filter is_new_rel_id
136+
|> List.map (id_to_rel all_defs)
137+
in
138+
let prem_funcs =
139+
func_ids
140+
|> dedup
141+
|> List.filter is_new_func_id
142+
|> List.map (id_to_func all_defs)
143+
in
144+
145+
expand (rels, funcs) (prem_rels, prem_funcs)
146+
in
147+
148+
expand ([], []) (validation_rels, [])
149+
67150
let extract_prose_hint hintexp =
68151
match hintexp.it with
69152
| El.Ast.TextE hint -> Some hint
@@ -293,7 +376,8 @@ let rec prem_to_instrs prem =
293376
let rel =
294377
match List.find_opt (rel_has_id id) !Langs.validation_il with
295378
| Some rel -> rel
296-
| None -> failwith ("Unknown relation id: " ^ id.it)
379+
| None -> failwith (
380+
Printf.sprintf "The relation %s is supposed to be included in `validation_il`. Hint: Plese fix `extract_validation_il`." id.it)
297381
in
298382
let frees = (Free.free_prem prem).varid in
299383
let args = exp_to_argexpr e in

0 commit comments

Comments
 (0)