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
|
(* ========================================================================= *)
(* More syntax constructors, and prelogical utilities like matching. *)
(* *)
(* John Harrison, University of Cambridge Computer Laboratory *)
(* *)
(* (c) Copyright, University of Cambridge 1998 *)
(* (c) Copyright, John Harrison 1998-2007 *)
(* ========================================================================= *)
needs "fusion.ml";;
(* ------------------------------------------------------------------------- *)
(* Create probably-fresh variable *)
(* ------------------------------------------------------------------------- *)
let genvar =
let gcounter = ref 0 in
fun ty -> let count = !gcounter in
(gcounter := count + 1;
mk_var("_"^(string_of_int count),ty));;
(* ------------------------------------------------------------------------- *)
(* Convenient functions for manipulating types. *)
(* ------------------------------------------------------------------------- *)
let dest_fun_ty ty =
match ty with
Tyapp("fun",[ty1;ty2]) -> (ty1,ty2)
| _ -> failwith "dest_fun_ty";;
let rec occurs_in ty bigty =
bigty = ty ||
is_type bigty && exists (occurs_in ty) (snd(dest_type bigty));;
let rec tysubst alist ty =
try rev_assoc ty alist with Failure _ ->
if is_vartype ty then ty else
let tycon,tyvars = dest_type ty in
mk_type(tycon,map (tysubst alist) tyvars);;
(* ------------------------------------------------------------------------- *)
(* A bit more syntax. *)
(* ------------------------------------------------------------------------- *)
let bndvar tm =
try fst(dest_abs tm)
with Failure _ -> failwith "bndvar: Not an abstraction";;
let body tm =
try snd(dest_abs tm)
with Failure _ -> failwith "body: Not an abstraction";;
let list_mk_comb(h,t) = rev_itlist (C (curry mk_comb)) t h;;
let list_mk_abs(vs,bod) = itlist (curry mk_abs) vs bod;;
let strip_comb = rev_splitlist dest_comb;;
let strip_abs = splitlist dest_abs;;
(* ------------------------------------------------------------------------- *)
(* Generic syntax to deal with some binary operators. *)
(* *)
(* Note that "mk_binary" only works for monomorphic functions. *)
(* ------------------------------------------------------------------------- *)
let is_binary s tm =
match tm with
Comb(Comb(Const(s',_),_),_) -> s' = s
| _ -> false;;
let dest_binary s tm =
match tm with
Comb(Comb(Const(s',_),l),r) when s' = s -> (l,r)
| _ -> failwith "dest_binary";;
let mk_binary s =
let c = mk_const(s,[]) in
fun (l,r) -> try mk_comb(mk_comb(c,l),r)
with Failure _ -> failwith "mk_binary";;
(* ------------------------------------------------------------------------- *)
(* Produces a sequence of variants, considering previous inventions. *)
(* ------------------------------------------------------------------------- *)
let rec variants av vs =
if vs = [] then [] else
let vh = variant av (hd vs) in vh::(variants (vh::av) (tl vs));;
(* ------------------------------------------------------------------------- *)
(* Gets all variables (free and/or bound) in a term. *)
(* ------------------------------------------------------------------------- *)
let variables =
let rec vars(acc,tm) =
if is_var tm then insert tm acc
else if is_const tm then acc
else if is_abs tm then
let v,bod = dest_abs tm in
vars(insert v acc,bod)
else
let l,r = dest_comb tm in
vars(vars(acc,l),r) in
fun tm -> vars([],tm);;
(* ------------------------------------------------------------------------- *)
(* General substitution (for any free expression). *)
(* ------------------------------------------------------------------------- *)
let subst =
let rec ssubst ilist tm =
if ilist = [] then tm else
try fst (find ((aconv tm) o snd) ilist) with Failure _ ->
match tm with
Comb(f,x) -> let f' = ssubst ilist f and x' = ssubst ilist x in
if f' == f && x' == x then tm else mk_comb(f',x')
| Abs(v,bod) ->
let ilist' = filter (not o (vfree_in v) o snd) ilist in
mk_abs(v,ssubst ilist' bod)
| _ -> tm in
fun ilist ->
let theta = filter (fun (s,t) -> Pervasives.compare s t <> 0) ilist in
if theta = [] then (fun tm -> tm) else
let ts,xs = unzip theta in
fun tm ->
let gs = variants (variables tm) (map (genvar o type_of) xs) in
let tm' = ssubst (zip gs xs) tm in
if tm' == tm then tm else vsubst (zip ts gs) tm';;
(* ------------------------------------------------------------------------- *)
(* Alpha conversion term operation. *)
(* ------------------------------------------------------------------------- *)
let alpha v tm =
let v0,bod = try dest_abs tm
with Failure _ -> failwith "alpha: Not an abstraction"in
if v = v0 then tm else
if type_of v = type_of v0 && not (vfree_in v bod) then
mk_abs(v,vsubst[v,v0]bod)
else failwith "alpha: Invalid new variable";;
(* ------------------------------------------------------------------------- *)
(* Type matching. *)
(* ------------------------------------------------------------------------- *)
let rec type_match vty cty sofar =
if is_vartype vty then
try if rev_assoc vty sofar = cty then sofar else failwith "type_match"
with Failure "find" -> (cty,vty)::sofar
else
let vop,vargs = dest_type vty and cop,cargs = dest_type cty in
if vop = cop then itlist2 type_match vargs cargs sofar
else failwith "type_match";;
(* ------------------------------------------------------------------------- *)
(* Conventional matching version of mk_const (but with a sanity test). *)
(* ------------------------------------------------------------------------- *)
let mk_mconst(c,ty) =
try let uty = get_const_type c in
let mat = type_match uty ty [] in
let con = mk_const(c,mat) in
if type_of con = ty then con else fail()
with Failure _ -> failwith "mk_const: generic type cannot be instantiated";;
(* ------------------------------------------------------------------------- *)
(* Like mk_comb, but instantiates type variables in rator if necessary. *)
(* ------------------------------------------------------------------------- *)
let mk_icomb(tm1,tm2) =
let "fun",[ty;_] = dest_type (type_of tm1) in
let tyins = type_match ty (type_of tm2) [] in
mk_comb(inst tyins tm1,tm2);;
(* ------------------------------------------------------------------------- *)
(* Instantiates types for constant c and iteratively makes combination. *)
(* ------------------------------------------------------------------------- *)
let list_mk_icomb cname args =
let atys,_ = nsplit dest_fun_ty args (get_const_type cname) in
let tyin = itlist2 (fun g a -> type_match g (type_of a)) atys args [] in
list_mk_comb(mk_const(cname,tyin),args);;
(* ------------------------------------------------------------------------- *)
(* Free variables in assumption list and conclusion of a theorem. *)
(* ------------------------------------------------------------------------- *)
let thm_frees th =
let asl,c = dest_thm th in
itlist (union o frees) asl (frees c);;
(* ------------------------------------------------------------------------- *)
(* Is one term free in another? *)
(* ------------------------------------------------------------------------- *)
let rec free_in tm1 tm2 =
if aconv tm1 tm2 then true
else if is_comb tm2 then
let l,r = dest_comb tm2 in free_in tm1 l || free_in tm1 r
else if is_abs tm2 then
let bv,bod = dest_abs tm2 in
not (vfree_in bv tm1) && free_in tm1 bod
else false;;
(* ------------------------------------------------------------------------- *)
(* Searching for terms. *)
(* ------------------------------------------------------------------------- *)
let rec find_term p tm =
if p tm then tm else
if is_abs tm then find_term p (body tm) else
if is_comb tm then
let l,r = dest_comb tm in
try find_term p l with Failure _ -> find_term p r
else failwith "find_term";;
let find_terms =
let rec accum tl p tm =
let tl' = if p tm then insert tm tl else tl in
if is_abs tm then
accum tl' p (body tm)
else if is_comb tm then
accum (accum tl' p (rator tm)) p (rand tm)
else tl' in
accum [];;
(* ------------------------------------------------------------------------- *)
(* General syntax for binders. *)
(* *)
(* NB! The "mk_binder" function expects polytype "A", which is the domain. *)
(* ------------------------------------------------------------------------- *)
let is_binder s tm =
match tm with
Comb(Const(s',_),Abs(_,_)) -> s' = s
| _ -> false;;
let dest_binder s tm =
match tm with
Comb(Const(s',_),Abs(x,t)) when s' = s -> (x,t)
| _ -> failwith "dest_binder";;
let mk_binder op =
let c = mk_const(op,[]) in
fun (v,tm) -> mk_comb(inst [type_of v,aty] c,mk_abs(v,tm));;
(* ------------------------------------------------------------------------- *)
(* Syntax for binary operators. *)
(* ------------------------------------------------------------------------- *)
let is_binop op tm =
match tm with
Comb(Comb(op',_),_) -> op' = op
| _ -> false;;
let dest_binop op tm =
match tm with
Comb(Comb(op',l),r) when op' = op -> (l,r)
| _ -> failwith "dest_binop";;
let mk_binop op tm1 =
let f = mk_comb(op,tm1) in
fun tm2 -> mk_comb(f,tm2);;
let list_mk_binop op = end_itlist (mk_binop op);;
let binops op = striplist (dest_binop op);;
(* ------------------------------------------------------------------------- *)
(* Some common special cases *)
(* ------------------------------------------------------------------------- *)
let is_conj = is_binary "/\\";;
let dest_conj = dest_binary "/\\";;
let conjuncts = striplist dest_conj;;
let is_imp = is_binary "==>";;
let dest_imp = dest_binary "==>";;
let is_forall = is_binder "!";;
let dest_forall = dest_binder "!";;
let strip_forall = splitlist dest_forall;;
let is_exists = is_binder "?";;
let dest_exists = dest_binder "?";;
let strip_exists = splitlist dest_exists;;
let is_disj = is_binary "\\/";;
let dest_disj = dest_binary "\\/";;
let disjuncts = striplist dest_disj;;
let is_neg tm =
try fst(dest_const(rator tm)) = "~"
with Failure _ -> false;;
let dest_neg tm =
try let n,p = dest_comb tm in
if fst(dest_const n) = "~" then p else fail()
with Failure _ -> failwith "dest_neg";;
let is_uexists = is_binder "?!";;
let dest_uexists = dest_binder "?!";;
let dest_cons = dest_binary "CONS";;
let is_cons = is_binary "CONS";;
let dest_list tm =
try let tms,nil = splitlist dest_cons tm in
if fst(dest_const nil) = "NIL" then tms else fail()
with Failure _ -> failwith "dest_list";;
let is_list = can dest_list;;
(* ------------------------------------------------------------------------- *)
(* Syntax for numerals. *)
(* ------------------------------------------------------------------------- *)
let dest_numeral =
let rec dest_num tm =
if try fst(dest_const tm) = "_0" with Failure _ -> false then num_0 else
let l,r = dest_comb tm in
let n = num_2 */ dest_num r in
let cn = fst(dest_const l) in
if cn = "BIT0" then n
else if cn = "BIT1" then n +/ num_1
else fail() in
fun tm -> try let l,r = dest_comb tm in
if fst(dest_const l) = "NUMERAL" then dest_num r else fail()
with Failure _ -> failwith "dest_numeral";;
(* ------------------------------------------------------------------------- *)
(* Syntax for generalized abstractions. *)
(* *)
(* These are here because they are used by the preterm->term translator; *)
(* preterms regard generalized abstractions as an atomic notion. This is *)
(* slightly unclean --- for example we need locally some operations on *)
(* universal quantifiers --- but probably simplest. It has to go somewhere! *)
(* ------------------------------------------------------------------------- *)
let dest_gabs =
let dest_geq = dest_binary "GEQ" in
fun tm ->
try if is_abs tm then dest_abs tm else
let l,r = dest_comb tm in
if not (fst(dest_const l) = "GABS") then fail() else
let ltm,rtm = dest_geq(snd(strip_forall(body r))) in
rand ltm,rtm
with Failure _ -> failwith "dest_gabs: Not a generalized abstraction";;
let is_gabs = can dest_gabs;;
let mk_gabs =
let mk_forall(v,t) =
let cop = mk_const("!",[type_of v,aty]) in
mk_comb(cop,mk_abs(v,t)) in
let list_mk_forall(vars,bod) = itlist (curry mk_forall) vars bod in
let mk_geq(t1,t2) =
let p = mk_const("GEQ",[type_of t1,aty]) in
mk_comb(mk_comb(p,t1),t2) in
fun (tm1,tm2) ->
if is_var tm1 then mk_abs(tm1,tm2) else
let fvs = frees tm1 in
let fty = mk_fun_ty (type_of tm1) (type_of tm2) in
let f = variant (frees tm1 @ frees tm2) (mk_var("f",fty)) in
let bod = mk_abs(f,list_mk_forall(fvs,mk_geq(mk_comb(f,tm1),tm2))) in
mk_comb(mk_const("GABS",[fty,aty]),bod);;
let list_mk_gabs(vs,bod) = itlist (curry mk_gabs) vs bod;;
let strip_gabs = splitlist dest_gabs;;
(* ------------------------------------------------------------------------- *)
(* Syntax for let terms. *)
(* ------------------------------------------------------------------------- *)
let dest_let tm =
try let l,aargs = strip_comb tm in
if fst(dest_const l) <> "LET" then fail() else
let vars,lebod = strip_gabs (hd aargs) in
let eqs = zip vars (tl aargs) in
let le,bod = dest_comb lebod in
if fst(dest_const le) = "LET_END" then eqs,bod else fail()
with Failure _ -> failwith "dest_let: not a let-term";;
let is_let = can dest_let;;
let mk_let(assigs,bod) =
let lefts,rights = unzip assigs in
let lend = mk_comb(mk_const("LET_END",[type_of bod,aty]),bod) in
let lbod = list_mk_gabs(lefts,lend) in
let ty1,ty2 = dest_fun_ty(type_of lbod) in
let ltm = mk_const("LET",[ty1,aty; ty2,bty]) in
list_mk_comb(ltm,lbod::rights);;
(* ------------------------------------------------------------------------- *)
(* Useful function to create stylized arguments using numbers. *)
(* ------------------------------------------------------------------------- *)
let make_args =
let rec margs n s avoid tys =
if tys = [] then [] else
let v = variant avoid (mk_var(s^(string_of_int n),hd tys)) in
v::(margs (n + 1) s (v::avoid) (tl tys)) in
fun s avoid tys ->
if length tys = 1 then
[variant avoid (mk_var(s,hd tys))]
else
margs 0 s avoid tys;;
(* ------------------------------------------------------------------------- *)
(* Director strings down a term. *)
(* ------------------------------------------------------------------------- *)
let find_path =
let rec find_path p tm =
if p tm then [] else
if is_abs tm then "b"::(find_path p (body tm)) else
try "r"::(find_path p (rand tm))
with Failure _ -> "l"::(find_path p (rator tm)) in
fun p tm -> implode(find_path p tm);;
let follow_path =
let rec follow_path s tm =
match s with
[] -> tm
| "l"::t -> follow_path t (rator tm)
| "r"::t -> follow_path t (rand tm)
| _::t -> follow_path t (body tm) in
fun s tm -> follow_path (explode s) tm;;
|