changeset 252:7e9bd70ad3ce

Monoized and optimized initial query test
author Adam Chlipala <adamc@hcoop.net>
date Sun, 31 Aug 2008 13:58:47 -0400
parents 326fb4686f60
children 7f6620853c36
files lib/basis.urs src/cjrize.sml src/mono.sml src/mono_print.sml src/mono_reduce.sml src/mono_util.sml src/monoize.sig src/monoize.sml
diffstat 8 files changed, 449 insertions(+), 64 deletions(-) [+]
line wrap: on
line diff
--- a/lib/basis.urs	Sun Aug 31 10:36:54 2008 -0400
+++ b/lib/basis.urs	Sun Aug 31 13:58:47 2008 -0400
@@ -156,8 +156,8 @@
         -> transaction t2
 
 val query : tables ::: {{Type}} -> exps ::: {Type} -> tables ~ exps
+        -> state ::: Type
         -> sql_query tables exps
-        -> state ::: Type
         -> ($(exps ++ fold (fn nm (fields :: {Type}) acc => [nm] ~ acc => [nm = $fields] ++ acc) [] tables)
                 -> state
                 -> transaction state)
--- a/src/cjrize.sml	Sun Aug 31 10:36:54 2008 -0400
+++ b/src/cjrize.sml	Sun Aug 31 13:58:47 2008 -0400
@@ -280,6 +280,8 @@
       | L.EClosure _ => (ErrorMsg.errorAt loc "Nested closure remains in code generation";
                          (dummye, sm))
 
+      | L.EQuery _ => raise Fail "Cjrize EQuery"
+
 fun cifyDecl ((d, loc), sm) =
     case d of
         L.DDatatype (x, n, xncs) =>
--- a/src/mono.sml	Sun Aug 31 10:36:54 2008 -0400
+++ b/src/mono.sml	Sun Aug 31 13:58:47 2008 -0400
@@ -75,6 +75,13 @@
 
        | EClosure of int * exp list
 
+       | EQuery of { exps : (string * typ) list,
+                     tables : (string * (string * typ) list) list,
+                     state : typ,
+                     query : exp,
+                     body : exp,
+                     initial : exp }
+
 
 withtype exp = exp' located
 
--- a/src/mono_print.sml	Sun Aug 31 10:36:54 2008 -0400
+++ b/src/mono_print.sml	Sun Aug 31 13:58:47 2008 -0400
@@ -38,6 +38,8 @@
 
 val debug = ref false
 
