annotate src/iflow.sml @ 1212:fc33072c4d33

Replaced Select predicate with special-case handling for one-or-no-rows queries
author Adam Chlipala <adamc@hcoop.net>
date Tue, 06 Apr 2010 15:17:28 -0400
parents 1d4d65245dd3
children e791d93d4616
rev   line source
adamc@1200 1 (* Copyright (c) 2010, Adam Chlipala
adamc@1200 2 * All rights reserved.
adamc@1200 3 *
adamc@1200 4 * Redistribution and use in source and binary forms, with or without
adamc@1200 5 * modification, are permitted provided that the following conditions are met:
adamc@1200 6 *
adamc@1200 7 * - Redistributions of source code must retain the above copyright notice,
adamc@1200 8 * this list of conditions and the following disclaimer.
adamc@1200 9 * - Redistributions in binary form must reproduce the above copyright notice,
adamc@1200 10 * this list of conditions and the following disclaimer in the documentation
adamc@1200 11 * and/or other materials provided with the distribution.
adamc@1200 12 * - The names of contributors may not be used to endorse or promote products
adamc@1200 13 * derived from this software without specific prior written permission.
adamc@1200 14 *
adamc@1200 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
adamc@1200 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
adamc@1200 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
adamc@1200 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
adamc@1200 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
adamc@1200 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
adamc@1200 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
adamc@1200 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
adamc@1200 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
adamc@1200 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
adamc@1200 25 * POSSIBILITY OF SUCH DAMAGE.
adamc@1200 26 *)
adamc@1200 27
adamc@1200 28 structure Iflow :> IFLOW = struct
adamc@1200 29
adamc@1200 30 open Mono
adamc@1200 31
adamc@1207 32 structure IS = IntBinarySet
adamc@1202 33 structure IM = IntBinaryMap
adamc@1202 34
adamc@1200 35 structure SS = BinarySetFn(struct
adamc@1200 36 type ord_key = string
adamc@1200 37 val compare = String.compare
adamc@1200 38 end)
adamc@1200 39
adamc@1200 40 val writers = ["htmlifyInt_w",
adamc@1200 41 "htmlifyFloat_w",
adamc@1200 42 "htmlifyString_w",
adamc@1200 43 "htmlifyBool_w",
adamc@1200 44 "htmlifyTime_w",
adamc@1200 45 "attrifyInt_w",
adamc@1200 46 "attrifyFloat_w",
adamc@1200 47 "attrifyString_w",
adamc@1200 48 "attrifyChar_w",
adamc@1200 49 "urlifyInt_w",
adamc@1200 50 "urlifyFloat_w",
adamc@1200 51 "urlifyString_w",
adamc@1200 52 "urlifyBool_w"]
adamc@1200 53
adamc@1200 54 val writers = SS.addList (SS.empty, writers)
adamc@1200 55
adamc@1200 56 type lvar = int
adamc@1200 57
adamc@1200 58 datatype exp =
adamc@1200 59 Const of Prim.t
adamc@1200 60 | Var of int
adamc@1200 61 | Lvar of lvar
adamc@1200 62 | Func of string * exp list
adamc@1200 63 | Recd of (string * exp) list
adamc@1200 64 | Proj of exp * string
adamc@1200 65 | Finish
adamc@1200 66
adamc@1200 67 datatype reln =
adamc@1207 68 Known
adamc@1207 69 | Sql of string
adamc@1211 70 | DtCon of string
adamc@1200 71 | Eq
adamc@1210 72 | Ne
adamc@1210 73 | Lt
adamc@1210 74 | Le
adamc@1210 75 | Gt
adamc@1210 76 | Ge
adamc@1200 77
adamc@1200 78 datatype prop =
adamc@1200 79 True
adamc@1200 80 | False
adamc@1200 81 | Unknown
adamc@1200 82 | And of prop * prop
adamc@1200 83 | Or of prop * prop
adamc@1200 84 | Reln of reln * exp list
adamc@1212 85 | Cond of exp * prop
adamc@1200 86
adamc@1200 87 local
adamc@1207 88 open Print
adamc@1207 89 val string = PD.string
adamc@1207 90 in
adamc@1207 91
adamc@1207 92 fun p_exp e =
adamc@1207 93 case e of
adamc@1207 94 Const p => Prim.p_t p
adamc@1207 95 | Var n => string ("x" ^ Int.toString n)
adamc@1207 96 | Lvar n => string ("X" ^ Int.toString n)
adamc@1207 97 | Func (f, es) => box [string (f ^ "("),
adamc@1207 98 p_list p_exp es,
adamc@1207 99 string ")"]
adamc@1207 100 | Recd xes => box [string "{",
adamc@1210 101 p_list (fn (x, e) => box [string x,
adamc@1207 102 space,
adamc@1207 103 string "=",
adamc@1207 104 space,
adamc@1207 105 p_exp e]) xes,
adamc@1207 106 string "}"]
adamc@1207 107 | Proj (e, x) => box [p_exp e,
adamc@1207 108 string ("." ^ x)]
adamc@1207 109 | Finish => string "FINISH"
adamc@1207 110
adamc@1210 111 fun p_bop s es =
adamc@1210 112 case es of
adamc@1210 113 [e1, e2] => box [p_exp e1,
adamc@1210 114 space,
adamc@1210 115 string s,
adamc@1210 116 space,
adamc@1210 117 p_exp e2]
adamc@1210 118 | _ => raise Fail "Iflow.p_bop"
adamc@1210 119
adamc@1207 120 fun p_reln r es =
adamc@1207 121 case r of
adamc@1207 122 Known =>
adamc@1207 123 (case es of
adamc@1207 124 [e] => box [string "known(",
adamc@1207 125 p_exp e,
adamc@1207 126 string ")"]
adamc@1207 127 | _ => raise Fail "Iflow.p_reln: Known")
adamc@1207 128 | Sql s => box [string (s ^ "("),
adamc@1207 129 p_list p_exp es,
adamc@1207 130 string ")"]
adamc@1211 131 | DtCon s => box [string (s ^ "("),
adamc@1211 132 p_list p_exp es,
adamc@1211 133 string ")"]
adamc@1210 134 | Eq => p_bop "=" es
adamc@1210 135 | Ne => p_bop "<>" es
adamc@1210 136 | Lt => p_bop "<" es
adamc@1210 137 | Le => p_bop "<=" es
adamc@1210 138 | Gt => p_bop ">" es
adamc@1210 139 | Ge => p_bop ">=" es
adamc@1207 140
adamc@1207 141 fun p_prop p =
adamc@1207 142 case p of
adamc@1207 143 True => string "True"
adamc@1207 144 | False => string "False"
adamc@1207 145 | Unknown => string "??"
adamc@1207 146 | And (p1, p2) => box [string "(",
adamc@1207 147 p_prop p1,
adamc@1207 148 string ")",
adamc@1207 149 space,
adamc@1207 150 string "&&",
adamc@1207 151 space,
adamc@1207 152 string "(",
adamc@1207 153 p_prop p2,
adamc@1207 154 string ")"]
adamc@1207 155 | Or (p1, p2) => box [string "(",
adamc@1207 156 p_prop p1,
adamc@1207 157 string ")",
adamc@1207 158 space,
adamc@1207 159 string "||",
adamc@1207 160 space,
adamc@1207 161 string "(",
adamc@1207 162 p_prop p2,
adamc@1207 163 string ")"]
adamc@1207 164 | Reln (r, es) => p_reln r es
adamc@1212 165 | Cond (e, p) => box [string "(",
adamc@1212 166 p_exp e,
adamc@1212 167 space,
adamc@1212 168 string "==",
adamc@1212 169 space,
adamc@1212 170 p_prop p,
adamc@1212 171 string ")"]
adamc@1207 172
adamc@1207 173 end
adamc@1207 174
adamc@1207 175 local
adamc@1202 176 val count = ref 1
adamc@1200 177 in
adamc@1200 178 fun newLvar () =
adamc@1200 179 let
adamc@1200 180 val n = !count
adamc@1200 181 in
adamc@1200 182 count := n + 1;
adamc@1200 183 n
adamc@1200 184 end
adamc@1200 185 end
adamc@1200 186
adamc@1200 187 fun isKnown e =
adamc@1200 188 case e of
adamc@1200 189 Const _ => true
adamc@1200 190 | Func (_, es) => List.all isKnown es
adamc@1200 191 | Recd xes => List.all (isKnown o #2) xes
adamc@1200 192 | Proj (e, _) => isKnown e
adamc@1200 193 | _ => false
adamc@1200 194
adamc@1200 195 fun isFinish e =
adamc@1200 196 case e of
adamc@1200 197 Finish => true
adamc@1200 198 | _ => false
adamc@1200 199
adamc@1208 200 val unif = ref (IM.empty : exp IM.map)
adamc@1208 201
adamc@1208 202 fun reset () = unif := IM.empty
adamc@1208 203 fun save () = !unif
adamc@1208 204 fun restore x = unif := x
adamc@1208 205
adamc@1200 206 fun simplify e =
adamc@1200 207 case e of
adamc@1200 208 Const _ => e
adamc@1200 209 | Var _ => e
adamc@1208 210 | Lvar n =>
adamc@1208 211 (case IM.find (!unif, n) of
adamc@1208 212 NONE => e
adamc@1208 213 | SOME e => simplify e)
adamc@1200 214 | Func (f, es) =>
adamc@1200 215 let
adamc@1200 216 val es = map simplify es
adamc@1211 217
adamc@1211 218 fun default () = Func (f, es)
adamc@1200 219 in
adamc@1200 220 if List.exists isFinish es then
adamc@1200 221 Finish
adamc@1211 222 else if String.isPrefix "un" f then
adamc@1211 223 case es of
adamc@1211 224 [Func (f', [e])] => if f' = String.extract (f, 2, NONE) then
adamc@1211 225 e
adamc@1211 226 else
adamc@1211 227 default ()
adamc@1211 228 | _ => default ()
adamc@1200 229 else
adamc@1211 230 default ()
adamc@1200 231 end
adamc@1200 232 | Recd xes =>
adamc@1200 233 let
adamc@1200 234 val xes = map (fn (x, e) => (x, simplify e)) xes
adamc@1200 235 in
adamc@1200 236 if List.exists (isFinish o #2) xes then
adamc@1200 237 Finish
adamc@1200 238 else
adamc@1200 239 Recd xes
adamc@1200 240 end
adamc@1200 241 | Proj (e, s) =>
adamc@1200 242 (case simplify e of
adamc@1200 243 Recd xes =>
adamc@1200 244 getOpt (ListUtil.search (fn (x, e') => if x = s then SOME e' else NONE) xes, Recd xes)
adamc@1200 245 | e' =>
adamc@1200 246 if isFinish e' then
adamc@1200 247 Finish
adamc@1200 248 else
adamc@1200 249 Proj (e', s))
adamc@1200 250 | Finish => Finish
adamc@1200 251
adamc@1212 252 datatype atom =
adamc@1212 253 AReln of reln * exp list
adamc@1212 254 | ACond of exp * prop
adamc@1212 255
adamc@1212 256 fun p_atom a =
adamc@1212 257 p_prop (case a of
adamc@1212 258 AReln x => Reln x
adamc@1212 259 | ACond x => Cond x)
adamc@1212 260
adamc@1202 261 fun decomp fals or =
adamc@1200 262 let
adamc@1200 263 fun decomp p k =
adamc@1200 264 case p of
adamc@1200 265 True => k []
adamc@1202 266 | False => fals
adamc@1200 267 | Unknown => k []
adamc@1200 268 | And (p1, p2) =>
adamc@1200 269 decomp p1 (fn ps1 =>
adamc@1200 270 decomp p2 (fn ps2 =>
adamc@1200 271 k (ps1 @ ps2)))
adamc@1200 272 | Or (p1, p2) =>
adamc@1200 273 or (decomp p1 k, fn () => decomp p2 k)
adamc@1212 274 | Reln x => k [AReln x]
adamc@1212 275 | Cond x => k [ACond x]
adamc@1200 276 in
adamc@1200 277 decomp
adamc@1200 278 end
adamc@1200 279
adamc@1202 280 fun lvarIn lv =
adamc@1202 281 let
adamc@1202 282 fun lvi e =
adamc@1202 283 case e of
adamc@1202 284 Const _ => false
adamc@1202 285 | Var _ => false
adamc@1202 286 | Lvar lv' => lv' = lv
adamc@1202 287 | Func (_, es) => List.exists lvi es
adamc@1202 288 | Recd xes => List.exists (lvi o #2) xes
adamc@1202 289 | Proj (e, _) => lvi e
adamc@1202 290 | Finish => false
adamc@1202 291 in
adamc@1202 292 lvi
adamc@1202 293 end
adamc@1202 294
adamc@1212 295 fun lvarInP lv =
adamc@1212 296 let
adamc@1212 297 fun lvi p =
adamc@1212 298 case p of
adamc@1212 299 True => false
adamc@1212 300 | False => false
adamc@1212 301 | Unknown => true
adamc@1212 302 | And (p1, p2) => lvi p1 orelse lvi p2
adamc@1212 303 | Or (p1, p2) => lvi p1 orelse lvi p2
adamc@1212 304 | Reln (_, es) => List.exists (lvarIn lv) es
adamc@1212 305 | Cond (e, p) => lvarIn lv e orelse lvi p
adamc@1212 306 in
adamc@1212 307 lvi
adamc@1212 308 end
adamc@1212 309
adamc@1212 310 fun varIn lv =
adamc@1212 311 let
adamc@1212 312 fun lvi e =
adamc@1212 313 case e of
adamc@1212 314 Const _ => false
adamc@1212 315 | Lvar _ => false
adamc@1212 316 | Var lv' => lv' = lv
adamc@1212 317 | Func (_, es) => List.exists lvi es
adamc@1212 318 | Recd xes => List.exists (lvi o #2) xes
adamc@1212 319 | Proj (e, _) => lvi e
adamc@1212 320 | Finish => false
adamc@1212 321 in
adamc@1212 322 lvi
adamc@1212 323 end
adamc@1212 324
adamc@1212 325 fun varInP lv =
adamc@1212 326 let
adamc@1212 327 fun lvi p =
adamc@1212 328 case p of
adamc@1212 329 True => false
adamc@1212 330 | False => false
adamc@1212 331 | Unknown => false
adamc@1212 332 | And (p1, p2) => lvi p1 orelse lvi p2
adamc@1212 333 | Or (p1, p2) => lvi p1 orelse lvi p2
adamc@1212 334 | Reln (_, es) => List.exists (varIn lv) es
adamc@1212 335 | Cond (e, p) => varIn lv e orelse lvi p
adamc@1212 336 in
adamc@1212 337 lvi
adamc@1212 338 end
adamc@1212 339
adamc@1202 340 fun eq' (e1, e2) =
adamc@1202 341 case (e1, e2) of
adamc@1202 342 (Const p1, Const p2) => Prim.equal (p1, p2)
adamc@1202 343 | (Var n1, Var n2) => n1 = n2
adamc@1202 344
adamc@1202 345 | (Lvar n1, _) =>
adamc@1202 346 (case IM.find (!unif, n1) of
adamc@1202 347 SOME e1 => eq' (e1, e2)
adamc@1202 348 | NONE =>
adamc@1202 349 case e2 of
adamc@1202 350 Lvar n2 =>
adamc@1202 351 (case IM.find (!unif, n2) of
adamc@1202 352 SOME e2 => eq' (e1, e2)
adamc@1202 353 | NONE => n1 = n2
adamc@1208 354 orelse (unif := IM.insert (!unif, n2, e1);
adamc@1202 355 true))
adamc@1202 356 | _ =>
adamc@1202 357 if lvarIn n1 e2 then
adamc@1202 358 false
adamc@1202 359 else
adamc@1202 360 (unif := IM.insert (!unif, n1, e2);
adamc@1202 361 true))
adamc@1202 362
adamc@1202 363 | (_, Lvar n2) =>
adamc@1202 364 (case IM.find (!unif, n2) of
adamc@1202 365 SOME e2 => eq' (e1, e2)
adamc@1202 366 | NONE =>
adamc@1202 367 if lvarIn n2 e1 then
adamc@1202 368 false
adamc@1202 369 else
adamc@1202 370 (unif := IM.insert (!unif, n2, e1);
adamc@1202 371 true))
adamc@1202 372
adamc@1202 373 | (Func (f1, es1), Func (f2, es2)) => f1 = f2 andalso ListPair.allEq eq' (es1, es2)
adamc@1202 374 | (Recd xes1, Recd xes2) => ListPair.allEq (fn ((x1, e1), (x2, e2)) => x1 = x2 andalso eq' (e1, e2)) (xes1, xes2)
adamc@1202 375 | (Proj (e1, s1), Proj (e2, s2)) => eq' (e1, e2) andalso s1 = s2
adamc@1202 376 | (Finish, Finish) => true
adamc@1202 377 | _ => false
adamc@1202 378
adamc@1202 379 fun eq (e1, e2) =
adamc@1202 380 let
adamc@1203 381 val saved = save ()
adamc@1202 382 in
adamc@1202 383 if eq' (simplify e1, simplify e2) then
adamc@1202 384 true
adamc@1202 385 else
adamc@1203 386 (restore saved;
adamc@1202 387 false)
adamc@1202 388 end
adamc@1202 389
adamc@1208 390 val debug = ref false
adamc@1208 391
adamc@1211 392 fun eeq (e1, e2) =
adamc@1211 393 case (e1, e2) of
adamc@1211 394 (Const p1, Const p2) => Prim.equal (p1, p2)
adamc@1211 395 | (Var n1, Var n2) => n1 = n2
adamc@1211 396 | (Lvar n1, Lvar n2) => n1 = n2
adamc@1211 397 | (Func (f1, es1), Func (f2, es2)) => f1 = f2 andalso ListPair.allEq eeq (es1, es2)
adamc@1211 398 | (Recd xes1, Recd xes2) => length xes1 = length xes2 andalso
adamc@1211 399 List.all (fn (x2, e2) =>
adamc@1211 400 List.exists (fn (x1, e1) => x1 = x2 andalso eeq (e1, e2)) xes2) xes1
adamc@1211 401 | (Proj (e1, x1), Proj (e2, x2)) => eeq (e1, e2) andalso x1 = x2
adamc@1211 402 | (Finish, Finish) => true
adamc@1211 403 | _ => false
adamc@1211 404
adamc@1208 405 (* Congruence closure *)
adamc@1208 406 structure Cc :> sig
adamc@1208 407 type t
adamc@1208 408 val empty : t
adamc@1208 409 val assert : t * exp * exp -> t
adamc@1208 410 val query : t * exp * exp -> bool
adamc@1208 411 val allPeers : t * exp -> exp list
adamc@1208 412 end = struct
adamc@1208 413
adamc@1211 414 fun eq (e1, e2) = eeq (simplify e1, simplify e2)
adamc@1208 415
adamc@1208 416 type t = (exp * exp) list
adamc@1208 417
adamc@1208 418 val empty = []
adamc@1208 419
adamc@1208 420 fun lookup (t, e) =
adamc@1208 421 case List.find (fn (e', _) => eq (e', e)) t of
adamc@1208 422 NONE => e
adamc@1208 423 | SOME (_, e2) => lookup (t, e2)
adamc@1208 424
adamc@1208 425 fun allPeers (t, e) =
adamc@1208 426 let
adamc@1208 427 val r = lookup (t, e)
adamc@1208 428 in
adamc@1208 429 r :: List.mapPartial (fn (e1, e2) =>
adamc@1208 430 let
adamc@1208 431 val r' = lookup (t, e2)
adamc@1208 432 in
adamc@1208 433 if eq (r, r') then
adamc@1208 434 SOME e1
adamc@1208 435 else
adamc@1208 436 NONE
adamc@1208 437 end) t
adamc@1208 438 end
adamc@1208 439
adamc@1212 440 fun assert (t, e1, e2) =
adamc@1212 441 let
adamc@1212 442 val r1 = lookup (t, e1)
adamc@1212 443 val r2 = lookup (t, e2)
adamc@1212 444
adamc@1212 445 fun doUn (t', e1, e2) =
adamc@1212 446 case e2 of
adamc@1212 447 Func (f, [e]) =>
adamc@1212 448 if String.isPrefix "un" f then
adamc@1212 449 let
adamc@1212 450 val f' = String.extract (f, 2, NONE)
adamc@1212 451 in
adamc@1212 452 foldl (fn (e', t') =>
adamc@1212 453 case e' of
adamc@1212 454 Func (f'', [e'']) =>
adamc@1212 455 if f'' = f' then
adamc@1212 456 (lookup (t', e1), e'') :: t'
adamc@1212 457 else
adamc@1212 458 t'
adamc@1212 459 | _ => t') t' (allPeers (t, e))
adamc@1212 460 end
adamc@1212 461 else
adamc@1212 462 t'
adamc@1212 463 | _ => t'
adamc@1212 464 in
adamc@1212 465 if eq (r1, r2) then
adamc@1212 466 t
adamc@1212 467 else
adamc@1212 468 doUn (doUn ((r1, r2) :: t, e1, e2), e2, e1)
adamc@1212 469 end
adamc@1212 470
adamc@1212 471 open Print
adamc@1212 472
adamc@1212 473 fun query (t, e1, e2) =
adamc@1212 474 ((*prefaces "CC query" [("e1", p_exp (simplify e1)),
adamc@1212 475 ("e2", p_exp (simplify e2)),
adamc@1212 476 ("t", p_list (fn (e1, e2) => box [p_exp (simplify e1),
adamc@1212 477 space,
adamc@1212 478 PD.string "->",
adamc@1212 479 space,
adamc@1212 480 p_exp (simplify e2)]) t)];*)
adamc@1212 481 eq (lookup (t, e1), lookup (t, e2)))
adamc@1212 482
adamc@1208 483 end
adamc@1208 484
adamc@1208 485 fun rimp cc ((r1, es1), (r2, es2)) =
adamc@1202 486 case (r1, r2) of
adamc@1202 487 (Sql r1', Sql r2') =>
adamc@1202 488 r1' = r2' andalso
adamc@1202 489 (case (es1, es2) of
adamc@1202 490 ([Recd xes1], [Recd xes2]) =>
adamc@1202 491 let
adamc@1203 492 val saved = save ()
adamc@1202 493 in
adamc@1202 494 if List.all (fn (f, e2) =>
adamc@1203 495 case ListUtil.search (fn (f', e1) => if f' = f then SOME e1 else NONE) xes1 of
adamc@1203 496 NONE => true
adamc@1203 497 | SOME e1 => eq (e1, e2)) xes2 then
adamc@1202 498 true
adamc@1202 499 else
adamc@1203 500 (restore saved;
adamc@1202 501 false)
adamc@1202 502 end
adamc@1202 503 | _ => false)
adamc@1202 504 | (Eq, Eq) =>
adamc@1202 505 (case (es1, es2) of
adamc@1202 506 ([x1, y1], [x2, y2]) =>
adamc@1202 507 let
adamc@1203 508 val saved = save ()
adamc@1202 509 in
adamc@1202 510 if eq (x1, x2) andalso eq (y1, y2) then
adamc@1202 511 true
adamc@1202 512 else
adamc@1203 513 (restore saved;
adamc@1208 514 if eq (x1, y2) andalso eq (y1, x2) then
adamc@1208 515 true
adamc@1208 516 else
adamc@1208 517 (restore saved;
adamc@1208 518 false))
adamc@1202 519 end
adamc@1202 520 | _ => false)
adamc@1207 521 | (Known, Known) =>
adamc@1207 522 (case (es1, es2) of
adamc@1208 523 ([Var v], [e2]) =>
adamc@1207 524 let
adamc@1208 525 fun matches e =
adamc@1208 526 case e of
adamc@1208 527 Var v' => v' = v
adamc@1208 528 | Proj (e, _) => matches e
adamc@1211 529 | Func (f, [e]) => String.isPrefix "un" f andalso matches e
adamc@1207 530 | _ => false
adamc@1207 531 in
adamc@1208 532 List.exists matches (Cc.allPeers (cc, e2))
adamc@1207 533 end
adamc@1207 534 | _ => false)
adamc@1202 535 | _ => false
adamc@1202 536
adamc@1202 537 fun imply (p1, p2) =
adamc@1208 538 let
adamc@1208 539 fun doOne doKnown =
adamc@1208 540 decomp true (fn (e1, e2) => e1 andalso e2 ()) p1
adamc@1208 541 (fn hyps =>
adamc@1208 542 decomp false (fn (e1, e2) => e1 orelse e2 ()) p2
adamc@1208 543 (fn goals =>
adamc@1208 544 let
adamc@1208 545 val cc = foldl (fn (p, cc) =>
adamc@1208 546 case p of
adamc@1212 547 AReln (Eq, [e1, e2]) => Cc.assert (cc, e1, e2)
adamc@1208 548 | _ => cc) Cc.empty hyps
adamc@1202 549
adamc@1208 550 fun gls goals onFail =
adamc@1208 551 case goals of
adamc@1208 552 [] => true
adamc@1212 553 | ACond _ :: _ => false
adamc@1212 554 | AReln g :: goals =>
adamc@1208 555 case (doKnown, g) of
adamc@1208 556 (false, (Known, _)) => gls goals onFail
adamc@1208 557 | _ =>
adamc@1208 558 let
adamc@1208 559 fun hps hyps =
adamc@1208 560 case hyps of
adamc@1208 561 [] => onFail ()
adamc@1212 562 | ACond _ :: hyps => hps hyps
adamc@1212 563 | AReln h :: hyps =>
adamc@1208 564 let
adamc@1208 565 val saved = save ()
adamc@1208 566 in
adamc@1208 567 if rimp cc (h, g) then
adamc@1208 568 let
adamc@1208 569 val changed = IM.numItems (!unif)
adamc@1208 570 <> IM.numItems saved
adamc@1208 571 in
adamc@1208 572 gls goals (fn () => (restore saved;
adamc@1208 573 changed andalso hps hyps))
adamc@1208 574 end
adamc@1208 575 else
adamc@1208 576 hps hyps
adamc@1208 577 end
adamc@1208 578 in
adamc@1208 579 (case g of
adamc@1208 580 (Eq, [e1, e2]) => Cc.query (cc, e1, e2)
adamc@1208 581 | _ => false)
adamc@1208 582 orelse hps hyps
adamc@1208 583 end
adamc@1208 584 in
adamc@1212 585 if List.exists (fn AReln (DtCon c1, [e]) =>
adamc@1212 586 List.exists (fn AReln (DtCon c2, [e']) =>
adamc@1211 587 c1 <> c2 andalso
adamc@1211 588 Cc.query (cc, e, e')
adamc@1211 589 | _ => false) hyps
adamc@1211 590 orelse List.exists (fn Func (c2, []) => c1 <> c2
adamc@1211 591 | Finish => true
adamc@1211 592 | _ => false)
adamc@1211 593 (Cc.allPeers (cc, e))
adamc@1211 594 | _ => false) hyps
adamc@1211 595 orelse gls goals (fn () => false) then
adamc@1211 596 true
adamc@1211 597 else
adamc@1212 598 ((*Print.prefaces "Can't prove"
adamc@1212 599 [("hyps", Print.p_list p_atom hyps),
adamc@1212 600 ("goals", Print.p_list p_atom goals)];*)
adamc@1211 601 false)
adamc@1208 602 end))
adamc@1208 603 in
adamc@1208 604 reset ();
adamc@1208 605 doOne false;
adamc@1208 606 doOne true
adamc@1208 607 end
adamc@1200 608
adamc@1200 609 fun patCon pc =
adamc@1200 610 case pc of
adamc@1200 611 PConVar n => "C" ^ Int.toString n
adamc@1200 612 | PConFfi {mod = m, datatyp = d, con = c, ...} => m ^ "." ^ d ^ "." ^ c
adamc@1200 613
adamc@1200 614 datatype chunk =
adamc@1200 615 String of string
adamc@1200 616 | Exp of Mono.exp
adamc@1200 617
adamc@1200 618 fun chunkify e =
adamc@1200 619 case #1 e of
adamc@1200 620 EPrim (Prim.String s) => [String s]
adamc@1207 621 | EStrcat (e1, e2) =>
adamc@1207 622 let
adamc@1207 623 val chs1 = chunkify e1
adamc@1207 624 val chs2 = chunkify e2
adamc@1207 625 in
adamc@1207 626 case chs2 of
adamc@1207 627 String s2 :: chs2' =>
adamc@1207 628 (case List.last chs1 of
adamc@1207 629 String s1 => List.take (chs1, length chs1 - 1) @ String (s1 ^ s2) :: chs2'
adamc@1207 630 | _ => chs1 @ chs2)
adamc@1207 631 | _ => chs1 @ chs2
adamc@1207 632 end
adamc@1200 633 | _ => [Exp e]
adamc@1200 634
adamc@1201 635 type 'a parser = chunk list -> ('a * chunk list) option
adamc@1201 636
adamc@1201 637 fun always v chs = SOME (v, chs)
adamc@1201 638
adamc@1202 639 fun parse p s =
adamc@1202 640 case p (chunkify s) of
adamc@1201 641 SOME (v, []) => SOME v
adamc@1201 642 | _ => NONE
adamc@1201 643
adamc@1201 644 fun const s chs =
adamc@1201 645 case chs of
adamc@1201 646 String s' :: chs => if String.isPrefix s s' then
adamc@1201 647 SOME ((), if size s = size s' then
adamc@1201 648 chs
adamc@1201 649 else
adamc@1201 650 String (String.extract (s', size s, NONE)) :: chs)
adamc@1201 651 else
adamc@1201 652 NONE
adamc@1201 653 | _ => NONE
adamc@1201 654
adamc@1201 655 fun follow p1 p2 chs =
adamc@1201 656 case p1 chs of
adamc@1201 657 NONE => NONE
adamc@1201 658 | SOME (v1, chs) =>
adamc@1201 659 case p2 chs of
adamc@1201 660 NONE => NONE
adamc@1201 661 | SOME (v2, chs) => SOME ((v1, v2), chs)
adamc@1201 662
adamc@1201 663 fun wrap p f chs =
adamc@1201 664 case p chs of
adamc@1201 665 NONE => NONE
adamc@1201 666 | SOME (v, chs) => SOME (f v, chs)
adamc@1201 667
adamc@1209 668 fun wrapP p f chs =
adamc@1209 669 case p chs of
adamc@1209 670 NONE => NONE
adamc@1209 671 | SOME (v, chs) =>
adamc@1209 672 case f v of
adamc@1209 673 NONE => NONE
adamc@1209 674 | SOME r => SOME (r, chs)
adamc@1209 675
adamc@1201 676 fun alt p1 p2 chs =
adamc@1201 677 case p1 chs of
adamc@1201 678 NONE => p2 chs
adamc@1201 679 | v => v
adamc@1201 680
adamc@1207 681 fun altL ps =
adamc@1207 682 case rev ps of
adamc@1207 683 [] => (fn _ => NONE)
adamc@1207 684 | p :: ps =>
adamc@1207 685 foldl (fn (p1, p2) => alt p1 p2) p ps
adamc@1207 686
adamc@1204 687 fun opt p chs =
adamc@1204 688 case p chs of
adamc@1204 689 NONE => SOME (NONE, chs)
adamc@1204 690 | SOME (v, chs) => SOME (SOME v, chs)
adamc@1204 691
adamc@1201 692 fun skip cp chs =
adamc@1201 693 case chs of
adamc@1201 694 String "" :: chs => skip cp chs
adamc@1201 695 | String s :: chs' => if cp (String.sub (s, 0)) then
adamc@1201 696 skip cp (String (String.extract (s, 1, NONE)) :: chs')
adamc@1201 697 else
adamc@1201 698 SOME ((), chs)
adamc@1201 699 | _ => SOME ((), chs)
adamc@1201 700
adamc@1201 701 fun keep cp chs =
adamc@1201 702 case chs of
adamc@1201 703 String "" :: chs => keep cp chs
adamc@1201 704 | String s :: chs' =>
adamc@1201 705 let
adamc@1201 706 val (befor, after) = Substring.splitl cp (Substring.full s)
adamc@1201 707 in
adamc@1201 708 if Substring.isEmpty befor then
adamc@1201 709 NONE
adamc@1201 710 else
adamc@1201 711 SOME (Substring.string befor,
adamc@1201 712 if Substring.isEmpty after then
adamc@1201 713 chs'
adamc@1201 714 else
adamc@1201 715 String (Substring.string after) :: chs')
adamc@1201 716 end
adamc@1201 717 | _ => NONE
adamc@1201 718
adamc@1204 719 fun ws p = wrap (follow (skip (fn ch => ch = #" "))
adamc@1204 720 (follow p (skip (fn ch => ch = #" ")))) (#1 o #2)
adamc@1204 721
adamc@1204 722 fun log name p chs =
adamc@1206 723 (if !debug then
adamc@1206 724 case chs of
adamc@1207 725 String s :: _ => print (name ^ ": " ^ s ^ "\n")
adamc@1206 726 | _ => print (name ^ ": blocked!\n")
adamc@1206 727 else
adamc@1206 728 ();
adamc@1204 729 p chs)
adamc@1201 730
adamc@1201 731 fun list p chs =
adamc@1207 732 altL [wrap (follow p (follow (ws (const ",")) (list p)))
adamc@1207 733 (fn (v, ((), ls)) => v :: ls),
adamc@1207 734 wrap (ws p) (fn v => [v]),
adamc@1207 735 always []] chs
adamc@1201 736
adamc@1201 737 val ident = keep (fn ch => Char.isAlphaNum ch orelse ch = #"_")
adamc@1201 738
adamc@1211 739 val t_ident = wrapP ident (fn s => if String.isPrefix "T_" s then
adamc@1211 740 SOME (String.extract (s, 2, NONE))
adamc@1201 741 else
adamc@1211 742 NONE)
adamc@1211 743 val uw_ident = wrapP ident (fn s => if String.isPrefix "uw_" s andalso size s >= 4 then
adamc@1211 744 SOME (str (Char.toUpper (String.sub (s, 3)))
adamc@1211 745 ^ String.extract (s, 4, NONE))
adamc@1211 746 else
adamc@1211 747 NONE)
adamc@1201 748
adamc@1211 749 val field = wrap (follow t_ident
adamc@1201 750 (follow (const ".")
adamc@1201 751 uw_ident))
adamc@1201 752 (fn (t, ((), f)) => (t, f))
adamc@1201 753
adamc@1206 754 datatype Rel =
adamc@1206 755 Exps of exp * exp -> prop
adamc@1206 756 | Props of prop * prop -> prop
adamc@1206 757
adamc@1204 758 datatype sqexp =
adamc@1206 759 SqConst of Prim.t
adamc@1206 760 | Field of string * string
adamc@1206 761 | Binop of Rel * sqexp * sqexp
adamc@1207 762 | SqKnown of sqexp
adamc@1207 763 | Inj of Mono.exp
adamc@1211 764 | SqFunc of string * sqexp
adamc@1211 765 | Count
adamc@1204 766
adamc@1210 767 fun cmp s r = wrap (const s) (fn () => Exps (fn (e1, e2) => Reln (r, [e1, e2])))
adamc@1210 768
adamc@1210 769 val sqbrel = altL [cmp "=" Eq,
adamc@1210 770 cmp "<>" Ne,
adamc@1210 771 cmp "<=" Le,
adamc@1210 772 cmp "<" Lt,
adamc@1210 773 cmp ">=" Ge,
adamc@1210 774 cmp ">" Gt,
adamc@1207 775 wrap (const "AND") (fn () => Props And),
adamc@1207 776 wrap (const "OR") (fn () => Props Or)]
adamc@1204 777
adamc@1204 778 datatype ('a, 'b) sum = inl of 'a | inr of 'b
adamc@1204 779
adamc@1209 780 fun string chs =
adamc@1206 781 case chs of
adamc@1209 782 String s :: chs =>
adamc@1209 783 if size s >= 2 andalso String.sub (s, 0) = #"'" then
adamc@1209 784 let
adamc@1209 785 fun loop (cs, acc) =
adamc@1209 786 case cs of
adamc@1209 787 [] => NONE
adamc@1209 788 | c :: cs =>
adamc@1209 789 if c = #"'" then
adamc@1209 790 SOME (String.implode (rev acc), cs)
adamc@1209 791 else if c = #"\\" then
adamc@1209 792 case cs of
adamc@1209 793 c :: cs => loop (cs, c :: acc)
adamc@1209 794 | _ => raise Fail "Iflow.string: Unmatched backslash escape"
adamc@1209 795 else
adamc@1209 796 loop (cs, c :: acc)
adamc@1209 797 in
adamc@1209 798 case loop (String.explode (String.extract (s, 1, NONE)), []) of
adamc@1209 799 NONE => NONE
adamc@1209 800 | SOME (s, []) => SOME (s, chs)
adamc@1209 801 | SOME (s, cs) => SOME (s, String (String.implode cs) :: chs)
adamc@1209 802 end
adamc@1209 803 else
adamc@1209 804 NONE
adamc@1209 805 | _ => NONE
adamc@1206 806
adamc@1209 807 val prim =
adamc@1209 808 altL [wrap (follow (wrapP (follow (keep Char.isDigit) (follow (const ".") (keep Char.isDigit)))
adamc@1209 809 (fn (x, ((), y)) => Option.map Prim.Float (Real64.fromString (x ^ "." ^ y))))
adamc@1209 810 (opt (const "::float8"))) #1,
adamc@1209 811 wrap (follow (wrapP (keep Char.isDigit)
adamc@1209 812 (Option.map Prim.Int o Int64.fromString))
adamc@1209 813 (opt (const "::int8"))) #1,
adamc@1209 814 wrap (follow (opt (const "E")) (follow string (opt (const "::text"))))
adamc@1209 815 (Prim.String o #1 o #2)]
adamc@1206 816
adamc@1207 817 fun known' chs =
adamc@1207 818 case chs of
adamc@1207 819 Exp (EFfi ("Basis", "sql_known"), _) :: chs => SOME ((), chs)
adamc@1207 820 | _ => NONE
adamc@1207 821
adamc@1207 822 fun sqlify chs =
adamc@1207 823 case chs of
adamc@1207 824 Exp (EFfiApp ("Basis", f, [e]), _) :: chs =>
adamc@1207 825 if String.isPrefix "sqlify" f then
adamc@1207 826 SOME (e, chs)
adamc@1207 827 else
adamc@1207 828 NONE
adamc@1207 829 | _ => NONE
adamc@1207 830
adamc@1211 831 fun constK s = wrap (const s) (fn () => s)
adamc@1211 832
adamc@1211 833 val funcName = altL [constK "COUNT",
adamc@1211 834 constK "MIN",
adamc@1211 835 constK "MAX",
adamc@1211 836 constK "SUM",
adamc@1211 837 constK "AVG"]
adamc@1211 838
adamc@1204 839 fun sqexp chs =
adamc@1206 840 log "sqexp"
adamc@1207 841 (altL [wrap prim SqConst,
adamc@1211 842 wrap field Field,
adamc@1207 843 wrap known SqKnown,
adamc@1211 844 wrap func SqFunc,
adamc@1211 845 wrap (const "COUNT(*)") (fn () => Count),
adamc@1207 846 wrap sqlify Inj,
adamc@1211 847 wrap (follow (const "COALESCE(") (follow sqexp (follow (const ",")
adamc@1211 848 (follow (keep (fn ch => ch <> #")")) (const ")")))))
adamc@1211 849 (fn ((), (e, _)) => e),
adamc@1207 850 wrap (follow (ws (const "("))
adamc@1207 851 (follow (wrap
adamc@1207 852 (follow sqexp
adamc@1207 853 (alt
adamc@1207 854 (wrap
adamc@1207 855 (follow (ws sqbrel)
adamc@1207 856 (ws sqexp))
adamc@1207 857 inl)
adamc@1207 858 (always (inr ()))))
adamc@1207 859 (fn (e1, sm) =>
adamc@1207 860 case sm of
adamc@1207 861 inl (bo, e2) => Binop (bo, e1, e2)
adamc@1207 862 | inr () => e1))
adamc@1207 863 (const ")")))
adamc@1207 864 (fn ((), (e, ())) => e)])
adamc@1207 865 chs
adamc@1206 866
adamc@1207 867 and known chs = wrap (follow known' (follow (const "(") (follow sqexp (const ")"))))
adamc@1211 868 (fn ((), ((), (e, ()))) => e) chs
adamc@1211 869
adamc@1211 870 and func chs = wrap (follow funcName (follow (const "(") (follow sqexp (const ")"))))
adamc@1211 871 (fn (f, ((), (e, ()))) => (f, e)) chs
adamc@1211 872
adamc@1211 873 datatype sitem =
adamc@1211 874 SqField of string * string
adamc@1211 875 | SqExp of sqexp * string
adamc@1211 876
adamc@1211 877 val sitem = alt (wrap field SqField)
adamc@1211 878 (wrap (follow sqexp (follow (const " AS ") uw_ident))
adamc@1211 879 (fn (e, ((), s)) => SqExp (e, s)))
adamc@1207 880
adamc@1207 881 val select = log "select"
adamc@1207 882 (wrap (follow (const "SELECT ") (list sitem))
adamc@1207 883 (fn ((), ls) => ls))
adamc@1201 884
adamc@1201 885 val fitem = wrap (follow uw_ident
adamc@1201 886 (follow (const " AS ")
adamc@1201 887 t_ident))
adamc@1201 888 (fn (t, ((), f)) => (t, f))
adamc@1201 889
adamc@1207 890 val from = log "from"
adamc@1207 891 (wrap (follow (const "FROM ") (list fitem))
adamc@1207 892 (fn ((), ls) => ls))
adamc@1201 893
adamc@1204 894 val wher = wrap (follow (ws (const "WHERE ")) sqexp)
adamc@1204 895 (fn ((), ls) => ls)
adamc@1204 896
adamc@1207 897 val query = log "query"
adamc@1207 898 (wrap (follow (follow select from) (opt wher))
adamc@1207 899 (fn ((fs, ts), wher) => {Select = fs, From = ts, Where = wher}))
adamc@1201 900
adamc@1211 901 fun removeDups ls =
adamc@1211 902 case ls of
adamc@1211 903 [] => []
adamc@1211 904 | x :: ls =>
adamc@1211 905 let
adamc@1211 906 val ls = removeDups ls
adamc@1211 907 in
adamc@1211 908 if List.exists (fn x' => x' = x) ls then
adamc@1211 909 ls
adamc@1211 910 else
adamc@1211 911 x :: ls
adamc@1211 912 end
adamc@1211 913
adamc@1212 914 datatype queryMode =
adamc@1212 915 SomeCol of exp
adamc@1212 916 | AllCols of exp
adamc@1212 917
adamc@1207 918 fun queryProp env rv oe e =
adamc@1202 919 case parse query e of
adamc@1207 920 NONE => (print ("Warning: Information flow checker can't parse SQL query at "
adamc@1207 921 ^ ErrorMsg.spanToString (#2 e) ^ "\n");
adamc@1210 922 (Unknown, []))
adamc@1201 923 | SOME r =>
adamc@1202 924 let
adamc@1211 925 fun usedFields e =
adamc@1211 926 case e of
adamc@1211 927 SqConst _ => []
adamc@1211 928 | Field (v, f) => [(v, f)]
adamc@1211 929 | Binop (_, e1, e2) => removeDups (usedFields e1 @ usedFields e2)
adamc@1211 930 | SqKnown _ => []
adamc@1211 931 | Inj _ => []
adamc@1211 932 | SqFunc (_, e) => usedFields e
adamc@1211 933 | Count => []
adamc@1211 934
adamc@1211 935 val allUsed = removeDups (List.mapPartial (fn SqField x => SOME x | _ => NONE) (#Select r)
adamc@1211 936 @ (case #Where r of
adamc@1211 937 NONE => []
adamc@1211 938 | SOME e => usedFields e))
adamc@1211 939
adamc@1202 940 val p =
adamc@1202 941 foldl (fn ((t, v), p) =>
adamc@1202 942 And (p,
adamc@1202 943 Reln (Sql t,
adamc@1202 944 [Recd (foldl (fn ((v', f), fs) =>
adamc@1202 945 if v' = v then
adamc@1212 946 (f, Proj (Proj (rv, v), f)) :: fs
adamc@1202 947 else
adamc@1211 948 fs) [] allUsed)])))
adamc@1202 949 True (#From r)
adamc@1205 950
adamc@1205 951 fun expIn e =
adamc@1205 952 case e of
adamc@1206 953 SqConst p => inl (Const p)
adamc@1212 954 | Field (v, f) => inl (Proj (Proj (rv, v), f))
adamc@1205 955 | Binop (bo, e1, e2) =>
adamc@1206 956 inr (case (bo, expIn e1, expIn e2) of
adamc@1206 957 (Exps f, inl e1, inl e2) => f (e1, e2)
adamc@1206 958 | (Props f, inr p1, inr p2) => f (p1, p2)
adamc@1206 959 | _ => Unknown)
adamc@1207 960 | SqKnown e =>
adamc@1207 961 inr (case expIn e of
adamc@1207 962 inl e => Reln (Known, [e])
adamc@1207 963 | _ => Unknown)
adamc@1207 964 | Inj e =>
adamc@1207 965 let
adamc@1207 966 fun deinj (e, _) =
adamc@1207 967 case e of
adamc@1207 968 ERel n => List.nth (env, n)
adamc@1207 969 | EField (e, f) => Proj (deinj e, f)
adamc@1207 970 | _ => raise Fail "Iflow: non-variable injected into query"
adamc@1207 971 in
adamc@1207 972 inl (deinj e)
adamc@1207 973 end
adamc@1211 974 | SqFunc (f, e) =>
adamc@1211 975 inl (case expIn e of
adamc@1211 976 inl e => Func (f, [e])
adamc@1211 977 | _ => raise Fail ("Iflow: non-expresion passed to function " ^ f))
adamc@1212 978 | Count => inl (Proj (rv, "$COUNT"))
adamc@1210 979
adamc@1205 980 val p = case #Where r of
adamc@1205 981 NONE => p
adamc@1205 982 | SOME e =>
adamc@1205 983 case expIn e of
adamc@1205 984 inr p' => And (p, p')
adamc@1205 985 | _ => p
adamc@1202 986 in
adamc@1212 987 (And (p, case oe of
adamc@1212 988 SomeCol oe =>
adamc@1212 989 foldl (fn (si, p) =>
adamc@1211 990 let
adamc@1211 991 val p' = case si of
adamc@1212 992 SqField (v, f) => Reln (Eq, [oe, Proj (Proj (rv, v), f)])
adamc@1211 993 | SqExp (e, f) =>
adamc@1211 994 case expIn e of
adamc@1211 995 inr _ => Unknown
adamc@1211 996 | inl e => Reln (Eq, [oe, e])
adamc@1211 997 in
adamc@1211 998 Or (p, p')
adamc@1211 999 end)
adamc@1212 1000 False (#Select r)
adamc@1212 1001 | AllCols oe =>
adamc@1212 1002 foldl (fn (si, p) =>
adamc@1212 1003 let
adamc@1212 1004 val p' = case si of
adamc@1212 1005 SqField (v, f) => Reln (Eq, [Proj (Proj (oe, v), f),
adamc@1212 1006 Proj (Proj (rv, v), f)])
adamc@1212 1007 | SqExp (e, f) =>
adamc@1212 1008 case expIn e of
adamc@1212 1009 inr p => Cond (Proj (oe, f), p)
adamc@1212 1010 | inl e => Reln (Eq, [Proj (oe, f), e])
adamc@1212 1011 in
adamc@1212 1012 And (p, p')
adamc@1212 1013 end)
adamc@1212 1014 True (#Select r)),
adamc@1210 1015
adamc@1210 1016 case #Where r of
adamc@1210 1017 NONE => []
adamc@1212 1018 | SOME e => map (fn (v, f) => Proj (Proj (rv, v), f)) (usedFields e))
adamc@1202 1019 end
adamc@1200 1020
adamc@1211 1021 fun evalPat env e (pt, _) =
adamc@1211 1022 case pt of
adamc@1211 1023 PWild => (env, True)
adamc@1211 1024 | PVar _ => (e :: env, True)
adamc@1211 1025 | PPrim _ => (env, True)
adamc@1211 1026 | PCon (_, pc, NONE) => (env, Reln (DtCon (patCon pc), [e]))
adamc@1211 1027 | PCon (_, pc, SOME pt) =>
adamc@1211 1028 let
adamc@1211 1029 val (env, p) = evalPat env (Func ("un" ^ patCon pc, [e])) pt
adamc@1211 1030 in
adamc@1211 1031 (env, And (p, Reln (DtCon (patCon pc), [e])))
adamc@1211 1032 end
adamc@1211 1033 | PRecord xpts =>
adamc@1211 1034 foldl (fn ((x, pt, _), (env, p)) =>
adamc@1211 1035 let
adamc@1211 1036 val (env, p') = evalPat env (Proj (e, x)) pt
adamc@1211 1037 in
adamc@1211 1038 (env, And (p', p))
adamc@1211 1039 end) (env, True) xpts
adamc@1211 1040 | PNone _ => (env, Reln (DtCon "None", [e]))
adamc@1211 1041 | PSome (_, pt) =>
adamc@1211 1042 let
adamc@1211 1043 val (env, p) = evalPat env (Func ("unSome", [e])) pt
adamc@1211 1044 in
adamc@1211 1045 (env, And (p, Reln (DtCon "Some", [e])))
adamc@1211 1046 end
adamc@1211 1047
adamc@1211 1048 fun peq (p1, p2) =
adamc@1211 1049 case (p1, p2) of
adamc@1211 1050 (True, True) => true
adamc@1211 1051 | (False, False) => true
adamc@1211 1052 | (Unknown, Unknown) => true
adamc@1211 1053 | (And (x1, y1), And (x2, y2)) => peq (x1, x2) andalso peq (y1, y2)
adamc@1211 1054 | (Or (x1, y1), Or (x2, y2)) => peq (x1, x2) andalso peq (y1, y2)
adamc@1211 1055 | (Reln (r1, es1), Reln (r2, es2)) => r1 = r2 andalso ListPair.allEq eeq (es1, es2)
adamc@1212 1056 | (Cond (e1, p1), Cond (e2, p2)) => eeq (e1, e2) andalso peq (p1, p2)
adamc@1211 1057 | _ => false
adamc@1211 1058
adamc@1211 1059 fun removeRedundant p1 =
adamc@1211 1060 let
adamc@1211 1061 fun rr p2 =
adamc@1211 1062 if peq (p1, p2) then
adamc@1211 1063 True
adamc@1211 1064 else
adamc@1211 1065 case p2 of
adamc@1211 1066 And (x, y) => And (rr x, rr y)
adamc@1211 1067 | Or (x, y) => Or (rr x, rr y)
adamc@1211 1068 | _ => p2
adamc@1211 1069 in
adamc@1211 1070 rr
adamc@1211 1071 end
adamc@1211 1072
adamc@1202 1073 fun evalExp env (e as (_, loc), st as (nv, p, sent)) =
adamc@1200 1074 let
adamc@1200 1075 fun default () =
adamc@1200 1076 (Var nv, (nv+1, p, sent))
adamc@1200 1077
adamc@1200 1078 fun addSent (p, e, sent) =
adamc@1200 1079 if isKnown e then
adamc@1200 1080 sent
adamc@1200 1081 else
adamc@1202 1082 (loc, e, p) :: sent
adamc@1200 1083 in
adamc@1200 1084 case #1 e of
adamc@1200 1085 EPrim p => (Const p, st)
adamc@1200 1086 | ERel n => (List.nth (env, n), st)
adamc@1200 1087 | ENamed _ => default ()
adamc@1200 1088 | ECon (_, pc, NONE) => (Func (patCon pc, []), st)
adamc@1200 1089 | ECon (_, pc, SOME e) =>
adamc@1200 1090 let
adamc@1200 1091 val (e, st) = evalExp env (e, st)
adamc@1200 1092 in
adamc@1200 1093 (Func (patCon pc, [e]), st)
adamc@1200 1094 end
adamc@1200 1095 | ENone _ => (Func ("None", []), st)
adamc@1200 1096 | ESome (_, e) =>
adamc@1200 1097 let
adamc@1200 1098 val (e, st) = evalExp env (e, st)
adamc@1200 1099 in
adamc@1200 1100 (Func ("Some", [e]), st)
adamc@1200 1101 end
adamc@1200 1102 | EFfi _ => default ()
adamc@1200 1103 | EFfiApp (m, s, es) =>
adamc@1200 1104 if m = "Basis" andalso SS.member (writers, s) then
adamc@1200 1105 let
adamc@1200 1106 val (es, st) = ListUtil.foldlMap (evalExp env) st es
adamc@1200 1107 in
adamc@1200 1108 (Func ("unit", []), (#1 st, p, foldl (fn (e, sent) => addSent (#2 st, e, sent)) sent es))
adamc@1200 1109 end
adamc@1200 1110 else if Settings.isEffectful (m, s) andalso not (Settings.isBenignEffectful (m, s)) then
adamc@1200 1111 default ()
adamc@1200 1112 else
adamc@1200 1113 let
adamc@1200 1114 val (es, st) = ListUtil.foldlMap (evalExp env) st es
adamc@1200 1115 in
adamc@1200 1116 (Func (m ^ "." ^ s, es), st)
adamc@1200 1117 end
adamc@1200 1118 | EApp _ => default ()
adamc@1200 1119 | EAbs _ => default ()
adamc@1200 1120 | EUnop (s, e1) =>
adamc@1200 1121 let
adamc@1200 1122 val (e1, st) = evalExp env (e1, st)
adamc@1200 1123 in
adamc@1200 1124 (Func (s, [e1]), st)
adamc@1200 1125 end
adamc@1200 1126 | EBinop (s, e1, e2) =>
adamc@1200 1127 let
adamc@1200 1128 val (e1, st) = evalExp env (e1, st)
adamc@1200 1129 val (e2, st) = evalExp env (e2, st)
adamc@1200 1130 in
adamc@1200 1131 (Func (s, [e1, e2]), st)
adamc@1200 1132 end
adamc@1200 1133 | ERecord xets =>
adamc@1200 1134 let
adamc@1200 1135 val (xes, st) = ListUtil.foldlMap (fn ((x, e, _), st) =>
adamc@1200 1136 let
adamc@1200 1137 val (e, st) = evalExp env (e, st)
adamc@1200 1138 in
adamc@1200 1139 ((x, e), st)
adamc@1200 1140 end) st xets
adamc@1200 1141 in
adamc@1200 1142 (Recd xes, st)
adamc@1200 1143 end
adamc@1200 1144 | EField (e, s) =>
adamc@1200 1145 let
adamc@1200 1146 val (e, st) = evalExp env (e, st)
adamc@1200 1147 in
adamc@1200 1148 (Proj (e, s), st)
adamc@1200 1149 end
adamc@1211 1150 | ECase (e, pes, _) =>
adamc@1211 1151 let
adamc@1211 1152 val (e, st) = evalExp env (e, st)
adamc@1211 1153 val r = #1 st
adamc@1211 1154 val st = (r + 1, #2 st, #3 st)
adamc@1211 1155 val orig = #2 st
adamc@1211 1156
adamc@1211 1157 val st = foldl (fn ((pt, pe), st) =>
adamc@1211 1158 let
adamc@1211 1159 val (env, pp) = evalPat env e pt
adamc@1211 1160 val (pe, st') = evalExp env (pe, (#1 st, And (orig, pp), #3 st))
adamc@1211 1161
adamc@1211 1162 val this = And (removeRedundant orig (#2 st'), Reln (Eq, [Var r, pe]))
adamc@1211 1163 in
adamc@1211 1164 (#1 st', Or (#2 st, this), #3 st')
adamc@1211 1165 end) (#1 st, False, #3 st) pes
adamc@1211 1166 in
adamc@1211 1167 (Var r, (#1 st, And (orig, #2 st), #3 st))
adamc@1211 1168 end
adamc@1200 1169 | EStrcat (e1, e2) =>
adamc@1200 1170 let
adamc@1200 1171 val (e1, st) = evalExp env (e1, st)
adamc@1200 1172 val (e2, st) = evalExp env (e2, st)
adamc@1200 1173 in
adamc@1200 1174 (Func ("cat", [e1, e2]), st)
adamc@1200 1175 end
adamc@1200 1176 | EError _ => (Finish, st)
adamc@1200 1177 | EReturnBlob {blob = b, mimeType = m, ...} =>
adamc@1200 1178 let
adamc@1200 1179 val (b, st) = evalExp env (b, st)
adamc@1200 1180 val (m, st) = evalExp env (m, st)
adamc@1200 1181 in
adamc@1200 1182 (Finish, (#1 st, p, addSent (#2 st, b, addSent (#2 st, m, sent))))
adamc@1200 1183 end
adamc@1200 1184 | ERedirect (e, _) =>
adamc@1200 1185 let
adamc@1200 1186 val (e, st) = evalExp env (e, st)
adamc@1200 1187 in
adamc@1200 1188 (Finish, (#1 st, p, addSent (#2 st, e, sent)))
adamc@1200 1189 end
adamc@1200 1190 | EWrite e =>
adamc@1200 1191 let
adamc@1200 1192 val (e, st) = evalExp env (e, st)
adamc@1200 1193 in
adamc@1200 1194 (Func ("unit", []), (#1 st, p, addSent (#2 st, e, sent)))
adamc@1200 1195 end
adamc@1200 1196 | ESeq (e1, e2) =>
adamc@1200 1197 let
adamc@1200 1198 val (_, st) = evalExp env (e1, st)
adamc@1200 1199 in
adamc@1200 1200 evalExp env (e2, st)
adamc@1200 1201 end
adamc@1200 1202 | ELet (_, _, e1, e2) =>
adamc@1200 1203 let
adamc@1200 1204 val (e1, st) = evalExp env (e1, st)
adamc@1200 1205 in
adamc@1200 1206 evalExp (e1 :: env) (e2, st)
adamc@1200 1207 end
adamc@1200 1208 | EClosure (n, es) =>
adamc@1200 1209 let
adamc@1200 1210 val (es, st) = ListUtil.foldlMap (evalExp env) st es
adamc@1200 1211 in
adamc@1200 1212 (Func ("Cl" ^ Int.toString n, es), st)
adamc@1200 1213 end
adamc@1200 1214
adamc@1200 1215 | EQuery {query = q, body = b, initial = i, ...} =>
adamc@1200 1216 let
adamc@1200 1217 val (_, st) = evalExp env (q, st)
adamc@1200 1218 val (i, st) = evalExp env (i, st)
adamc@1200 1219
adamc@1200 1220 val r = #1 st
adamc@1212 1221 val rv = #1 st + 1
adamc@1212 1222 val acc = #1 st + 2
adamc@1212 1223 val st' = (#1 st + 3, #2 st, #3 st)
adamc@1200 1224
adamc@1200 1225 val (b, st') = evalExp (Var acc :: Var r :: env) (b, st')
adamc@1200 1226
adamc@1212 1227 val (qp, used) = queryProp env (Var rv) (AllCols (Var r)) q
adamc@1200 1228
adamc@1212 1229 val p' = And (qp, #2 st')
adamc@1200 1230
adamc@1212 1231 val (nvs, p, res) = if varInP acc (#2 st') then
adamc@1212 1232 (#1 st + 1, #2 st, Var r)
adamc@1212 1233 else
adamc@1212 1234 let
adamc@1212 1235 val out = #1 st'
adamc@1210 1236
adamc@1212 1237 val p = Or (Reln (Eq, [Var out, i]),
adamc@1212 1238 And (Reln (Eq, [Var out, b]),
adamc@1212 1239 And (Reln (Gt, [Proj (Var rv, "$COUNT"),
adamc@1212 1240 Const (Prim.Int 0)]),
adamc@1212 1241 p')))
adamc@1212 1242 in
adamc@1212 1243 (out + 1, p, Var out)
adamc@1212 1244 end
adamc@1212 1245
adamc@1212 1246 val sent = map (fn (loc, e, p) => (loc, e, And (qp, p))) (#3 st')
adamc@1210 1247 val sent = map (fn e => (loc, e, p')) used @ sent
adamc@1200 1248 in
adamc@1212 1249 (res, (nvs, p, sent))
adamc@1200 1250 end
adamc@1200 1251 | EDml _ => default ()
adamc@1200 1252 | ENextval _ => default ()
adamc@1200 1253 | ESetval _ => default ()
adamc@1200 1254
adamc@1200 1255 | EUnurlify _ => default ()
adamc@1200 1256 | EJavaScript _ => default ()
adamc@1200 1257 | ESignalReturn _ => default ()
adamc@1200 1258 | ESignalBind _ => default ()
adamc@1200 1259 | ESignalSource _ => default ()
adamc@1200 1260 | EServerCall _ => default ()
adamc@1200 1261 | ERecv _ => default ()
adamc@1200 1262 | ESleep _ => default ()
adamc@1200 1263 | ESpawn _ => default ()
adamc@1200 1264 end
adamc@1200 1265
adamc@1200 1266 fun check file =
adamc@1200 1267 let
adamc@1207 1268 val exptd = foldl (fn ((d, _), exptd) =>
adamc@1207 1269 case d of
adamc@1207 1270 DExport (_, _, n, _, _, _) => IS.add (exptd, n)
adamc@1207 1271 | _ => exptd) IS.empty file
adamc@1207 1272
adamc@1202 1273 fun decl ((d, _), (vals, pols)) =
adamc@1200 1274 case d of
adamc@1207 1275 DVal (_, n, _, e, _) =>
adamc@1200 1276 let
adamc@1207 1277 val isExptd = IS.member (exptd, n)
adamc@1207 1278
adamc@1207 1279 fun deAbs (e, env, nv, p) =
adamc@1200 1280 case #1 e of
adamc@1207 1281 EAbs (_, _, _, e) => deAbs (e, Var nv :: env, nv + 1,
adamc@1207 1282 if isExptd then
adamc@1207 1283 And (p, Reln (Known, [Var nv]))
adamc@1207 1284 else
adamc@1207 1285 p)
adamc@1207 1286 | _ => (e, env, nv, p)
adamc@1200 1287
adamc@1207 1288 val (e, env, nv, p) = deAbs (e, [], 1, True)
adamc@1200 1289
adamc@1207 1290 val (e, (_, p, sent)) = evalExp env (e, (nv, p, []))
adamc@1200 1291 in
adamc@1207 1292 (sent @ vals, pols)
adamc@1200 1293 end
adamc@1202 1294
adamc@1212 1295 | DPolicy (PolQuery e) => (vals, #1 (queryProp [] (Lvar 0) (SomeCol (Var 0)) e) :: pols)
adamc@1202 1296
adamc@1202 1297 | _ => (vals, pols)
adamc@1202 1298
adamc@1203 1299 val () = reset ()
adamc@1202 1300
adamc@1202 1301 val (vals, pols) = foldl decl ([], []) file
adamc@1200 1302 in
adamc@1207 1303 app (fn (loc, e, p) =>
adamc@1207 1304 let
adamc@1207 1305 val p = And (p, Reln (Eq, [Var 0, e]))
adamc@1207 1306 in
adamc@1208 1307 if List.exists (fn pol => if imply (p, pol) then
adamc@1208 1308 (if !debug then
adamc@1208 1309 Print.prefaces "Match"
adamc@1208 1310 [("Hyp", p_prop p),
adamc@1208 1311 ("Goal", p_prop pol)]
adamc@1208 1312 else
adamc@1208 1313 ();
adamc@1208 1314 true)
adamc@1208 1315 else
adamc@1208 1316 false) pols then
adamc@1207 1317 ()
adamc@1207 1318 else
adamc@1207 1319 (ErrorMsg.errorAt loc "The information flow policy may be violated here.";
adamc@1207 1320 Print.preface ("The state satisifes this predicate:", p_prop p))
adamc@1207 1321 end) vals
adamc@1200 1322 end
adamc@1200 1323
adamc@1200 1324 end