diff src/cjr_print.sml @ 144:f0d3402184d1

Simple forms work
author Adam Chlipala <adamc@hcoop.net>
date Tue, 22 Jul 2008 15:12:20 -0400
parents 4b9c2bd6157c
children e52dfb1e6b19
line wrap: on
line diff
--- a/src/cjr_print.sml	Sun Jul 20 13:30:19 2008 -0400
+++ b/src/cjr_print.sml	Tue Jul 22 15:12:20 2008 -0400
@@ -37,6 +37,20 @@
 structure E = CjrEnv
 structure EM = ErrorMsg
 
+structure SK = struct
+type ord_key = string
+val compare = String.compare
+end
+
+structure SS = BinarySetFn(SK)
+structure SM = BinaryMapFn(SK)
+structure IS = IntBinarySet
+
+structure CM = BinaryMapFn(struct
+                           type ord_key = char
+                           val compare = Char.compare
+                           end)
+
 val debug = ref false
 
 val dummyTyp = (TNamed 0, ErrorMsg.dummySpan)
@@ -208,90 +222,11 @@
                  newline]
         end
 
-fun unurlify env (t, loc) =
-    case t of
-        TFfi ("Basis", "int") => string "lw_unurlifyInt(&request)"
-      | TFfi ("Basis", "float") => string "lw_unurlifyFloat(&request)"
-      | TFfi ("Basis", "string") => string "lw_unurlifyString(ctx, &request)"
+datatype 'a search =
+         Found of 'a
+       | NotFound
+       | Error
 
-      | TRecord 0 => string "lw_unit_v"
-      | TRecord i =>
-        let
-            val xts = E.lookupStruct env i
-        in
-            box [string "({",
-                 newline,
-                 box (map (fn (x, t) =>
-                              box [p_typ env t,
-                                   space,
-                                   string x,
-                                   space,
-                                   string "=",
-                                   space,
-                                   unurlify env t,
-                                   string ";",
-                                   newline]) xts),
-                 string "struct",
-                 space,
-                 string "__lws_",
-                 string (Int.toString i),
-                 space,
-                 string "__lw_tmp",
-                 space,
-                 string "=",
-                 space,
-                 string "{",
-                 space,
-                 p_list_sep (box [string ",", space]) (fn (x, _) => string x) xts,
-                 space,
-                 string "};",
-                 newline,
-                 string "__lw_tmp;",
-                 newline,
-                 string "})"]
-        end
-
-      | _ => (ErrorMsg.errorAt loc "Unable to choose a URL decoding function";
-              space)
-
-fun p_page env (s, n, ts) =
-    box [string "if (!strncmp(request, \"",
-         string (String.toString s),
-         string "\", ",
-         string (Int.toString (size s)),
-         string ")) {",
-         newline,
-         string "request += ",
-         string (Int.toString (size s)),
-         string ";",
-         newline,
-         string "if (*request == '/') ++request;",
-         newline,
-         box [string "{",
-              newline,
-              box (ListUtil.mapi (fn (i, t) => box [p_typ env t,
-                                                    space,
-                                                    string "arg",
-                                                    string (Int.toString i),
-                                                    space,
-                                                    string "=",
-                                                    space,
-                                                    unurlify env t,
-                                                    string ";",
-                                                    newline]) ts),
-              p_enamed env n,
-              string "(",
-              p_list_sep (box [string ",", space])
-                         (fn x => x)
-                         (string "ctx" :: ListUtil.mapi (fn (i, _) => string ("arg" ^ Int.toString i)) ts),
-              string ");",
-              newline,
-              string "return;",
-              newline,
-              string "}",
-              newline,
-              string "}"]
-        ]
 
 fun p_file env (ds, ps) =
     let
@@ -299,13 +234,318 @@
                                              (p_decl env d,
                                               E.declBinds env d))
                              env ds
