|
| 1 | +[@@@ocaml.warning "+a-40-41-42"] |
| 2 | + |
| 3 | +let pp_concat pp ppf list = |
| 4 | + Format.pp_print_list ~pp_sep:Format.pp_print_cut pp ppf list |
| 5 | + |
| 6 | +module Name : sig |
| 7 | + type t = private { |
| 8 | + head : string; |
| 9 | + args : (t * t) list; |
| 10 | + } |
| 11 | + |
| 12 | + val create : string -> (t * t) list -> t |
| 13 | + |
| 14 | + val unsafe_create_unchecked : string -> (t * t) list -> t |
| 15 | + |
| 16 | + include Identifiable.S with type t := t |
| 17 | +end = struct |
| 18 | + type t = { |
| 19 | + head : string; |
| 20 | + args : (t * t) list; |
| 21 | + } |
| 22 | + |
| 23 | + include Identifiable.Make (struct |
| 24 | + type nonrec t = t |
| 25 | + |
| 26 | + let rec compare |
| 27 | + ({ head = head1; args = args1 } as t1) |
| 28 | + ({ head = head2; args = args2 } as t2) = |
| 29 | + if t1 == t2 then 0 |
| 30 | + else |
| 31 | + match String.compare head1 head2 with |
| 32 | + | 0 -> List.compare compare_arg args1 args2 |
| 33 | + | c -> c |
| 34 | + and compare_arg (name1, arg1) (name2, arg2) = |
| 35 | + match compare name1 name2 with |
| 36 | + | 0 -> compare arg1 arg2 |
| 37 | + | c -> c |
| 38 | + |
| 39 | + let equal t1 t2 = compare t1 t2 = 0 |
| 40 | + |
| 41 | + let rec print ppf ({ head; args } : t) = |
| 42 | + match args with |
| 43 | + | [] -> |
| 44 | + (* Preserve simple non-wrapping behaviour in atomic case *) |
| 45 | + Format.fprintf ppf "%s" head |
| 46 | + | _ -> |
| 47 | + Format.fprintf ppf "@[<hov 1>%s%a@]" |
| 48 | + head |
| 49 | + (pp_concat print_arg_pair) args |
| 50 | + and print_arg_pair ppf (name, arg) = |
| 51 | + Format.fprintf ppf "[%a:%a]" print name print arg |
| 52 | + |
| 53 | + let output = print |> Misc.output_of_print |
| 54 | + |
| 55 | + let hash = Hashtbl.hash |
| 56 | + end) |
| 57 | + |
| 58 | + let create head args = |
| 59 | + let sorted_args = |
| 60 | + List.sort_uniq (fun (name1, _) (name2, _) -> compare name1 name2) args |
| 61 | + in |
| 62 | + let t = { head; args = sorted_args } in |
| 63 | + if List.length args != List.length sorted_args then |
| 64 | + Misc.fatal_errorf "Names of instance arguments must be unique:@ %a" |
| 65 | + print t; |
| 66 | + t |
| 67 | + |
| 68 | + let unsafe_create_unchecked head args = { head; args } |
| 69 | +end |
| 70 | + |
| 71 | +let compare_arg_name (name1, _) (name2, _) = Name.compare name1 name2 |
| 72 | + |
| 73 | +let rec list_similar f list1 list2 = |
| 74 | + match list1, list2 with |
| 75 | + | [], [] -> true |
| 76 | + | a :: list1, b :: list2 -> f a b && list_similar f list1 list2 |
| 77 | + | (_ :: _), [] | [], (_ :: _) -> false |
| 78 | + |
| 79 | +module T0 : sig |
| 80 | + type t = private { |
| 81 | + head : string; |
| 82 | + visible_args : (Name.t * t) list; |
| 83 | + hidden_args : (Name.t * t) list; |
| 84 | + } |
| 85 | + |
| 86 | + include Identifiable.S with type t := t |
| 87 | + |
| 88 | + val create : string -> (Name.t * t) list -> hidden_args:(Name.t * t) list -> t |
| 89 | + |
| 90 | + val to_name : t -> Name.t |
| 91 | +end = struct |
| 92 | + type t = { |
| 93 | + head : string; |
| 94 | + visible_args : (Name.t * t) list; |
| 95 | + hidden_args : (Name.t * t) list; |
| 96 | + } |
| 97 | + |
| 98 | + include Identifiable.Make (struct |
| 99 | + type nonrec t = t |
| 100 | + |
| 101 | + let rec compare |
| 102 | + ({ head = head1; visible_args = visible_args1; hidden_args = hidden_args1 } as t1) |
| 103 | + ({ head = head2; visible_args = visible_args2; hidden_args = hidden_args2 } as t2) = |
| 104 | + if t1 == t2 then 0 |
| 105 | + else |
| 106 | + match String.compare head1 head2 with |
| 107 | + | 0 -> begin |
| 108 | + match List.compare compare_pairs visible_args1 visible_args2 with |
| 109 | + | 0 -> List.compare compare_pairs hidden_args1 hidden_args2 |
| 110 | + | c -> c |
| 111 | + end |
| 112 | + | c -> c |
| 113 | + and compare_pairs (param1, value1) (param2, value2) = |
| 114 | + match Name.compare param1 param2 with |
| 115 | + | 0 -> compare value1 value2 |
| 116 | + | c -> c |
| 117 | + |
| 118 | + let equal t1 t2 = compare t1 t2 = 0 |
| 119 | + |
| 120 | + let rec equal_looking t name = |
| 121 | + let { head; visible_args; hidden_args } = t in |
| 122 | + let { Name.head = name_head; args = name_args } = name in |
| 123 | + hidden_args = [] |
| 124 | + && String.equal head name_head |
| 125 | + && list_similar equal_looking_args visible_args name_args |
| 126 | + and equal_looking_args (name1, value1) (name2, value2) = |
| 127 | + Name.equal name1 name2 && equal_looking value1 value2 |
| 128 | + |
| 129 | + let rec print ppf { head; visible_args; hidden_args } = |
| 130 | + Format.fprintf ppf "@[<hov 1>%s%a%a@]" |
| 131 | + head |
| 132 | + (pp_concat print_visible_pair) visible_args |
| 133 | + (pp_concat print_hidden_pair) hidden_args |
| 134 | + and print_visible_pair ppf (name, value) = |
| 135 | + Format.fprintf ppf "[%a:%a]" Name.print name print value |
| 136 | + and print_hidden_pair ppf (name, value) = |
| 137 | + if equal_looking value name then |
| 138 | + Format.fprintf ppf "{%a}" Name.print name |
| 139 | + else |
| 140 | + Format.fprintf ppf "{%a:%a}" Name.print name print value |
| 141 | + |
| 142 | + let output = print |> Misc.output_of_print |
| 143 | + |
| 144 | + let hash = Hashtbl.hash |
| 145 | + end) |
| 146 | + |
| 147 | + let create head visible_args ~hidden_args = |
| 148 | + let visible_args_sorted = List.sort compare_arg_name visible_args in |
| 149 | + let hidden_args_sorted = List.sort compare_arg_name hidden_args in |
| 150 | + let t = |
| 151 | + { |
| 152 | + head; |
| 153 | + visible_args = visible_args_sorted; |
| 154 | + hidden_args = hidden_args_sorted; |
| 155 | + } |
| 156 | + in |
| 157 | + if |
| 158 | + List.length visible_args != List.length visible_args_sorted |
| 159 | + || List.length hidden_args != List.length hidden_args_sorted |
| 160 | + then |
| 161 | + Misc.fatal_errorf "Names of arguments and parameters must be unique:@ %a" |
| 162 | + print t; |
| 163 | + t |
| 164 | + |
| 165 | + (* CR-someday lmaurer: Should try and make this unnecessary or at least cheap. |
| 166 | + Could do it by making [Name.t] an unboxed existential so that converting from |
| 167 | + [t] is the identity. Or just have [Name.t] wrap [t] and ignore [hidden_args]. *) |
| 168 | + let rec to_name ({ head; visible_args; hidden_args = _ }) : Name.t = |
| 169 | + (* Safe because we already checked the names in this exact argument list *) |
| 170 | + Name.unsafe_create_unchecked head (List.map arg_to_name visible_args) |
| 171 | + and arg_to_name (name, value) = |
| 172 | + name, to_name value |
| 173 | +end |
| 174 | + |
| 175 | +include T0 |
| 176 | + |
| 177 | +let all_args t = t.visible_args @ t.hidden_args |
| 178 | + |
| 179 | +module Subst = Name.Map |
| 180 | +type subst = t Subst.t |
| 181 | + |
| 182 | +let rec subst0 (t : t) (s : subst) ~changed = |
| 183 | + match Subst.find_opt (to_name t) s with |
| 184 | + | Some rhs -> changed := true; rhs |
| 185 | + | None -> subst0_inside t s ~changed |
| 186 | +and subst0_inside { head; visible_args; hidden_args } s ~changed = |
| 187 | + let matching_hidden_args, non_matching_hidden_args = |
| 188 | + List.partition_map |
| 189 | + (fun ((name, value) as pair) -> |
| 190 | + match Subst.find_opt (to_name value) s with |
| 191 | + | Some rhs -> changed := true; Left (name, rhs) |
| 192 | + | None -> Right pair) |
| 193 | + hidden_args |
| 194 | + in |
| 195 | + let visible_args = subst0_alist visible_args s ~changed in |
| 196 | + let hidden_args = subst0_alist non_matching_hidden_args s ~changed in |
| 197 | + let visible_args = |
| 198 | + List.merge compare_arg_name visible_args matching_hidden_args |
| 199 | + in |
| 200 | + create head visible_args ~hidden_args |
| 201 | +and subst0_alist l s ~changed = |
| 202 | + List.map (fun (name, value) -> name, subst0 value s ~changed) l |
| 203 | + |
| 204 | +let subst t s = |
| 205 | + let changed = ref false in |
| 206 | + let new_t = subst0 t s ~changed in |
| 207 | + if !changed then new_t, `Changed else t, `Did_not_change |
| 208 | + |
| 209 | +let subst_inside t s = |
| 210 | + let changed = ref false in |
| 211 | + let new_t = subst0_inside t s ~changed in |
| 212 | + if !changed then new_t else t |
| 213 | + |
| 214 | +let check s args = |
| 215 | + (* This could do more - say, check that the replacement (the argument) has |
| 216 | + all the parameters of the original (the parameter). (The subset rule |
| 217 | + requires this, since an argument has to refer to the parameter it |
| 218 | + implements, and thus the parameter's parameters must include the |
| 219 | + argument's parameters.) It would be redundant with the checks |
| 220 | + implemented elsewhere but could still be helpful. *) |
| 221 | + let param_set = List.map to_name args |> Name.Set.of_list in |
| 222 | + Name.Set.subset (Name.Map.keys s) param_set |
| 223 | + |
| 224 | +let rec is_complete t = |
| 225 | + match t.hidden_args with |
| 226 | + | [] -> List.for_all (fun (_, value) -> is_complete value) t.visible_args |
| 227 | + | _ -> false |
| 228 | + |
| 229 | +let has_arguments t = |
| 230 | + match t with |
| 231 | + | { head = _; visible_args = []; hidden_args = [] } -> false |
| 232 | + | _ -> true |
0 commit comments