changeset 341:389399d65331

Crud update form
author Adam Chlipala <adamc@hcoop.net>
date Sun, 14 Sep 2008 19:03:55 -0400
parents 5ccb1c6412e4
children f55034419a07
files lib/basis.urs lib/top.ur lib/top.urs src/elab_env.sml src/elaborate.sml src/mono_reduce.sml src/urweb.grm tests/crud.ur tests/crud.urs tests/crud1.ur
diffstat 10 files changed, 122 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/lib/basis.urs	Sun Sep 14 15:20:53 2008 -0400
+++ b/lib/basis.urs	Sun Sep 14 19:03:55 2008 -0400
@@ -296,7 +296,7 @@
         ctx ::: {Unit} -> [LForm] ~ ctx
         -> nm :: Name -> unit
         -> tag attrs ([LForm] ++ ctx) inner [] [nm = ty]
-val textbox : lformTag string [] []
+val textbox : lformTag string [] [Value = string]
 val password : lformTag string [] []
 val ltextarea : lformTag string [] []
 
--- a/lib/top.ur	Sun Sep 14 15:20:53 2008 -0400
+++ b/lib/top.ur	Sun Sep 14 19:03:55 2008 -0400
@@ -103,3 +103,9 @@
         query q
                 (fn fs acc => return <xml>{acc}{f fs}</xml>)
                 <xml></xml>
+
+fun oneOrNoRows (tables ::: {{Type}}) (exps ::: {Type}) (q : sql_query tables exps) =
+        [tables ~ exps] =>
+        query q
+                (fn fs _ => return (Some fs))
+                None
--- a/lib/top.urs	Sun Sep 14 15:20:53 2008 -0400
+++ b/lib/top.urs	Sun Sep 14 19:03:55 2008 -0400
@@ -66,3 +66,8 @@
         -> ($(exps ++ fold (fn nm (fields :: {Type}) acc => [nm] ~ acc => [nm = $fields] ++ acc) [] tables)
                 -> xml ctx [] [])
         -> transaction (xml ctx [] [])
+
+val oneOrNoRows : tables ::: {{Type}} -> exps ::: {Type} -> sql_query tables exps
+        -> tables ~ exps
+        -> transaction
+                (option $(exps ++ fold (fn nm (fields :: {Type}) acc => [nm] ~ acc => [nm = $fields] ++ acc) [] tables))
--- a/src/elab_env.sml	Sun Sep 14 15:20:53 2008 -0400
+++ b/src/elab_env.sml	Sun Sep 14 19:03:55 2008 -0400
@@ -795,7 +795,10 @@
       | SgiCon (x, n, k, c) => pushCNamedAs env x n k (SOME c)
       | SgiDatatype (x, n, xs, xncs) =>
         let