-        val pds' = map (p_page env) ps
+
+        val fields = foldl (fn ((ek, _, _, ts), fields) =>
+                               case ek of
+                                   Core.Link => fields
+                                 | Core.Action =>
+                                   case List.last ts of
+                                       (TRecord i, _) =>
+                                       let
+                                           val xts = E.lookupStruct env i
+                                           val xtsSet = SS.addList (SS.empty, map #1 xts)
+                                       in
+                                           foldl (fn ((x, _), fields) =>
+                                                     let
+                                                         val xtsSet' = Option.getOpt (SM.find (fields, x), SS.empty)
+                                                     in
+                                                         SM.insert (fields, x, SS.union (SS.delete (xtsSet, x),
+                                                                                         xtsSet'))
+                                                     end) fields xts
+                                       end
+                                     | _ => raise Fail "CjrPrint: Last argument of action isn't record")
+                     SM.empty ps
+
+        val fnums = SM.foldli (fn (x, xs, fnums) =>
+                                  let
+                                      val unusable = SS.foldl (fn (x', unusable) =>
+                                                                  case SM.find (fnums, x') of
+                                                                      NONE => unusable
+                                                                    | SOME n => IS.add (unusable, n))
+                                                     IS.empty xs
+
+                                      fun findAvailable n =
+                                          if IS.member (unusable, n) then
+                                              findAvailable (n + 1)
+                                          else
+                                              n
+                                  in
+                                      SM.insert (fnums, x, findAvailable 0)
+                                  end)
+                    SM.empty fields
+
+        fun makeSwitch (fnums, i) =
+            case SM.foldl (fn (n, NotFound) => Found n
+                            | (n, Error) => Error
+                            | (n, Found n') => if n = n' then
+                                                  Found n'
+                                               else
+                                                   Error) NotFound fnums of
+                NotFound => box [string "return",
+                                 space,
+                                 string "-1;"]
+              | Found n => box [string "return",
+                                space,
+                                string (Int.toString n),
+                                string ";"]
+              | Error =>
+                let
+                    val cmap = SM.foldli (fn (x, n, cmap) =>
+                                             let
+                                                 val ch = if i < size x then
+                                                              String.sub (x, i)
+                                                          else
+                                                              chr 0
+
+                                                 val fnums = case CM.find (cmap, ch) of
+                                                                 NONE => SM.empty
+                                                               | SOME fnums => fnums
+                                                 val fnums = SM.insert (fnums, x, n)
+                                             in
+                                                 CM.insert (cmap, ch, fnums)
+                                             end)
+                               CM.empty fnums
+
+                    val cmap = CM.listItemsi cmap
+                in
+                    case cmap of
+                        [(_, fnums)] =>
+                        box [string "if",
+                             space,
+                             string "(name[",
+                             string (Int.toString i),
+                             string "]",
+                             space,
+                             string "==",
+                             space,
+                             string "0)",
+                             space,
+                             string "return",
+                             space,
+                             string "-1;",
+                             newline,
+                             makeSwitch (fnums, i+1)]
+                      | _ =>
+                        box [string "switch",
+                             space,
+                             string "(name[",
+                             string (Int.toString i),
+                             string "])",
+                             space,
+                             string "{",
+                             newline,
+                             box (map (fn (ch, fnums) =>
+                                          box [string "case",
+                                               space,
+                                               if ch = chr 0 then
+                                                   string "0:"
+                                               else
+                                                   box [string "'",
+                                                        string (Char.toString ch),
+                                                        string "':"],
+                                               newline,
+                                               makeSwitch (fnums, i+1),
+                                               newline]) cmap),
+                             string "default:",
+                             newline,
+                             string "return",
+                             space,
+                             string "-1;",
+                             newline,
+                             string "}"]
+                end
+
+        fun unurlify (t, loc) =
+            case t of
+                TFfi ("Basis", "int") => string "lw_unurlifyInt(&request)"
+              | TFfi ("Basis", "float") => string "lw_unurlifyFloat(&request)"
+              | TFfi ("Basis", "string") => string "lw_unurlifyString(ctx, &request)"
+
+              | TRecord 0 => string "lw_unit_v"
+              | TRecord i =>
+                let
+                    val xts = E.lookupStruct env i
+                in
+                    box [string "({",
+                         newline,
+                         box (map (fn (x, t) =>
+                                      box [p_typ env t,
+                                           space,
+                                           string x,
+                                           space,
+                                           string "=",
+                                           space,
+                                           unurlify t,
+                                           string ";",
+                                           newline]) xts),
+                         string "struct",
+                         space,
+                         string "__lws_",
+                         string (Int.toString i),
+                         space,
+                         string "__lw_tmp",
+                         space,
+                         string "=",
+                         space,
+                         string "{",
+                         space,
+                         p_list_sep (box [string ",", space]) (fn (x, _) => string x) xts,
+                         space,
+                         string "};",
+                         newline,
+                         string "__lw_tmp;",
+                         newline,
+                         string "})"]
+                end
+
+              | _ => (ErrorMsg.errorAt loc "Unable to choose a URL decoding function";
+                      space)
+
+
+        fun p_page (ek, s, n, ts) =
+            let
+                val (ts, defInputs, inputsVar) =
+                    case ek of
+                        Core.Link => (ts, string "", string "")
+                      | Core.Action =>
+                        case List.last ts of
+                            (TRecord i, _) =>
+                            let
+                                val xts = E.lookupStruct env i
+                            in
+                                (List.drop (ts, 1),
+                                 box [box (map (fn (x, t) => box [p_typ env t,
+                                                                  space,
+                                                                  string "lw_input_",
+                                                                  string x,
+                                                                  string ";",
+                                                                  newline]) xts),
+                                      newline,
+                                      box (map (fn (x, t) =>
+                                                   let
+                                                       val n = case SM.find (fnums, x) of
+                                                                   NONE => raise Fail "CjrPrint: Can't find in fnums"
+                                                                 | SOME n => n
+                                                   in
+                                                       box [string "request = lw_get_input(ctx, ",
+                                                            string (Int.toString n),
+                                                            string ");",
+                                                            newline,
+                                                            string "if (request == NULL) {",
+                                                            newline,
+                                                            box [string "printf(\"Missing input ",
+                                                                 string x,
+                                                                 string "\\n\");",
+                                                                 newline,
+                                                                 string "exit(1);"],
+                                                            newline,
+                                                            string "}",
+                                                            newline,
+                                                            string "lw_input_",
+                                                            string x,
+                                                            space,
+                                                            string "=",
+                                                            space,
+                                                            unurlify t,
+                                                            string ";",
+                                                            newline]
+                                                   end) xts),
+                                      string "struct __lws_",
+                                      string (Int.toString i),
+                                      space,
+                                      string "lw_inputs",
+                                      space,
+                                      string "= {",
+                                      newline,
+                                      box (map (fn (x, _) => box [string "lw_input_",
+                                                                  string x,
+                                                                  string ",",
+                                                                  newline]) xts),
+                                      string "};",
+                                      newline],
+                                 box [string ",",
+                                      space,
+                                      string "lw_inputs"])
+                            end
+
+                          | _ => raise Fail "CjrPrint: Last argument to an action isn't a record"
+            in
+                box [string "if (!strncmp(request, \"",
+                     string (String.toString s),
+                     string "\", ",
+                     string (Int.toString (size s)),
+                     string ")) {",
+                     newline,
+                     string "request += ",
+                     string (Int.toString (size s)),
+                     string ";",
+                     newline,
+                     string "if (*request == '/') ++request;",
+                     newline,
+                     box [string "{",
+                          newline,
+                          box (ListUtil.mapi (fn (i, t) => box [p_typ env t,
+                                                                space,
+                                                                string "arg",
+                                                                string (Int.toString i),
+                                                                space,
+                                                                string "=",
+                                                                space,
+                                                                unurlify t,
+                                                                string ";",
+                                                                newline]) ts),
+                          defInputs,
+                          p_enamed env n,
+                          string "(",
+                          p_list_sep (box [string ",", space])
+                                     (fn x => x)
+                                     (string "ctx" :: ListUtil.mapi (fn (i, _) => string ("arg" ^ Int.toString i)) ts),
+                          inputsVar,
+                          string ");",
+                          newline,
+                          string "return;",
+                          newline,
+                          string "}",
+                          newline,
+                          string "}"]
+                    ]
+            end
+
+        val pds' = map p_page ps
     in
-        box [string "#include \"lacweb.h\"",
+        box [string "#include <stdio.h>",
+             newline,
+             string "#include <stdlib.h>",
+             newline,
+             newline,
+             string "#include \"lacweb.h\"",
              newline,
              newline,
              p_list_sep newline (fn x => x) pds,
              newline,
+             string "int lw_inputs_len = ",
+             string (Int.toString (SM.foldl Int.max 0 fnums + 1)),
+             string ";",
+             newline,
+             newline,
+             string "int lw_input_num(char *name) {",
+             newline,
+             string "if",
+             space,
+             string "(name[0]",
+             space,
+             string "==",
+             space,
+             string "0)",
+             space,
+             string "return",
+             space,
+             string "-1;",
+             newline,
+             makeSwitch (fnums, 0),
+             string "}",
+             newline,
+             newline,
              string "void lw_handle(lw_context ctx, char *request) {",
              newline,
              p_list_sep newline (fn x => x) pds',