| author | urbanc | 
| Tue, 27 Mar 2007 19:13:28 +0200 | |
| changeset 22534 | bd4b954e85ee | 
| parent 21065 | 42669b5bf98e | 
| child 23655 | d2d1138e0ddc | 
| permissions | -rw-r--r-- | 
| 6118 | 1 | (* Title: Pure/General/table.ML | 
| 5015 | 2 | ID: $Id$ | 
| 15014 | 3 | Author: Markus Wenzel and Stefan Berghofer, TU Muenchen | 
| 5015 | 4 | |
| 16342 | 5 | Generic tables. Efficient purely functional implementation using | 
| 6 | balanced 2-3 trees. | |
| 5015 | 7 | *) | 
| 8 | ||
| 9 | signature KEY = | |
| 10 | sig | |
| 11 | type key | |
| 12 | val ord: key * key -> order | |
| 13 | end; | |
| 14 | ||
| 15 | signature TABLE = | |
| 16 | sig | |
| 17 | type key | |
| 18 | type 'a table | |
| 19 | exception DUP of key | |
| 20 | exception DUPS of key list | |
| 19031 | 21 | exception SAME | 
| 15014 | 22 | exception UNDEF of key | 
| 5015 | 23 | val empty: 'a table | 
| 24 | val is_empty: 'a table -> bool | |
| 5681 | 25 |   val map: ('a -> 'b) -> 'a table -> 'b table
 | 