+val dummyt = (TRecord [], ErrorMsg.dummySpan)
+
 fun p_typ' par env (t, _) =
     case t of
         TFun (t1, t2) => parenIf par (box [p_typ' true env t1,
@@ -133,17 +135,17 @@
       | EApp (e1, e2) => parenIf par (box [p_exp env e1,
                                            space,
                                            p_exp' true env e2])
-      | EAbs (x, t, _, e) => parenIf par (box [string "fn",
-                                               space,
-                                               string x,
-                                               space,
-                                               string ":",
-                                               space,
-                                               p_typ env t,
-                                               space,
-                                               string "=>",
-                                               space,
-                                               p_exp (E.pushERel env x t NONE) e])
+      | EAbs (x, t, _, e) => parenIf true (box [string "fn",
+                                                space,
+                                                string x,
+                                                space,
+                                                string ":",
+                                                space,
+                                                p_typ env t,
+                                                space,
+                                                string "=>",
+                                                space,
+                                                p_exp (E.pushERel env x t NONE) e])
 
       | ERecord xes => box [string "{",
                             p_list (fn (x, e, _) =>
@@ -158,18 +160,18 @@
              string ".",
              string x]
 
-      | ECase (e, pes, _) => parenIf par (box [string "case",
-                                               space,
-                                               p_exp env e,
-                                               space,
-                                               string "of",
-                                               space,
-                                               p_list_sep (box [space, string "|", space])
-                                                          (fn (p, e) => box [p_pat env p,
-                                                                             space,
-                                                                             string "=>",
-                                                                             space,
-                                                                             p_exp (E.patBinds env p) e]) pes])
+      | ECase (e, pes, _) => parenIf true (box [string "case",
+                                                space,
+                                                p_exp env e,
+                                                space,
+                                                string "of",
+                                                space,
+                                                p_list_sep (box [space, string "|", space])
+                                                           (fn (p, e) => box [p_pat env p,
+                                                                              space,
+                                                                              string "=>",
+                                                                              space,
+                                                                              p_exp (E.patBinds env p) e]) pes])
 
       | EStrcat (e1, e2) => box [p_exp' true env e1,
                                  space,
@@ -185,7 +187,7 @@
                               string ";",
                               space,
                               p_exp env e2]
-      | ELet (x, t, e1, e2) => box [string "let",
+      | ELet (x, t, e1, e2) => box [string "(let",
                                     space,
                                     string x,
                                     space,
@@ -195,11 +197,15 @@
                                     space,
                                     string "=",
                                     space,
+                                    string "(",
                                     p_exp env e1,
+                                    string ")",
                                     space,
                                     string "in",
                                     space,
-                                    p_exp (E.pushERel env x t NONE) e2]
+                                    string "(",
+                                    p_exp (E.pushERel env x t NONE) e2,
+                                    string "))"]
 
       | EClosure (n, es) => box [string "CLOSURE(",
                                  p_enamed env n,
@@ -207,6 +213,31 @@
                                                                       p_exp env e]) es,
                                  string ")"]
 
+      | EQuery {exps, tables, state, query, body, initial} =>
+        box [string "query[",
+             p_list (fn (x, t) => box [string x, space, string ":", space, p_typ env t]) exps,
+             string "] [",
+             p_list (fn (x, xts) => box [string x,
+                                         space,
+                                         string ":",
+                                         space,
+                                         string "{",
+                                         p_list (fn (x, t) => box [string x, space, string ":", space, p_typ env t]) xts,
+                                         string "}"]) tables,
+             string "] [",
+             p_typ env state,
+             string "]",
+             space,
+             p_exp env query,
+             space,
+             string "initial",
+             space,
+             p_exp env initial,
+             space,
+             string "in",
+             space,
+             p_exp (E.pushERel (E.pushERel env "r" dummyt NONE) "acc" dummyt NONE) body]
+
 and p_exp env = p_exp' false env
 
 fun p_vali env (x, n, t, e, s) =
--- a/src/mono_reduce.sml	Sun Aug 31 10:36:54 2008 -0400
+++ b/src/mono_reduce.sml	Sun Aug 31 13:58:47 2008 -0400
@@ -34,20 +34,38 @@
 structure E = MonoEnv
 structure U = MonoUtil
 
-val liftExpInExp =
-    U.Exp.mapB {typ = fn t => t,
-                exp = fn bound => fn e =>
-                                     case e of
-                                         ERel xn =>
-                                         if xn < bound then
-                                             e
-                                         else
-                                             ERel (xn + 1)
-                                       | _ => e,
-                bind = fn (bound, U.Exp.RelE _) => bound + 1
-                        | (bound, _) => bound}
 
-val subExpInExp =
+fun impure (e, _) =
+    case e of
+        EWrite _ => true
+      | EQuery _ => true
+      | EAbs _ => false
+
+      | EPrim _ => false
+      | ERel _ => false
+      | ENamed _ => false
+      | ECon (_, _, eo) => (case eo of NONE => false | SOME e => impure e)
+      | EFfi _ => false
+      | EFfiApp _ => false
+      | EApp ((EFfi _, _), _) => false
+      | EApp _ => true
+
+      | ERecord xes => List.exists (fn (_, e, _) => impure e) xes
+      | EField (e, _) => impure e
+
+      | ECase (e, pes, _) => impure e orelse List.exists (fn (_, e) => impure e) pes
+
+      | EStrcat (e1, e2) => impure e1 orelse impure e2
+
+      | ESeq (e1, e2) => impure e1 orelse impure e2
+      | ELet (_, _, e1, e2) => impure e1 orelse impure e2
+
+      | EClosure (_, es) => List.exists impure es
+
+
+val liftExpInExp = Monoize.liftExpInExp
+
+val subExpInExp' =
     U.Exp.mapB {typ = fn t => t,
                 exp = fn (xn, rep) => fn e =>
                                   case e of
@@ -60,11 +78,15 @@
                 bind = fn ((xn, rep), U.Exp.RelE _) => (xn+1, liftExpInExp 0 rep)
                         | (ctx, _) => ctx}
 
-fun bind (env, b) =
-    case b of
-        U.Decl.Datatype (x, n, xncs) => E.pushDatatype env x n xncs
-      | U.Decl.RelE (x, t) => E.pushERel env x t NONE
-      | U.Decl.NamedE (x, n, t, eo, s) => E.pushENamed env x n t eo s
+fun subExpInExp (n, e1) e2 =
+    let
+        val r = subExpInExp' (n, e1) e2
+    in
+        (*Print.prefaces "subExpInExp" [("e1", MonoPrint.p_exp MonoEnv.empty e1),
+                                      ("e2", MonoPrint.p_exp MonoEnv.empty e2),
+                                      ("r", MonoPrint.p_exp MonoEnv.empty r)];*)
+        r
+    end
 
 fun typ c = c
 
@@ -132,8 +154,13 @@
              (_, _, SOME e', _) => #1 e'
            | _ => e)
 
-      | EApp ((EAbs (_, _, _, e1), loc), e2) =>
-        #1 (reduceExp env (subExpInExp (0, e2) e1))
+      | EApp ((EAbs (x, t, _, e1), loc), e2) =>
+        ((*Print.prefaces "Considering" [("e1", MonoPrint.p_exp env e1),
+                                       ("e2", MonoPrint.p_exp env e2)];*)
+        if impure e2 then
+            #1 (reduceExp env (ELet (x, t, e2, e1), loc))
+        else
+            #1 (reduceExp env (subExpInExp (0, e2) e1)))
 
       | ECase (disc, pes, _) =>
         (case ListUtil.search (fn (p, body) =>
@@ -143,8 +170,38 @@
              NONE => e
            | SOME e' => e')
 
+      | EField ((ERecord xes, _), x) =>
+        (case List.find (fn (x', _, _) => x' = x) xes of
+             SOME (_, e, _) => #1 e
+           | NONE => e)
+
+      | ELet (x1, t1, (ELet (x2, t2, e1, b1), loc), b2) =>
+        let
+            val e' = (ELet (x2, t2, e1,
+                            (ELet (x1, t1, b1,
+                                   liftExpInExp 1 b2), loc)), loc)
+        in
+            Print.prefaces "ELet commute" [("e", MonoPrint.p_exp env (e, loc)),
+                                           ("e'", MonoPrint.p_exp env e')];
+            #1 (reduceExp env e')
+        end
+      | EApp ((ELet (x, t, e, b), loc), e') =>
+        #1 (reduceExp env (ELet (x, t, e,
+                                 (EApp (b, liftExpInExp 0 e'), loc)), loc))
+      | ELet (x, t, e', b) =>
+        if impure e' then
+            e
+        else
+            #1 (reduceExp env (subExpInExp (0, e') b))
+
       | _ => e
 
+and bind (env, b) =
+    case b of
+        U.Decl.Datatype (x, n, xncs) => E.pushDatatype env x n xncs
+      | U.Decl.RelE (x, t) => E.pushERel env x t NONE
+      | U.Decl.NamedE (x, n, t, eo, s) => E.pushENamed env x n t (Option.map (reduceExp env) eo) s
+
 and reduceExp env = U.Exp.mapB {typ = typ, exp = exp, bind = bind} env
 
 fun decl env d = d
--- a/src/mono_util.sml	Sun Aug 31 10:36:54 2008 -0400
+++ b/src/mono_util.sml	Sun Aug 31 13:58:47 2008 -0400
@@ -218,7 +218,7 @@
                          fn t' =>
                             S.bind2 (mfe ctx e1,
                                   fn e1' =>
-                                     S.map2 (mfe (bind (ctx, RelE (x, t))) e2,
+                                     S.map2 (mfe (bind (ctx, RelE (x, t'))) e2,
                                           fn e2' =>
                                              (ELet (x, t', e1', e2'), loc))))
 
@@ -226,6 +226,34 @@
                 S.map2 (ListUtil.mapfold (mfe ctx) es,
                      fn es' =>
                         (EClosure (n, es'), loc))
+
+              | EQuery {exps, tables, state, query, body, initial} =>
+                S.bind2 (ListUtil.mapfold (fn (x, t) =>
+                                              S.map2 (mft t,
+                                                      fn t' => (x, t'))) exps,
+                         fn exps' =>
+                            S.bind2 (ListUtil.mapfold (fn (x, xts) =>
+                                                          S.map2 (ListUtil.mapfold
+                                                                       (fn (x, t) =>
+                                                                           S.map2 (mft t,
+                                                                                fn t' => (x, t'))) xts,
+                                                                   fn xts' => (x, xts'))) tables,
+                                  fn tables' =>
+                                     S.bind2 (mft state,
+                                              fn state' =>
+                                                 S.bind2 (mfe ctx query,
+                                                          fn query' =>
+                                                             S.bind2 (mfe ctx body,
+                                                                     fn body' =>
+                                                                        S.map2 (mfe ctx initial,
+                                                                                fn initial' =>
+                                                                                   (EQuery {exps = exps',
+                                                                                            tables = tables',
+                                                                                            state = state',
+                                                                                            query = query',
+                                                                                            body = body',
+                                                                                            initial = initial'},
+                                                                                    loc)))))))
     in
         mfe
     end
--- a/src/monoize.sig	Sun Aug 31 10:36:54 2008 -0400
+++ b/src/monoize.sig	Sun Aug 31 13:58:47 2008 -0400
@@ -29,4 +29,6 @@
 
     val monoize : CoreEnv.env -> Core.file -> Mono.file
 
+    val liftExpInExp : int -> Mono.exp -> Mono.exp
+
 end
--- a/src/monoize.sml	Sun Aug 31 10:36:54 2008 -0400
+++ b/src/monoize.sml	Sun Aug 31 13:58:47 2008 -0400
@@ -37,6 +37,21 @@
 
 val dummyTyp = (L'.TDatatype (0, ref (L'.Enum, [])), E.dummySpan)
 
+structure U = MonoUtil
+
+val liftExpInExp =
+    U.Exp.mapB {typ = fn t => t,
+                exp = fn bound => fn e =>
+                                     case e of
+                                         L'.ERel xn =>
+                                         if xn < bound then
+                                             e
+                                         else
+                                             L'.ERel (xn + 1)
+                                       | _ => e,
+                bind = fn (bound, U.Exp.RelE _) => bound + 1
+                        | (bound, _) => bound}
+
 fun monoName env (all as (c, loc)) =
     let
         fun poly () =
@@ -71,7 +86,43 @@
                     (L'.TFfi ("Basis", "string"), loc)
 
                   | L.CApp ((L.CFfi ("Basis", "transaction"), _), t) =>
-                    (L'.TFun (mt env dtmap t, (L'.TRecord [], loc)), loc)
+                    (L'.TFun ((L'.TRecord [], loc), mt env dtmap t), loc)
+                  | L.CApp ((L.CFfi ("Basis", "sql_table"), _), _) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CApp ((L.CApp ((L.CFfi ("Basis", "sql_query"), _), _), _), _) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CApp ((L.CApp ((L.CApp ((L.CFfi ("Basis", "sql_query1"), _), _), _), _), _), _) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CApp ((L.CApp ((L.CApp ((L.CApp ((L.CFfi ("Basis", "sql_exp"), _), _), _), _), _), _), _), _) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+
+                  | L.CApp ((L.CApp ((L.CFfi ("Basis", "sql_subset"), _), _), _), _) =>
+                    (L'.TRecord [], loc)
+                  | L.CFfi ("Basis", "sql_relop") =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CFfi ("Basis", "sql_direction") =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CApp ((L.CApp ((L.CFfi ("Basis", "sql_order_by"), _), _), _), _) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CFfi ("Basis", "sql_limit") =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CFfi ("Basis", "sql_offset") =>
+                    (L'.TFfi ("Basis", "string"), loc)
+
+                  | L.CApp ((L.CFfi ("Basis", "sql_injectable"), _), t) =>
+                    (L'.TFun (mt env dtmap t, (L'.TFfi ("Basis", "string"), loc)), loc)
+                  | L.CApp ((L.CApp ((L.CFfi ("Basis", "sql_unary"), _), _), _), _) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CApp ((L.CApp ((L.CApp ((L.CFfi ("Basis", "sql_binary"), _), _), _), _), _), _) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CFfi ("Basis", "sql_comparison") =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CApp ((L.CFfi ("Basis", "sql_aggregate"), _), t) =>
+                    (L'.TFfi ("Basis", "string"), loc)
+                  | L.CApp ((L.CFfi ("Basis", "sql_summable"), _), _) =>
+                    (L'.TRecord [], loc)
+                  | L.CApp ((L.CFfi ("Basis", "sql_maxable"), _), _) =>
+                    (L'.TRecord [], loc)
 
                   | L.CRel _ => poly ()
                   | L.CNamed n =>
@@ -347,6 +398,41 @@
           | L.PRecord xps => (L'.PRecord (map (fn (x, p, t) => (x, monoPat env p, monoType env t)) xps), loc)
     end
 
+fun strcat loc es =
+    case es of
+        [] => (L'.EPrim (Prim.String ""), loc)
+      | [e] => e
+      | _ =>
+        let
+            val e2 = List.last es
+            val es = List.take (es, length es - 1)
+            val e1 = List.last es
+            val es = List.take (es, length es - 1)
+        in
+            foldr (fn (e, e') => (L'.EStrcat (e, e'), loc))
+            (L'.EStrcat (e1, e2), loc) es
+        end
+
+fun strcatComma loc es =
+    case es of
+        [] => (L'.EPrim (Prim.String ""), loc)
+      | [e] => e
+      | _ =>
+        let
+            val e1 = List.last es
+            val es = List.take (es, length es - 1)
+        in
+            foldr (fn (e, e') =>
+                      case e of
+                          (L'.EPrim (Prim.String ""), _) => e'
+                        | _ =>
+                          (L'.EStrcat (e,
+                                       (L'.EStrcat ((L'.EPrim (Prim.String ", "), loc), e'), loc)), loc))
+            e1 es
+        end
+
+fun strcatR loc e xs = strcatComma loc (map (fn (x, _) => (L'.EField (e, x), loc)) xs)
+
 fun monoExp (env, st, fm) (all as (e, loc)) =
     let
         fun poly () =
@@ -373,32 +459,195 @@
                 ((L'.ECon (dk, monoPatCon env pc, eo), loc), fm)
             end
           | L.ECon _ => poly ()
-          | L.EFfi mx => ((L'.EFfi mx, loc), fm)
-          | L.EFfiApp (m, x, es) =>
-            let
-                val (es, fm) = ListUtil.foldlMap (fn (e, fm) => monoExp (env, st, fm) e) fm es
-            in
-                ((L'.EFfiApp (m, x, es), loc), fm)
-            end
 
           | L.ECApp ((L.EFfi ("Basis", "return"), _), t) =>
-            ((L'.EAbs ("x", monoType env t, (L'.TRecord [], loc), (L'.ERel 0, loc)), loc), fm)
+            let
+                val t = monoType env t
+            in
+                ((L'.EAbs ("x", t,
+                           (L'.TFun ((L'.TRecord [], loc), t), loc),
+                           (L'.EAbs ("_", (L'.TRecord [], loc), t,
+                                     (L'.ERel 1, loc)), loc)), loc), fm)
+            end
           | L.ECApp ((L.ECApp ((L.EFfi ("Basis", "bind"), _), t1), _), t2) =>
             let
                 val t1 = monoType env t1
                 val t2 = monoType env t2
                 val un = (L'.TRecord [], loc)
-                val mt1 = (L'.TFun (t1, un), loc)
-                val mt2 = (L'.TFun (t2, un), loc)
+                val mt1 = (L'.TFun (un, t1), loc)
+                val mt2 = (L'.TFun (un, t2), loc)
             in
-                ((L'.EAbs ("m1", mt1, (L'.TFun (mt1, (L'.TFun (mt2, un), loc)), loc),
-                           (L'.EAbs ("m2", mt2, un,
-                                     (L'.ELet ("r", t1, (L'.ERel 1, loc),
-                                               (L'.EApp ((L'.ERel 1, loc), (L'.ERel 0, loc)),
-                                                loc)), loc)), loc)), loc),
+                ((L'.EAbs ("m1", mt1, (L'.TFun (mt1, (L'.TFun (mt2, (L'.TFun (un, un), loc)), loc)), loc),
+                           (L'.EAbs ("m2", mt2, (L'.TFun (un, un), loc),
+                                     (L'.EAbs ("_", un, un,
+                                               (L'.ELet ("r", t1, (L'.EApp ((L'.ERel 2, loc),
+                                                                            (L'.ERecord [], loc)), loc),
+                                                         (L'.EApp (
+                                                          (L'.EApp ((L'.ERel 2, loc), (L'.ERel 0, loc)), loc),
+                                                          (L'.ERecord [], loc)),
+                                                          loc)), loc)), loc)), loc)), loc),
                  fm)
             end
 
+          | L.ECApp (
+            (L.ECApp (
+             (L.ECApp ((L.EFfi ("Basis", "query"), _), (L.CRecord (_, tables), _)), _),
+             exps), _),
+            state) =>
+            (case monoType env (L.TRecord exps, loc) of
+                 (L'.TRecord exps, _) =>
+                 let
+                     val tables = map (fn ((L.CName x, _), xts) =>
+                                        (case monoType env (L.TRecord xts, loc) of
+                                             (L'.TRecord xts, _) => SOME (x, xts)
+                                           | _ => NONE)
+                                      | _ => NONE) tables
+                 in
+                     if List.exists (fn x => x = NONE) tables then
+                         poly ()
+                     else
+                         let
+                             val tables = List.mapPartial (fn x => x) tables
+                             val state = monoType env state
+                             val s = (L'.TFfi ("Basis", "string"), loc)
+                             val un = (L'.TRecord [], loc)
+
+                             val rt = exps @ map (fn (x, xts) => (x, (L'.TRecord xts, loc))) tables
+                             val ft = (L'.TFun ((L'.TRecord rt, loc),
+                                                (L'.TFun (state,
+                                                          (L'.TFun (un, state), loc)),
+                                                 loc)), loc)
+
+                             val body' = (L'.EAbs ("r", (L'.TRecord rt, loc),
+                                                   (L'.TFun (state, state), loc),
+                                                   (L'.EAbs ("acc", state, state,
+                                                             (L'.EApp (
+                                                              (L'.EApp (
+                                                               (L'.EApp ((L'.ERel 4, loc),
+                                                                         (L'.ERel 1, loc)), loc),
+                                                               (L'.ERel 0, loc)), loc),
+                                                              (L'.ERecord [], loc)), loc)), loc)), loc)
+
+                             val body = (L'.EQuery {exps = exps,
+                                                    tables = tables,
+                                                    state = state,
+                                                    query = (L'.ERel 3, loc),
+                                                    body = body',
+                                                    initial = (L'.ERel 1, loc)},
+                                         loc)
+                         in
+                             ((L'.EAbs ("q", s, (L'.TFun (ft, (L'.TFun (state, (L'.TFun (un, state), loc)), loc)), loc),
+                                        (L'.EAbs ("f", ft, (L'.TFun (state, (L'.TFun (un, state), loc)), loc),
+                                                  (L'.EAbs ("i", state, (L'.TFun (un, state), loc),
+                                                            (L'.EAbs ("_", un, state,
+                                                                      body), loc)), loc)), loc)), loc), fm)
+                         end
+                 end
+               | _ => poly ())
+
+          | L.ECApp ((L.ECApp ((L.ECApp ((L.EFfi ("Basis", "sql_query"), _), _), _), _), _), _) =>
+            let
+                fun sc s = (L'.EPrim (Prim.String s), loc)
+                val s = (L'.TFfi ("Basis", "string"), loc)
+                fun gf s = (L'.EField ((L'.ERel 0, loc), s), loc)
+            in
+                ((L'.EAbs ("r",
+                           (L'.TRecord [("Rows", s), ("OrderBy", s), ("Limit", s), ("Offset", s)], loc),
+                           s,
+                           strcat loc [gf "Rows",
+                                       gf "OrderBy",
+                                       gf "Limit",
+                                       gf "Offset"]), loc), fm)
+            end
+
+          | L.ECApp (
+            (L.ECApp (
+             (L.ECApp (
+              (L.ECApp (
+               (L.EFfi ("Basis", "sql_query1"), _),
+               (L.CRecord (_, tables), _)), _),
+              (L.CRecord (_, grouped), _)), _),
+             (L.CRecord (_, stables), _)), _),
+            sexps) =>
+            let
+                fun sc s = (L'.EPrim (Prim.String s), loc)
+                val s = (L'.TFfi ("Basis", "string"), loc)
+                val un = (L'.TRecord [], loc)
+                fun gf s = (L'.EField ((L'.ERel 0, loc), s), loc)
+
+                fun doTables tables =
+                    let
+                        val tables = map (fn ((L.CName x, _), xts) =>
+                                             (case monoType env (L.TRecord xts, loc) of
+                                                  (L'.TRecord xts, _) => SOME (x, xts)
+                                                | _ => NONE)
+                                           | _ => NONE) tables
+                    in
+                        if List.exists (fn x => x = NONE) tables then
+                            NONE
+                        else
+                            SOME (List.mapPartial (fn x => x) tables)
+                    end
+            in
+                case (doTables tables, doTables grouped, doTables stables, monoType env (L.TRecord sexps, loc)) of
+                    (SOME tables, SOME grouped, SOME stables, (L'.TRecord sexps, _)) =>
+                    ((L'.EAbs ("r",
+                               (L'.TRecord [("From", (L'.TRecord (map (fn (x, _) => (x, s)) tables), loc)),
+                                            ("Where", s),
+                                            ("GroupBy", un),
+                                            ("Having", s),
+                                            ("SelectFields", un),
+                                            ("SelectExps", (L'.TRecord (map (fn (x, _) => (x, s)) sexps), loc))],
+                                loc),
+                               s,
+                               strcat loc [sc "SELECT ",
+                                           strcatR loc (gf "SelectExps") sexps,
+                                           case sexps of
+                                               [] => sc ""
+                                             | _ => sc ", ",
+                                           strcatComma loc (map (fn (x, xts) =>
+                                                                    strcatComma loc
+                                                                        (map (fn (x', _) =>
+                                                                                 sc (x ^ "." ^ x'))
+                                                                         xts)) stables),
+                                           sc " FROM ",
+                                           strcatComma loc (map (fn (x, _) => strcat loc [(L'.EField (gf "From", x), loc),
+                                                                                          sc (" AS " ^ x)]) tables)
+                              ]), loc),
+                     fm)
+                  | _ => poly ()
+            end
+
+          | L.ECApp (
+            (L.ECApp (
+             (L.ECApp (
+              (L.ECApp (
+               (L.EFfi ("Basis", "sql_inject"), _),
+               _), _),
+              _), _),
+             _), _),
+            t) =>
+            let
+                val t = monoType env t
+                val s = (L'.TFfi ("Basis", "string"), loc)
+            in
+                ((L'.EAbs ("f", (L'.TFun (t, s), loc), (L'.TFun (t, s), loc),
+                           (L'.ERel 0, loc)), loc), fm)
+            end
+
+          | L.ECApp ((L.EFfi ("Basis", "sql_subset"), _), _) =>
+            ((L'.ERecord [], loc), fm)
+          | L.ECApp ((L.EFfi ("Basis", "sql_subset_all"), _), _) =>
+            ((L'.ERecord [], loc), fm)
+
+          | L.ECApp ((L.ECApp ((L.EFfi ("Basis", "sql_order_by_Nil"), _), _), _), _) =>
+            ((L'.EPrim (Prim.String ""), loc), fm)
+
+          | L.EFfi ("Basis", "sql_no_limit") =>
+            ((L'.EPrim (Prim.String ""), loc), fm)
+          | L.EFfi ("Basis", "sql_no_offset") =>
+            ((L'.EPrim (Prim.String ""), loc), fm)
+                    
           | L.EApp (
             (L.ECApp (
              (L.ECApp ((L.EFfi ("Basis", "cdata"), _), _), _),
@@ -721,6 +970,14 @@
           | L.ECApp _ => poly ()
           | L.ECAbs _ => poly ()
 
+          | L.EFfi mx => ((L'.EFfi mx, loc), fm)
+          | L.EFfiApp (m, x, es) =>
+            let
+                val (es, fm) = ListUtil.foldlMap (fn (e, fm) => monoExp (env, st, fm) e) fm es
+            in
+                ((L'.EFfiApp (m, x, es), loc), fm)
+            end
+
           | L.ERecord xes =>
             let
                 val (xes, fm) = ListUtil.foldlMap
@@ -762,7 +1019,8 @@
             let
                 val (e, fm) = monoExp (env, st, fm) e
             in
-                ((L'.EWrite e, loc), fm)
+                ((L'.EAbs ("_", (L'.TRecord [], loc), (L'.TRecord [], loc),
+                           (L'.EWrite (liftExpInExp 0 e), loc)), loc), fm)
             end
 
           | L.EClosure (n, es) =>