1(* ===================================================================== *)
2(* FILE          : Lib.sml                                               *)
3(* DESCRIPTION   : library of useful SML functions.                      *)
4(*                                                                       *)
5(* AUTHOR        : (c) Konrad Slind, University of Calgary               *)
6(* DATE          : August 26, 1991                                       *)
7(* Modified      : September 22, 1997, Ken Larsen                        *)
8(* ===================================================================== *)
9
10structure Lib :> Lib =
11struct
12
13open Feedback;
14
15val ERR = mk_HOL_ERR "Lib"
16
17open Portable;
18datatype frag = datatype HOLPP.frag
19
20(*---------------------------------------------------------------------------*
21 * A version of try that coerces non-HOL_ERR exceptions to be HOL_ERRs.      *
22 * To be used with tacticals and the like that get driven by HOL_ERR         *
23 * exceptions.                                                               *
24 *---------------------------------------------------------------------------*)
25
26local
27   val default_exn = ERR "trye" "original exn. not a HOL_ERR"
28in
29   fun trye f x =
30      f x handle e as HOL_ERR _ => raise e
31               | Interrupt => raise Interrupt
32               | _ => raise default_exn
33end
34
35(*---------------------------------------------------------------------------*
36 * For interactive use: "Raise" prints out the exception.                    *
37 *---------------------------------------------------------------------------*)
38
39fun try f x = f x handle e => Feedback.Raise e
40
41fun assert P x = assert_exn P x (ERR "assert" "predicate not true")
42
43(*---------------------------------------------------------------------------*
44 *        Common list operations                                             *
45 *---------------------------------------------------------------------------*)
46
47(* turning lists into tuples *)
48
49fun singleton_of_list [x] = x
50  | singleton_of_list _ = raise ERR "singleton_of_list" "not a list of length 1"
51
52fun pair_of_list [x, y] = (x, y)
53  | pair_of_list _ = raise ERR "pair_of_list" "not a list of length 2"
54
55fun triple_of_list [x, y, z] = (x, y, z)
56  | triple_of_list _ = raise ERR "triple_of_list" "not a list of length 3"
57
58fun quadruple_of_list [x1, x2, x3, x4] = (x1, x2, x3, x4)
59  | quadruple_of_list _ = raise ERR "quadruple_of_list" "not a list of length 4"
60
61fun tryfind f =
62   let
63      fun F [] = raise ERR "tryfind" "all applications failed"
64        | F (h :: t) = f h handle Interrupt => raise Interrupt | _ => F t
65   in
66      F
67   end
68
69(* Counting starts from 1 *)
70
71fun el n l =
72   if n < 1
73      then raise ERR "el" "index too small"
74   else let
75           fun elem (_, []) = raise ERR "el" "index too large"
76             | elem (1, h :: _) = h
77             | elem (n, _ :: rst) = elem (n - 1, rst)
78        in
79           elem (n, l)
80        end
81
82fun index P l =
83   let
84      fun idx (i, []) = raise ERR "index" "no such element"
85        | idx (i, y :: l) = if P y then i else idx (i + 1, l)
86   in
87      idx (0, l)
88   end
89
90fun map2 f L1 L2 =
91   let
92      fun mp2 [] [] L = rev L
93        | mp2 (a1 :: rst1) (a2 :: rst2) L = mp2 rst1 rst2 (f a1 a2 :: L)
94        | mp2 _ _ _ = raise ERR "map2" "different length lists"
95   in
96      mp2 L1 L2 []
97   end
98
99fun all2 P =
100   let
101      fun every2 [] [] = true
102        | every2 (a1 :: rst1) (a2 :: rst2) = P a1 a2 andalso every2 rst1 rst2
103        | every2  _ _ = raise ERR "all2" "different length lists"
104   in
105      every2
106   end
107
108fun first P =
109   let
110      fun oneth (a :: rst) = if P a then a else oneth rst
111        | oneth [] = raise ERR "first" "unsatisfied predicate"
112   in
113      oneth
114   end
115
116fun split_after n alist =
117   if n >= 0
118      then let
119              fun spa 0 (L, R) = (rev L, R)
120                | spa n (L, a :: rst) = spa (n - 1) (a :: L, rst)
121                | spa _ _ = raise ERR "split_after" "index too big"
122           in
123              spa n ([], alist)
124           end
125   else raise ERR "split_after" "negative index"
126
127fun itlist2 f L1 L2 base_value =
128   let
129      fun itl ([], []) = base_value
130        | itl (a :: rst1, b :: rst2) = f a b (itl (rst1, rst2))
131        | itl _ = raise ERR "itlist2" "lists of different length"
132   in
133      itl (L1, L2)
134   end
135
136fun rev_itlist2 f L1 L2 =
137  let
138     fun rev_it2 ([], []) base = base
139       | rev_it2 (a :: rst1, b :: rst2) base = rev_it2 (rst1, rst2) (f a b base)
140       | rev_it2 _ _ = raise ERR"rev_itlist2" "lists of different length"
141  in
142     rev_it2 (L1, L2)
143  end
144
145fun end_itlist f =
146   let
147      fun endit [] = raise ERR "end_itlist" "list too short"
148        | endit [x] = x
149        | endit (h :: t) = f h (endit t)
150   in
151      endit
152   end
153
154fun get_first f l =
155   case l of
156      [] => NONE
157    | h :: t => (case f h of NONE => get_first f t | some => some)
158
159fun zip [] [] = []
160  | zip (a :: b) (c :: d) = (a, c) :: zip b d
161  | zip _ _ = raise ERR "zip" "different length lists"
162
163fun combine (l1, l2) = zip l1 l2
164
165fun butlast l =
166   fst (front_last l) handle HOL_ERR _ => raise ERR "butlast" "empty list"
167
168fun last [] = raise ERR "last" "empty list"
169  | last [x] = x
170  | last (_ :: t) = last t
171
172fun pluck P =
173   let
174      fun pl _ [] = raise ERR "pluck" "predicate not satisfied"
175        | pl A (h :: t) =
176            if P h then (h, List.revAppend (A, t)) else pl (h :: A) t
177   in
178      pl []
179   end
180
181fun trypluck f =
182   let
183      fun try acc [] = raise ERR "trypluck" "no successful fn. application"
184        | try acc (h :: t) =
185             case total f h of
186                NONE => try (h :: acc) t
187              | SOME v => (v, List.revAppend (acc, t))
188   in
189      try []
190   end
191
192(* apnth : ('a -> 'a) -> int -> 'a list -> 'a list
193  apply a function to the nth member of a list *)
194fun apnth f 0 (y :: ys) = f y :: ys
195  | apnth f n (y :: ys) = y :: apnth f (n-1) ys
196  | apnth f n [] = raise ERR "apnth" "list too short (or -ve index)"
197
198fun mapshape [] _ _ =  []
199  | mapshape (n :: nums) (f :: funcs) all_args =
200     let
201        val (fargs, rst) = split_after n all_args
202     in
203        f fargs :: mapshape nums funcs rst
204     end
205  | mapshape _ _ _ = raise ERR "mapshape" "irregular lists"
206
207(*---------------------------------------------------------------------------*
208 * Assoc lists.                                                              *
209 *---------------------------------------------------------------------------*)
210
211fun assoc item =
212   let
213      fun assc ((key, ob) :: rst) = if item = key then ob else assc rst
214        | assc [] = raise ERR "assoc" "not found"
215   in
216      assc
217   end
218
219fun rev_assoc item =
220   let
221      fun assc ((ob, key) :: rst) = if item = key then ob else assc rst
222        | assc [] = raise ERR "assoc" "not found"
223   in
224      assc
225   end
226
227fun op_assoc eq_func k l =
228  case l of
229      [] => raise ERR "op_assoc" "not found"
230    | (key,ob) :: rst => if eq_func k key then ob else op_assoc eq_func k rst
231
232fun op_rev_assoc eq_func k l =
233  case l of
234      [] => raise ERR "op_rev_assoc" "not found"
235    | (ob,key) :: rest => if eq_func k key then ob
236                          else op_rev_assoc eq_func k rest
237
238(*---------------------------------------------------------------------------*)
239(* Topologically sort a list wrt partial order R.                            *)
240(*---------------------------------------------------------------------------*)
241
242fun topsort R =
243   let
244      fun pred_of y x = R x y
245      fun a1 l1 l2 = l1 @ l2
246      fun a2 l1 l2 = foldl (op ::) l2 l1
247      fun deps [] _ acc = acc
248        | deps (h :: t) front (nodeps, rst) =
249            let
250               val pred_of_h = pred_of h
251               val hdep = exists pred_of_h t orelse exists pred_of_h front
252               val acc = if hdep then (nodeps, h :: rst) else (h :: nodeps, rst)
253            in
254               deps t (h :: front) acc
255            end
256      fun loop _ [] = []
257        | loop (a1, a2) l =
258            case (deps l [] ([], [])) of
259              ([], _) => raise ERR "topsort" "cyclic dependency"
260            | (nodeps, rst) => a1 nodeps (loop (a2, a1) rst)
261   in
262      loop (a2, a1)
263   end
264
265(* for the following two implementations,
266   each node appears before all its dependencies
267   in the returned list *)
268
269(* O(n*log(n)) time version
270   deps = map from nodes to adjacency lists *)
271
272fun dict_topsort deps =
273   let
274      open Redblackmap
275      val deps = transform (fn ls => ref (SOME ls)) deps
276      fun visit (n, ls) =
277         let
278            val r = find (deps, n)
279         in
280            case !r of
281               NONE => ls
282             | SOME dl => let
283                             val _ = r := NONE
284                             val ls = List.foldl visit ls dl
285                          in
286                             n :: ls
287                          end
288         end
289      fun v (n, _, ls) = visit (n, ls)
290   in
291      foldl v [] deps
292   end
293
294(*---------------------------------------------------------------------------*
295 * Strings.                                                                  *
296 *---------------------------------------------------------------------------*)
297
298val string_to_int =
299   partial (ERR "string_to_int" "not convertable") Int.fromString
300
301val saying = ref true
302
303fun say s = if !saying then !Feedback.MESG_outstream s else ()
304
305fun unprime s =
306   let
307     val n = size s - 1
308   in
309      if 0 <= n andalso String.sub (s, n) = #"'"
310         then String.substring (s, 0, n)
311      else raise ERR "unprime" "string doesn't end with a prime"
312   end
313
314fun extract_pc s =
315    let
316      (* c = # of primes seen;
317         i = current index into string, terminate on -1
318         -- does right thing on UTF8 encoded codepoints
319       *)
320      fun recurse c i =
321          if i < 0 then ("", c)
322          else if String.sub(s,i) = #"'" then recurse (c + 1) (i - 1)
323          else (String.substring(s,0,i+1), c)
324    in
325      recurse 0 (String.size s - 1)
326    end
327
328
329fun unprefix pfx s =
330   if String.isPrefix pfx s
331      then String.extract (s, size pfx, NONE)
332   else raise ERR "unprefix" "1st argument is not a prefix of 2nd argument"
333
334fun ppstring pf x = HOLPP.pp_to_string (!Globals.linewidth) pf x
335
336fun delete_trailing_wspace s =
337  let
338    val toks = String.fields (equal #"\n") s
339    fun do1 i s =
340      if i < 0 then ""
341      else if Char.isSpace (String.sub(s,i)) then do1 (i - 1) s
342      else String.extract(s,0,SOME(i + 1))
343    fun remove_rptd_nls i cnt s =
344      if i < 0 then if cnt > 0 then "\n" else ""
345      else if String.sub(s,i) = #"\n" then
346        remove_rptd_nls (i - 1) (cnt + 1) s
347      else String.extract(s,0,SOME(i + 1 + (if cnt > 0 then 1 else 0)))
348    val s1 = String.concatWith "\n" (map (fn s => do1 (size s - 1) s) toks)
349  in
350    remove_rptd_nls (size s1 - 1) 0 s1
351  end
352
353(*---------------------------------------------------------------------------*
354 * Timing                                                                    *
355 *---------------------------------------------------------------------------*)
356
357local
358   val second = Time.fromReal 1.0
359   val minute = Time.fromReal 60.0
360   val year0 = Date.year (Date.fromTimeUniv Time.zeroTime)
361   fun to_str i u = if i = 0 then "" else Int.toString i ^ u
362in
363   fun time_to_string t =
364      if Time.< (t, second)
365         then Time.fmt 5 t ^ "s"
366      else if Time.< (t, minute)
367         then Time.fmt 1 t ^ "s"
368      else let
369              val d = Date.fromTimeUniv t
370              val years = Date.year d - year0
371              val days = Date.yearDay d
372              val hours = Date.hour d
373              val minutes = Date.minute d
374           in
375              if years + days + hours = 0 andalso minutes < 10 then
376                 to_str minutes "m" ^ Date.fmt "%Ss" d
377              else to_str years "y" ^ to_str days "d" ^ to_str hours "h" ^
378                   Date.fmt "%Mm%Ss" d
379           end
380end
381
382fun start_time () = Timer.startCPUTimer ()
383
384fun end_time timer =
385   let
386      val {sys, usr} = Timer.checkCPUTimer timer
387      val gc = Timer.checkGCTime timer
388   in
389      say ("runtime: " ^ time_to_string usr ^ ",\
390       \    gctime: " ^ time_to_string gc ^ ", \
391       \    systime: " ^ time_to_string sys ^ ".\n")
392   end
393
394fun time f x =
395   let
396      val timer = start_time ()
397      val y = f x handle e => (end_time timer; raise e)
398   in
399      end_time timer; y
400   end
401
402fun start_real_time () = Timer.startRealTimer ()
403
404fun end_real_time timer =
405  say ("realtime: " ^ Time.toString (Timer.checkRealTimer timer) ^ "s\n")
406
407fun real_time f x =
408   let
409      val timer = start_real_time ()
410      val y = f x handle e => (end_real_time timer; raise e)
411   in
412      end_real_time timer; y
413   end
414
415(* set helpers/abbreviations *)
416type 'a set = 'a HOLset.set
417fun op Un p = HOLset.union p
418fun op Isct p = HOLset.intersection p
419fun op -- p = HOLset.difference p
420fun op IN (e,s) = HOLset.member(s,e)
421
422(* more equality function functions *)
423fun subst_eq aeq beq = list_eq (redres_eq aeq beq)
424
425end (* Lib *)
426