| 16446 | 26 | val map': (key -> 'a -> 'b) -> 'a table -> 'b table | 
| 27 | val fold: (key * 'b -> 'a -> 'a) -> 'b table -> 'a -> 'a | |
| 17579 | 28 | val fold_map: (key * 'b -> 'a -> 'c * 'a) -> 'b table -> 'a -> 'c table * 'a | 
| 5015 | 29 | val dest: 'a table -> (key * 'a) list | 
| 5681 | 30 | val keys: 'a table -> key list | 
| 8409 | 31 | val min_key: 'a table -> key option | 
| 15160 | 32 | val max_key: 'a table -> key option | 
| 7061 | 33 | val exists: (key * 'a -> bool) -> 'a table -> bool | 
| 16192 | 34 | val forall: (key * 'a -> bool) -> 'a table -> bool | 
| 16887 | 35 | val defined: 'a table -> key -> bool | 
| 17412 | 36 | val lookup: 'a table -> key -> 'a option | 
| 37 | val update: (key * 'a) -> 'a table -> 'a table | |
| 38 | val update_new: (key * 'a) -> 'a table -> 'a table (*exception DUP*) | |
| 17179 | 39 | val default: key * 'a -> 'a table -> 'a table | 
| 15665 | 40 |   val map_entry: key -> ('a -> 'a) -> 'a table -> 'a table
 | 
| 20141 | 41 |   val map_default: (key * 'a) -> ('a -> 'a) -> 'a table -> 'a table
 | 
| 12287 | 42 | val make: (key * 'a) list -> 'a table (*exception DUPS*) | 
| 43 | val extend: 'a table * (key * 'a) list -> 'a table (*exception DUPS*) | |
| 19031 | 44 | val join: (key -> 'a * 'a -> 'a) (*exception DUP/SAME*) -> | 
| 16446 | 45 | 'a table * 'a table -> 'a table (*exception DUPS*) | 
| 12287 | 46 |   val merge: ('a * 'a -> bool) -> 'a table * 'a table -> 'a table      (*exception DUPS*)
 | 
| 15665 | 47 | val delete: key -> 'a table -> 'a table (*exception UNDEF*) | 
| 15761 | 48 | val delete_safe: key -> 'a table -> 'a table | 
| 16466 | 49 |   val member: ('b * 'a -> bool) -> 'a table -> key * 'b -> bool
 | 
| 15761 | 50 |   val insert: ('a * 'a -> bool) -> key * 'a -> 'a table -> 'a table    (*exception DUP*)
 | 
| 16139 | 51 |   val remove: ('b * 'a -> bool) -> key * 'b -> 'a table -> 'a table
 | 
| 18946 | 52 | val lookup_list: 'a list table -> key -> 'a list | 
| 53 | val update_list: (key * 'a) -> 'a list table -> 'a list table | |
| 19506 | 54 |   val insert_list: ('a * 'a -> bool) -> key * 'a -> 'a list table -> 'a list table
 | 
| 18946 | 55 |   val remove_list: ('b * 'a -> bool) -> key * 'b -> 'a list table -> 'a list table
 | 
| 56 | val make_list: (key * 'a) list -> 'a list table | |
| 57 | val dest_list: 'a list table -> (key * 'a) list | |
| 58 |   val merge_list: ('a * 'a -> bool) ->
 | |
| 15761 | 59 | 'a list table * 'a list table -> 'a list table (*exception DUPS*) | 
| 5015 | 60 | end; | 
| 61 | ||
| 62 | functor TableFun(Key: KEY): TABLE = | |
| 63 | struct | |
| 64 | ||
| 65 | ||
| 66 | (* datatype table *) | |
| 67 | ||
| 68 | type key = Key.key; | |
| 69 | ||
| 70 | datatype 'a table = | |
| 71 | Empty | | |
| 72 | Branch2 of 'a table * (key * 'a) * 'a table | | |
| 73 | Branch3 of 'a table * (key * 'a) * 'a table * (key * 'a) * 'a table; | |
| 74 | ||
| 75 | exception DUP of key; | |
| 76 | exception DUPS of key list; | |
| 77 | ||
| 78 | ||
| 5681 | 79 | (* empty *) | 
| 80 | ||
| 5015 | 81 | val empty = Empty; | 
| 82 | ||
| 83 | fun is_empty Empty = true | |
| 84 | | is_empty _ = false; | |
| 85 | ||
| 5681 | 86 | |
| 87 | (* map and fold combinators *) | |
| 88 | ||
| 16657 | 89 | fun map_table f = | 
| 90 | let | |
| 91 | fun map Empty = Empty | |
| 92 | | map (Branch2 (left, (k, x), right)) = | |
| 93 | Branch2 (map left, (k, f k x), map right) | |
| 94 | | map (Branch3 (left, (k1, x1), mid, (k2, x2), right)) = | |
| 95 | Branch3 (map left, (k1, f k1 x1), map mid, (k2, f k2 x2), map right); | |
| 96 | in map end; | |
| 5681 | 97 | |
| 16657 | 98 | fun fold_table f = | 
| 99 | let | |
| 100 | fun fold Empty x = x | |
| 101 | | fold (Branch2 (left, p, right)) x = | |
| 102 | fold right (f p (fold left x)) | |
| 103 | | fold (Branch3 (left, p1, mid, p2, right)) x = | |
| 104 | fold right (f p2 (fold mid (f p1 (fold left x)))); | |
| 105 | in fold end; | |
| 5681 | 106 | |
| 17709 | 107 | fun fold_map_table f = | 
| 17579 | 108 | let | 
| 109 | fun fold_map Empty s = (Empty, s) | |
| 110 | | fold_map (Branch2 (left, p as (k, x), right)) s = | |
| 111 | s | |
| 112 | |> fold_map left | |
| 113 | ||>> f p | |
| 114 | ||>> fold_map right | |
| 115 | |-> (fn ((l, e), r) => pair (Branch2 (l, (k, e), r))) | |
| 116 | | fold_map (Branch3 (left, p1 as (k1, x1), mid, p2 as (k2, x2), right)) s = | |
| 117 | s | |
| 118 | |> fold_map left | |
| 119 | ||>> f p1 | |
| 120 | ||>> fold_map mid | |
| 121 | ||>> f p2 | |
| 122 | ||>> fold_map right | |
| 123 | |-> (fn ((((l, e1), m), e2), r) => pair (Branch3 (l, (k1, e1), m, (k2, e2), r))) | |
| 124 | in fold_map end; | |
| 125 | ||
| 16446 | 126 | fun dest tab = rev (fold_table cons tab []); | 
| 127 | fun keys tab = rev (fold_table (cons o #1) tab []); | |
| 16192 | 128 | |
| 129 | local exception TRUE in | |
| 130 | ||
| 131 | fun exists pred tab = | |
| 16446 | 132 | (fold_table (fn p => fn () => if pred p then raise TRUE else ()) tab (); false) | 
| 16192 | 133 | handle TRUE => true; | 
| 134 | ||
| 135 | fun forall pred = not o exists (not o pred); | |
| 136 | ||
| 137 | end; | |
| 5015 | 138 | |
| 15531 | 139 | fun min_key Empty = NONE | 
| 16864 | 140 | | min_key (Branch2 (Empty, (k, _), _)) = SOME k | 
| 141 | | min_key (Branch3 (Empty, (k, _), _, _, _)) = SOME k | |
| 142 | | min_key (Branch2 (left, _, _)) = min_key left | |
| 143 | | min_key (Branch3 (left, _, _, _, _)) = min_key left; | |
| 8409 | 144 | |
| 15531 | 145 | fun max_key Empty = NONE | 
| 16864 | 146 | | max_key (Branch2 (_, (k, _), Empty)) = SOME k | 
| 147 | | max_key (Branch3 (_, _, _, (k, _), Empty)) = SOME k | |
| 148 | | max_key (Branch2 (_, _, right)) = max_key right | |
| 149 | | max_key (Branch3 (_, _, _, _, right)) = max_key right; | |
| 15665 | 150 | |
| 5015 | 151 | |
| 152 | (* lookup *) | |
| 153 | ||
| 19031 | 154 | fun lookup tab key = | 
| 155 | let | |
| 156 | fun look Empty = NONE | |
| 157 | | look (Branch2 (left, (k, x), right)) = | |
| 158 | (case Key.ord (key, k) of | |
| 159 | LESS => look left | |
| 160 | | EQUAL => SOME x | |
| 161 | | GREATER => look right) | |
| 162 | | look (Branch3 (left, (k1, x1), mid, (k2, x2), right)) = | |
| 163 | (case Key.ord (key, k1) of | |
| 164 | LESS => look left | |
| 165 | | EQUAL => SOME x1 | |
| 166 | | GREATER => | |
| 167 | (case Key.ord (key, k2) of | |
| 168 | LESS => look mid | |
| 169 | | EQUAL => SOME x2 | |
| 170 | | GREATER => look right)); | |
| 171 | in look tab end; | |
| 5015 | 172 | |
| 19031 | 173 | fun defined tab key = | 
| 174 | let | |
| 175 | fun def Empty = false | |
| 176 | | def (Branch2 (left, (k, x), right)) = | |
| 177 | (case Key.ord (key, k) of | |
| 178 | LESS => def left | |
| 179 | | EQUAL => true | |
| 180 | | GREATER => def right) | |
| 181 | | def (Branch3 (left, (k1, x1), mid, (k2, x2), right)) = | |
| 182 | (case Key.ord (key, k1) of | |
| 183 | LESS => def left | |
| 184 | | EQUAL => true | |
| 185 | | GREATER => | |
| 186 | (case Key.ord (key, k2) of | |
| 187 | LESS => def mid | |
| 188 | | EQUAL => true | |
| 189 | | GREATER => def right)); | |
| 190 | in def tab end; | |
| 16887 | 191 | |
| 5015 | 192 | |
| 19031 | 193 | (* modify *) | 
| 5015 | 194 | |
| 195 | datatype 'a growth = | |
| 196 | Stay of 'a table | | |
| 197 | Sprout of 'a table * (key * 'a) * 'a table; | |
| 198 | ||
| 15761 | 199 | exception SAME; | 
| 200 | ||
| 15665 | 201 | fun modify key f tab = | 
| 202 | let | |
| 203 | fun modfy Empty = Sprout (Empty, (key, f NONE), Empty) | |
| 204 | | modfy (Branch2 (left, p as (k, x), right)) = | |
| 205 | (case Key.ord (key, k) of | |
| 206 | LESS => | |
| 207 | (case modfy left of | |
| 208 | Stay left' => Stay (Branch2 (left', p, right)) | |
| 209 | | Sprout (left1, q, left2) => Stay (Branch3 (left1, q, left2, p, right))) | |
| 210 | | EQUAL => Stay (Branch2 (left, (k, f (SOME x)), right)) | |
| 211 | | GREATER => | |
| 212 | (case modfy right of | |
| 213 | Stay right' => Stay (Branch2 (left, p, right')) | |
| 214 | | Sprout (right1, q, right2) => | |
| 215 | Stay (Branch3 (left, p, right1, q, right2)))) | |
| 216 | | modfy (Branch3 (left, p1 as (k1, x1), mid, p2 as (k2, x2), right)) = | |
| 217 | (case Key.ord (key, k1) of | |
| 5015 | 218 | LESS => | 
| 15665 | 219 | (case modfy left of | 
| 220 | Stay left' => Stay (Branch3 (left', p1, mid, p2, right)) | |
| 221 | | Sprout (left1, q, left2) => | |
| 222 | Sprout (Branch2 (left1, q, left2), p1, Branch2 (mid, p2, right))) | |
| 223 | | EQUAL => Stay (Branch3 (left, (k1, f (SOME x1)), mid, p2, right)) | |
| 5015 | 224 | | GREATER => | 
| 15665 | 225 | (case Key.ord (key, k2) of | 
| 226 | LESS => | |
| 227 | (case modfy mid of | |
| 228 | Stay mid' => Stay (Branch3 (left, p1, mid', p2, right)) | |
| 229 | | Sprout (mid1, q, mid2) => | |
| 230 | Sprout (Branch2 (left, p1, mid1), q, Branch2 (mid2, p2, right))) | |
| 231 | | EQUAL => Stay (Branch3 (left, p1, mid, (k2, f (SOME x2)), right)) | |
| 232 | | GREATER => | |
| 233 | (case modfy right of | |
| 234 | Stay right' => Stay (Branch3 (left, p1, mid, p2, right')) | |
| 235 | | Sprout (right1, q, right2) => | |
| 236 | Sprout (Branch2 (left, p1, mid), p2, Branch2 (right1, q, right2))))); | |
| 5015 | 237 | |
| 15665 | 238 | in | 
| 239 | (case modfy tab of | |
| 240 | Stay tab' => tab' | |
| 241 | | Sprout br => Branch2 br) | |
| 242 | handle SAME => tab | |
| 243 | end; | |
| 5015 | 244 | |
| 17412 | 245 | fun update (key, x) tab = modify key (fn _ => x) tab; | 
| 246 | fun update_new (key, x) tab = modify key (fn NONE => x | SOME _ => raise DUP key) tab; | |
| 17709 | 247 | fun default (key, x) tab = modify key (fn NONE => x | SOME _ => raise SAME) tab; | 
| 15761 | 248 | fun map_entry key f = modify key (fn NONE => raise SAME | SOME x => f x); | 
| 20141 | 249 | fun map_default (key, x) f = modify key (fn NONE => f x | SOME x => f x); | 
| 5015 | 250 | |
| 251 | ||
| 15014 | 252 | (* delete *) | 
| 253 | ||
| 15665 | 254 | exception UNDEF of key; | 
| 255 | ||
| 256 | local | |
| 257 | ||
| 258 | fun compare NONE (k2, _) = LESS | |
| 259 | | compare (SOME k1) (k2, _) = Key.ord (k1, k2); | |
| 15014 | 260 | |
| 261 | fun if_eq EQUAL x y = x | |
| 262 | | if_eq _ x y = y; | |
| 263 | ||
| 15531 | 264 | fun del (SOME k) Empty = raise UNDEF k | 
| 265 | | del NONE (Branch2 (Empty, p, Empty)) = (p, (true, Empty)) | |
| 266 | | del NONE (Branch3 (Empty, p, Empty, q, Empty)) = | |
| 15014 | 267 | (p, (false, Branch2 (Empty, q, Empty))) | 
| 15665 | 268 | | del k (Branch2 (Empty, p, Empty)) = (case compare k p of | 
| 16002 | 269 | EQUAL => (p, (true, Empty)) | _ => raise UNDEF (the k)) | 
| 15665 | 270 | | del k (Branch3 (Empty, p, Empty, q, Empty)) = (case compare k p of | 
| 15014 | 271 | EQUAL => (p, (false, Branch2 (Empty, q, Empty))) | 
| 15665 | 272 | | _ => (case compare k q of | 
| 15014 | 273 | EQUAL => (q, (false, Branch2 (Empty, p, Empty))) | 
| 16002 | 274 | | _ => raise UNDEF (the k))) | 
| 15665 | 275 | | del k (Branch2 (l, p, r)) = (case compare k p of | 
| 15014 | 276 | LESS => (case del k l of | 
| 277 | (p', (false, l')) => (p', (false, Branch2 (l', p, r))) | |
| 278 | | (p', (true, l')) => (p', case r of | |
| 279 | Branch2 (rl, rp, rr) => | |
| 280 | (true, Branch3 (l', p, rl, rp, rr)) | |
| 281 | | Branch3 (rl, rp, rm, rq, rr) => (false, Branch2 | |
| 282 | (Branch2 (l', p, rl), rp, Branch2 (rm, rq, rr))))) | |
| 15531 | 283 | | ord => (case del (if_eq ord NONE k) r of | 
| 15014 | 284 | (p', (false, r')) => (p', (false, Branch2 (l, if_eq ord p' p, r'))) | 
| 285 | | (p', (true, r')) => (p', case l of | |
| 286 | Branch2 (ll, lp, lr) => | |
| 287 | (true, Branch3 (ll, lp, lr, if_eq ord p' p, r')) | |
| 288 | | Branch3 (ll, lp, lm, lq, lr) => (false, Branch2 | |
| 289 | (Branch2 (ll, lp, lm), lq, Branch2 (lr, if_eq ord p' p, r')))))) | |
| 15665 | 290 | | del k (Branch3 (l, p, m, q, r)) = (case compare k q of | 
| 291 | LESS => (case compare k p of | |
| 15014 | 292 | LESS => (case del k l of | 
| 293 | (p', (false, l')) => (p', (false, Branch3 (l', p, m, q, r))) | |
| 294 | | (p', (true, l')) => (p', (false, case (m, r) of | |
| 295 | (Branch2 (ml, mp, mr), Branch2 _) => | |
| 296 | Branch2 (Branch3 (l', p, ml, mp, mr), q, r) | |
| 297 | | (Branch3 (ml, mp, mm, mq, mr), _) => | |
| 298 | Branch3 (Branch2 (l', p, ml), mp, Branch2 (mm, mq, mr), q, r) | |
| 299 | | (Branch2 (ml, mp, mr), Branch3 (rl, rp, rm, rq, rr)) => | |
| 300 | Branch3 (Branch2 (l', p, ml), mp, Branch2 (mr, q, rl), rp, | |
| 301 | Branch2 (rm, rq, rr))))) | |
| 15531 | 302 | | ord => (case del (if_eq ord NONE k) m of | 
| 15014 | 303 | (p', (false, m')) => | 
| 304 | (p', (false, Branch3 (l, if_eq ord p' p, m', q, r))) | |
| 305 | | (p', (true, m')) => (p', (false, case (l, r) of | |
| 306 | (Branch2 (ll, lp, lr), Branch2 _) => | |
| 307 | Branch2 (Branch3 (ll, lp, lr, if_eq ord p' p, m'), q, r) | |
| 308 | | (Branch3 (ll, lp, lm, lq, lr), _) => | |
| 309 | Branch3 (Branch2 (ll, lp, lm), lq, | |
| 310 | Branch2 (lr, if_eq ord p' p, m'), q, r) | |
| 311 | | (_, Branch3 (rl, rp, rm, rq, rr)) => | |
| 312 | Branch3 (l, if_eq ord p' p, Branch2 (m', q, rl), rp, | |
| 313 | Branch2 (rm, rq, rr)))))) | |
| 15531 | 314 | | ord => (case del (if_eq ord NONE k) r of | 
| 15014 | 315 | (q', (false, r')) => | 
| 316 | (q', (false, Branch3 (l, p, m, if_eq ord q' q, r'))) | |
| 317 | | (q', (true, r')) => (q', (false, case (l, m) of | |
| 318 | (Branch2 _, Branch2 (ml, mp, mr)) => | |
| 319 | Branch2 (l, p, Branch3 (ml, mp, mr, if_eq ord q' q, r')) | |
| 320 | | (_, Branch3 (ml, mp, mm, mq, mr)) => | |
| 321 | Branch3 (l, p, Branch2 (ml, mp, mm), mq, | |
| 322 | Branch2 (mr, if_eq ord q' q, r')) | |
| 323 | | (Branch3 (ll, lp, lm, lq, lr), Branch2 (ml, mp, mr)) => | |
| 324 | Branch3 (Branch2 (ll, lp, lm), lq, Branch2 (lr, p, ml), mp, | |
| 325 | Branch2 (mr, if_eq ord q' q, r')))))); | |
| 326 | ||
| 15665 | 327 | in | 
| 328 | ||
| 15761 | 329 | fun delete key tab = snd (snd (del (SOME key) tab)); | 
| 330 | fun delete_safe key tab = delete key tab handle UNDEF _ => tab; | |
| 15014 | 331 | |
| 15665 | 332 | end; | 
| 333 | ||
| 15014 | 334 | |
| 19031 | 335 | (* membership operations *) | 
| 16466 | 336 | |
| 337 | fun member eq tab (key, x) = | |
| 17412 | 338 | (case lookup tab key of | 
| 16466 | 339 | NONE => false | 
| 340 | | SOME y => eq (x, y)); | |
| 15761 | 341 | |
| 342 | fun insert eq (key, x) = | |
| 343 | modify key (fn NONE => x | SOME y => if eq (x, y) then raise SAME else raise DUP key); | |
| 344 | ||
| 345 | fun remove eq (key, x) tab = | |
| 17412 | 346 | (case lookup tab key of | 
| 15761 | 347 | NONE => tab | 
| 348 | | SOME y => if eq (x, y) then delete key tab else tab); | |
| 349 | ||
| 350 | ||
| 19031 | 351 | (* simultaneous modifications *) | 
| 352 | ||
| 353 | fun reject_dups (tab, []) = tab | |
| 354 | | reject_dups (_, dups) = raise DUPS (rev dups); | |
| 355 | ||
| 356 | fun extend (table, args) = | |
| 357 | let | |
| 358 | fun add (key, x) (tab, dups) = | |
| 359 | (update_new (key, x) tab, dups) handle DUP dup => (tab, dup :: dups); | |
| 360 | in reject_dups (fold add args (table, [])) end; | |
| 361 | ||
| 362 | fun make entries = extend (empty, entries); | |
| 5015 | 363 | |
| 12287 | 364 | fun join f (table1, table2) = | 
| 365 | let | |
| 19031 | 366 | fun add (key, y) (tab, dups) = | 
| 367 | let val tab' = modify key (fn NONE => y | SOME x => f key (x, y)) tab | |
| 368 | in (tab', dups) end handle DUP dup => (tab, dup :: dups); | |
| 369 | in reject_dups (fold_table add table2 (table1, [])) end; | |
| 12287 | 370 | |
| 19031 | 371 | fun merge eq = join (fn key => fn xy => if eq xy then raise SAME else raise DUP key); | 
| 5015 | 372 | |
| 373 | ||
| 19031 | 374 | (* list tables *) | 
| 15761 | 375 | |
| 18946 | 376 | fun lookup_list tab key = these (lookup tab key); | 
| 377 | fun update_list (key, x) tab = modify key (fn NONE => [x] | SOME xs => x :: xs) tab; | |
| 5015 | 378 | |
| 20124 | 379 | fun insert_list eq (key, x) = | 
| 380 | modify key (fn NONE => [x] | SOME xs => if Library.member eq xs x then raise SAME else x :: xs); | |
| 19506 | 381 | |
| 18946 | 382 | fun remove_list eq (key, x) tab = | 
| 15761 | 383 | map_entry key (fn xs => (case Library.remove eq x xs of [] => raise UNDEF key | ys => ys)) tab | 
| 384 | handle UNDEF _ => delete key tab; | |
| 5015 | 385 | |
| 18946 | 386 | fun make_list args = fold_rev update_list args empty; | 
| 19482 
9f11af8f7ef9
tuned basic list operators (flat, maps, map_filter);
 wenzelm parents: 
19073diff
changeset | 387 | fun dest_list tab = maps (fn (key, xs) => map (pair key) xs) (dest tab); | 
| 19031 | 388 | fun merge_list eq = join (fn _ => Library.merge eq); | 
| 5015 | 389 | |
| 390 | ||
| 5681 | 391 | (*final declarations of this structure!*) | 
| 16446 | 392 | fun map f = map_table (K f); | 
| 393 | val map' = map_table; | |
| 394 | val fold = fold_table; | |
| 17579 | 395 | val fold_map = fold_map_table; | 
| 5015 | 396 | |
| 397 | end; | |
| 398 | ||
| 16342 | 399 | structure Inttab = TableFun(type key = int val ord = int_ord); | 
| 16687 
51fa05ce0f32
Symtab: use fast_string_ord instead of string_ord -- affects order of Symtab.dest etc.;
 wenzelm parents: 
16657diff
changeset | 400 | structure Symtab = TableFun(type key = string val ord = fast_string_ord); |