-            val env = pushCNamedAs env x n (KType, loc) NONE
+            val k = (KType, loc)
+            val k' = foldr (fn (_, k') => (KArrow (k, k'), loc)) k xs
+
+            val env = pushCNamedAs env x n k' NONE
         in
             foldl (fn ((x', n', to), env) =>
                       let
@@ -813,7 +816,10 @@
         end
       | SgiDatatypeImp (x, n, m1, ms, x', xs, xncs) =>
         let
-            val env = pushCNamedAs env x n (KType, loc) (SOME (CModProj (m1, ms, x'), loc))
+            val k = (KType, loc)
+            val k' = foldr (fn (_, k') => (KArrow (k, k'), loc)) k xs
+
+            val env = pushCNamedAs env x n k' (SOME (CModProj (m1, ms, x'), loc))
         in
             foldl (fn ((x', n', to), env) =>
                       let
@@ -880,10 +886,24 @@
         SgnConst sgis =>
         (case sgnSeek (fn SgiConAbs (x, _, k) => if x = field then SOME (k, NONE) else NONE
                         | SgiCon (x, _, k, c) => if x = field then SOME (k, SOME c) else NONE
-                        | SgiDatatype (x, _, _, _) => if x = field then SOME ((KType, #2 sgn), NONE) else NONE
-                        | SgiDatatypeImp (x, _, m1, ms, x', _, _) =>
+                        | SgiDatatype (x, _, xs, _) =>
                           if x = field then
-                              SOME ((KType, #2 sgn), SOME (CModProj (m1, ms, x'), #2 sgn))
+                              let
+                                  val k = (KType, #2 sgn)
+                                  val k' = foldl (fn (_, k') => (KArrow (k, k'), #2 sgn)) k xs
+                              in
+                                  SOME (k', NONE)
+                              end
+                          else
+                              NONE
+                        | SgiDatatypeImp (x, _, m1, ms, x', xs, _) =>
+                          if x = field then
+                              let
+                                  val k = (KType, #2 sgn)
+                                  val k' = foldl (fn (_, k') => (KArrow (k, k'), #2 sgn)) k xs
+                              in
+                                  SOME (k', SOME (CModProj (m1, ms, x'), #2 sgn))
+                              end
                           else
                               NONE
                         | SgiClassAbs (x, _) => if x = field then
@@ -1032,8 +1052,7 @@
                                                 (KArrow (k, kb), loc)))
                                            ((CNamed n, loc), k) xs
 
-            val t' = foldr (fn (x, t) => (TCFun (Implicit, x, k, t), loc)) t xs
-            val env = pushCNamedAs env x n kb (SOME t')
+            val env = pushCNamedAs env x n kb (SOME t)
             val env = pushDatatype env n xs xncs
         in
             foldl (fn ((x', n', to), env) =>
--- a/src/elaborate.sml	Sun Sep 14 15:20:53 2008 -0400
+++ b/src/elaborate.sml	Sun Sep 14 19:03:55 2008 -0400
@@ -1321,7 +1321,9 @@
                               | SOME (_, cons) => dtype cons
                         end
                       | L'.CError => (true, gs)
-                      | _ => raise Fail "isTotal: Not a datatype"
+                      | c =>
+                        (prefaces "Not a datatype" [("c", p_con env (c, ErrorMsg.dummySpan))];
+                         raise Fail "isTotal: Not a datatype")
                 end
               | Record _ => (List.all (fn c2 => coverageImp (c, c2)) (enumerateCases t), [])
     in
--- a/src/mono_reduce.sml	Sun Sep 14 15:20:53 2008 -0400
+++ b/src/mono_reduce.sml	Sun Sep 14 19:03:55 2008 -0400
@@ -111,6 +111,21 @@
                 bind = fn (lower, U.Exp.RelE _) => lower+1
                         | (lower, _) => lower}
 
+val swapExpVarsPat =
+    U.Exp.mapB {typ = fn t => t,
+                exp = fn (lower, len) => fn e =>
+                                     case e of
+                                         ERel xn =>
+                                         if xn = lower then
+                                             ERel (lower + 1)
+                                         else if xn >= lower + 1 andalso xn < lower + 1 + len then
+                                             ERel (xn - 1)
+                                         else
+                                             e
+                                       | _ => e,
+                bind = fn ((lower, len), U.Exp.RelE _) => (lower+1, len)
+                        | (st, _) => st}
+
 datatype result = Yes of E.env | No | Maybe
 
 fun match (env, p : pat, e : exp) =
@@ -272,15 +287,29 @@
         else
             #1 (reduceExp env (subExpInExp (0, e2) e1)))
 
-      | ECase (disc, pes, _) =>
+      | ECase (e', pes, {disc, result}) =>
         let
+            fun push () =
+                case result of
+                    (TFun (dom, result), loc) =>
+                    if List.all (fn (_, (EAbs _, _)) => true | _ => false) pes then
+                        EAbs ("_", dom, result,
+                              (ECase (liftExpInExp 0 e',
+                                      map (fn (p, (EAbs (_, _, _, e), _)) =>
+                                              (p, swapExpVarsPat (0, patBinds p) e)
+                                            | _ => raise Fail "MonoReduce ECase") pes,
+                                      {disc = disc, result = result}), loc))
+                    else
+                        e
+                  | _ => e
+
             fun search pes =
                 case pes of
-                    [] => e
+                    [] => push ()
                   | (p, body) :: pes =>
-                    case match (env, p, disc) of
+                    case match (env, p, e') of
                         No => search pes
-                      | Maybe => e
+                      | Maybe => push ()
                       | Yes env => #1 (reduceExp env body)
         in
             search pes
--- a/src/urweb.grm	Sun Sep 14 15:20:53 2008 -0400
+++ b/src/urweb.grm	Sun Sep 14 19:03:55 2008 -0400
@@ -43,6 +43,7 @@
 datatype select_item =
          Field of con * con
        | Exp of con * exp
+       | Fields of con * con
 
 datatype select =
          Star
@@ -77,6 +78,22 @@
             
             (tabs, exps)
         end
+      | Fields (tx, fs) =>
+        let
+            val (tabs, found) = ListUtil.foldlMap (fn ((tx', c'), found) =>
+                                                      if eqTnames (tx, tx') then
+                                                          ((tx', (CConcat (fs, c'), loc)), true)
+                                                      else
+                                                          ((tx', c'), found))
+                                                  false tabs
+        in
+            if found then
+                ()
+            else
+                ErrorMsg.errorAt loc "Select of field from unbound table";
+            
+            (tabs, exps)
+        end
       | Exp (c, e) => (tabs, (c, e) :: exps)
 
 fun amend_group loc (gi, tabs) =
@@ -1041,6 +1058,7 @@
 
 seli   : tident DOT fident              (Field (tident, fident))
        | sqlexp AS fident               (Exp (fident, sqlexp))
+       | tident DOT LBRACE LBRACE cexp RBRACE RBRACE (Fields (tident, cexp))
 
 selis  : seli                           ([seli])
        | seli COMMA selis               (seli :: selis)
--- a/tests/crud.ur	Sun Sep 14 15:20:53 2008 -0400
+++ b/tests/crud.ur	Sun Sep 14 19:03:55 2008 -0400
@@ -2,6 +2,7 @@
         Nam : string,
         Show : t_formT.1 -> xbody,
         Widget : nm :: Name -> xml form [] [nm = t_formT.2],
+        WidgetPopulated : nm :: Name -> t_formT.1 -> xml form [] [nm = t_formT.2],
         Parse : t_formT.2 -> t_formT.1,
         Inject : sql_injectable t_formT.1
 }
@@ -36,6 +37,29 @@
                 Inserted with ID {txt _ id}.
         </body></html>
 
+fun save (id : int) _ =
+        return <html><body>
+                Under Construction
+        </body></html>
+
+fun update (id : int) =
+        fso <- oneOrNoRows (SELECT tab.{{mapT2T fstTT M.cols}} FROM tab WHERE tab.Id = {id});
+        case fso : (Basis.option {Tab : $(mapT2T fstTT M.cols)}) of
+          None => return <html><body>Not found!</body></html>
+        | Some fs => return <html><body><lform>
+                {foldT2R2 [fstTT] [colMeta] [fn cols :: {(Type * Type)} => xml form [] (mapT2T sndTT cols)]
+                        (fn (nm :: Name) (t :: (Type * Type)) (rest :: {(Type * Type)}) =>
+                                [[nm] ~ rest] =>
+                                fn (v : t.1) (col : colMeta t) (acc : xml form [] (mapT2T sndTT rest)) => <lform>
+                                        <li> {cdata col.Nam}: {col.WidgetPopulated [nm] v}</li>
+                                        {useMore acc}
+                                </lform>)
+                        <lform></lform>
+                        [M.cols] fs.Tab M.cols}
+
+                <submit action={save id}/>
+        </lform></body></html>
+
 fun delete (id : int) =
         () <- dml (DELETE FROM tab WHERE Id = {id});
         return <html><body>
@@ -60,7 +84,7 @@
                                                         <td>{col.Show v}</td>
                                                 </tr>)
                                         [M.cols] (fs.T -- #Id) M.cols}
-                                <td><a link={confirm fs.T.Id}>[Delete]</a></td>
+                                <td><a link={update fs.T.Id}>[Update]</a> <a link={confirm fs.T.Id}>[Delete]</a></td>
                         </tr>
                 </body>);
         return <html><head>
--- a/tests/crud.urs	Sun Sep 14 15:20:53 2008 -0400
+++ b/tests/crud.urs	Sun Sep 14 19:03:55 2008 -0400
@@ -2,6 +2,7 @@
         Nam : string,
         Show : t_formT.1 -> xbody,
         Widget : nm :: Name -> xml form [] [nm = t_formT.2],
+        WidgetPopulated : nm :: Name -> t_formT.1 -> xml form [] [nm = t_formT.2],
         Parse : t_formT.2 -> t_formT.1,
         Inject : sql_injectable t_formT.1
 }
--- a/tests/crud1.ur	Sun Sep 14 15:20:53 2008 -0400
+++ b/tests/crud1.ur	Sun Sep 14 19:03:55 2008 -0400
@@ -17,6 +17,7 @@
                         Nam = "A",
                         Show = txt _,
                         Widget = fn nm :: Name => <lform><textbox{nm}/></lform>,
+                        WidgetPopulated = fn (nm :: Name) n => <lform><textbox{nm} value={show _ n}/></lform>,
                         Parse = readError _,
                         Inject = sql_int
                     },
@@ -24,6 +25,7 @@
                         Nam = "B",
                         Show = txt _,
                         Widget = fn nm :: Name => <lform><textbox{nm}/></lform>,
+                        WidgetPopulated = fn (nm :: Name) s => <lform><textbox{nm} value={s}/></lform>,
                         Parse = readError _,
                         Inject = sql_string
                     },
@@ -31,6 +33,7 @@
                         Nam = "C",
                         Show = txt _,
                         Widget = fn nm :: Name => <lform><textbox{nm}/></lform>,
+                        WidgetPopulated = fn (nm :: Name) n => <lform><textbox{nm} value={show _ n}/></lform>,
                         Parse = readError _,
                         Inject = sql_float
                     },
@@ -38,6 +41,7 @@
                         Nam = "D",
                         Show = txt _,
                         Widget = fn nm :: Name => <lform><textbox{nm}/></lform>,
+                        WidgetPopulated = fn (nm :: Name) b => <lform><textbox{nm} value={show _ b}/></lform>,
                         Parse = readError _,
                         Inject = sql_bool
                     }