changeset 759:67cd8326f743

subforms working
author Adam Chlipala <adamc@hcoop.net>
date Thu, 30 Apr 2009 13:47:46 -0400
parents 8323c1beef2e
children 21f6d2e65685
files include/urweb.h src/c/urweb.c src/cjr_print.sml tests/subforms.ur
diffstat 4 files changed, 297 insertions(+), 50 deletions(-) [+]
line wrap: on
line diff
--- a/include/urweb.h	Thu Apr 30 11:48:56 2009 -0400
+++ b/include/urweb.h	Thu Apr 30 13:47:46 2009 -0400
@@ -48,6 +48,8 @@
 uw_Basis_file uw_get_file_input(uw_context, int name);
 void uw_enter_subform(uw_context, int name);
 void uw_leave_subform(uw_context);
+int uw_enter_subforms(uw_context, int name);
+int uw_next_entry(uw_context);
 
 void uw_write(uw_context, const char*);
 
--- a/src/c/urweb.c	Thu Apr 30 11:48:56 2009 -0400
+++ b/src/c/urweb.c	Thu Apr 30 13:47:46 2009 -0400
@@ -284,7 +284,7 @@
 } delta;
 
 typedef enum {
-  UNSET, NORMAL, FIL, SUBFORM
+  UNSET, NORMAL, FIL, SUBFORM, SUBFORMS, ENTRY
 } input_kind;
 
 typedef struct input {
@@ -293,8 +293,14 @@
     char *normal;
     uw_Basis_file file;
     struct {
-      struct input *fields, *prev;
+      struct input *fields, *parent;
     } subform;
+    struct {
+      struct input *entries, *parent;
+    } subforms;
+    struct {
+      struct input *fields, *next, *parent;
+    } entry;
   } data;
 } input;
 
@@ -302,7 +308,7 @@
   char *headers, *headers_end;
 
   buf outHeaders, page, heap, script;
-  input *inputs, *subinputs, *cur_inputs;
+  input *inputs, *subinputs, *cur_container;
   size_t n_subinputs, used_subinputs;
 
   int source_count;
@@ -343,7 +349,7 @@
   ctx->script.start[0] = 0;
 
   ctx->inputs = calloc(uw_inputs_len, sizeof(input));
-  ctx->cur_inputs = NULL;
+  ctx->cur_container = NULL;
   ctx->subinputs = malloc(0);
   ctx->n_subinputs = ctx->used_subinputs = 0;
 
@@ -412,6 +418,7 @@
   ctx->source_count = 0;
   ctx->used_deltas = 0;
   ctx->client = NULL;
+  ctx->cur_container = NULL;
 }
 
 void uw_reset_keep_request(uw_context ctx) {
@@ -423,7 +430,6 @@
   uw_reset_keep_request(ctx);
   memset(ctx->inputs, 0, uw_inputs_len * sizeof(input));
   memset(ctx->subinputs, 0, ctx->n_subinputs * sizeof(input));
-  ctx->cur_inputs = NULL;
   ctx->used_subinputs = 0;
 }
 
@@ -577,11 +583,69 @@
 
 extern int uw_input_num(const char*);
 
-#define INP(ctx) (ctx->cur_inputs ? ctx->cur_inputs : ctx->inputs)
+static input *INP(uw_context ctx) {
+  if (ctx->cur_container == NULL)
+    return ctx->inputs;
+  else if (ctx->cur_container->kind == SUBFORM)
+    return ctx->cur_container->data.subform.fields;
+  else if (ctx->cur_container->kind == ENTRY)
+    return ctx->cur_container->data.entry.fields;
+  else
+    uw_error(ctx, FATAL, "INP: Wrong kind");
+}
+
+static void adjust_input(input *x, size_t offset) {
+  switch (x->kind) {
+  case SUBFORM:
+    x->data.subform.fields += offset;
+    if (x->data.subform.parent != NULL)
+      x->data.subform.parent += offset;
+    break;
+  case SUBFORMS:
+    if (x->data.subforms.entries != NULL)
+      x->data.subforms.entries += offset;
+    if (x->data.subforms.parent != NULL)
+      x->data.subforms.parent += offset;
+    break;
+  case ENTRY:
+    x->data.entry.fields += offset;
+    if (x->data.entry.next != NULL)
+      x->data.entry.next += offset;
+    if (x->data.entry.parent != NULL)
+      x->data.entry.parent += offset;
+  }  
+}
+
+static input *check_input_space(uw_context ctx, size_t len) {
+  size_t i;
+  input *r;
+
+  if (ctx->used_subinputs + len >= ctx->n_subinputs) {
+    input *new_subinputs = realloc(ctx->subinputs, sizeof(input) * (ctx->used_subinputs + len));
+    size_t offset = new_subinputs - ctx->subinputs;
+
+    for (i = 0; i < ctx->used_subinputs; ++i)
+      adjust_input(&new_subinputs[i], offset);
+    for (i = 0; i < uw_inputs_len; ++i)
+      adjust_input(&ctx->inputs[i], offset);
+
+    if (ctx->cur_container >= ctx->subinputs && ctx->cur_container < ctx->subinputs + ctx->n_subinputs)
+      ctx->cur_container += offset;
+
+    ctx->n_subinputs = ctx->used_subinputs + len;
+    ctx->subinputs = new_subinputs;
+  }
+
+  r = &ctx->subinputs[ctx->used_subinputs];
+
+  for (i = 0; i < len; ++i)
+    ctx->subinputs[ctx->used_subinputs++].kind = UNUSED;
+
+  return r;
+}
 
 void uw_set_input(uw_context ctx, const char *name, char *value) {
   if (!strcasecmp(name, ".b")) {
-    size_t i;
     int n = uw_input_num(value);
     input *inps;
 
@@ -591,46 +655,65 @@
     if (n >= uw_inputs_len)
       uw_error(ctx, FATAL, "For subform name %s, index %d is out of range", value, n);
 
-    if (ctx->used_subinputs + uw_inputs_len >= ctx->n_subinputs) {
-      input *new_subinputs = realloc(ctx->subinputs, sizeof(input) * (ctx->used_subinputs + uw_inputs_len));
-      size_t offset = new_subinputs - ctx->subinputs;
-
-      for (i = 0; i < ctx->used_subinputs; ++i)
-        if (new_subinputs[i].kind == SUBFORM) {
-          new_subinputs[i].data.subform.fields += offset;
-          if (new_subinputs[i].data.subform.prev != NULL)
-            new_subinputs[i].data.subform.prev += offset;
-        }
-
-      for (i = 0; i < uw_inputs_len; ++i)
-        if (ctx->inputs[i].kind == SUBFORM) {
-          ctx->inputs[i].data.subform.fields += offset;
-          if (ctx->inputs[i].data.subform.prev != NULL)
-            ctx->inputs[i].data.subform.prev += offset;
-        }
-
-      if (ctx->cur_inputs != NULL)
-        ctx->cur_inputs += offset;
-
-      ctx->n_subinputs = ctx->used_subinputs + uw_inputs_len;
-      ctx->subinputs = new_subinputs;
-    }
-
-    ctx->inputs[n].kind = SUBFORM;
-    ctx->inputs[n].data.subform.prev = ctx->cur_inputs;
-    ctx->cur_inputs = ctx->inputs[n].data.subform.fields = &ctx->subinputs[ctx->used_subinputs];
-
-    for (i = 0; i < uw_inputs_len; ++i)
-      ctx->subinputs[ctx->used_subinputs++].kind = UNUSED;
+    inps = check_input_space(ctx, uw_inputs_len);
+
+    INP(ctx)[n].kind = SUBFORM;
+    INP(ctx)[n].data.subform.parent = ctx->cur_container;
+    INP(ctx)[n].data.subform.fields = inps;
+    ctx->cur_container = &INP(ctx)[n];
   } else if (!strcasecmp(name, ".e")) {
     input *tmp;
 
-    if (ctx->cur_inputs == NULL)
+    if (ctx->cur_container == NULL)
       uw_error(ctx, FATAL, "Unmatched subform closer");
 
-    tmp = ctx->cur_inputs;
-    ctx->cur_inputs = tmp->data.subform.prev;
-    tmp->data.subform.prev = NULL;
+    tmp = ctx->cur_container;
+    switch (tmp->kind) {
+    case SUBFORM:
+      ctx->cur_container = tmp->data.subform.parent;
+      tmp->data.subform.parent = NULL;
+      break;
+    case SUBFORMS:
+      ctx->cur_container = tmp->data.subforms.parent;
+      tmp->data.subforms.parent = NULL;
+      break;
+    case ENTRY:
+      ctx->cur_container = tmp->data.entry.parent;
+      break;
+    default:
+      uw_error(ctx, FATAL, "uw_set_input: Wrong kind");
+    }
+  } else if (!strcasecmp(name, ".s")) {
+    int n = uw_input_num(value);
+
+    if (n < 0)
+      uw_error(ctx, FATAL, "Bad subforms name %s", value);
+
+    if (n >= uw_inputs_len)
+      uw_error(ctx, FATAL, "For subforms name %s, index %d is out of range", value, n);
+
+    INP(ctx)[n].kind = SUBFORMS;
+    INP(ctx)[n].data.subforms.parent = ctx->cur_container;
+    INP(ctx)[n].data.subforms.entries = NULL;
+    ctx->cur_container = &INP(ctx)[n];
+  } else if (!strcasecmp(name, ".i")) {
+    input *inps;
+
+    if (!ctx->cur_container)
+      uw_error(ctx, FATAL, "New entry without container");
+
+    if (ctx->cur_container->kind != SUBFORMS)
+      uw_error(ctx, FATAL, "Bad kind for entry parent");
+
+    inps = check_input_space(ctx, uw_inputs_len + 1);
+
+    inps->kind = ENTRY;
+    inps->data.entry.parent = ctx->cur_container;
+    inps->data.entry.next = ctx->cur_container->data.subforms.entries;
+    ctx->cur_container->data.subforms.entries = inps;
+
+    inps->data.entry.fields = inps+1;
+    ctx->cur_container = inps;
   } else {
     int n = uw_input_num(name);
 
@@ -658,6 +741,10 @@
     uw_error(ctx, FATAL, "Tried to read a file form input as normal");
   case SUBFORM:
     uw_error(ctx, FATAL, "Tried to read a subform form input as normal");
+  case SUBFORMS:
+    uw_error(ctx, FATAL, "Tried to read a subforms form input as normal");
+  case ENTRY:
+    uw_error(ctx, FATAL, "Tried to read an entry form input as normal");
   case NORMAL:
     return INP(ctx)[n].data.normal;
   default:
@@ -678,6 +765,10 @@
     uw_error(ctx, FATAL, "Tried to read a file form input as normal");
   case SUBFORM:
     uw_error(ctx, FATAL, "Tried to read a subform form input as normal");
+  case SUBFORMS:
+    uw_error(ctx, FATAL, "Tried to read a subforms form input as normal");
+  case ENTRY:
+    uw_error(ctx, FATAL, "Tried to read an entry form input as normal");
   case NORMAL:
     return INP(ctx)[n].data.normal;
   default:
@@ -719,6 +810,10 @@
     uw_error(ctx, FATAL, "Tried to read a normal form input as files");
   case SUBFORM:
     uw_error(ctx, FATAL, "Tried to read a subform form input as files");
+  case SUBFORMS:
+    uw_error(ctx, FATAL, "Tried to read a subforms form input as files");
+  case ENTRY:
+    uw_error(ctx, FATAL, "Tried to read an entry form input as files");
   default:
     uw_error(ctx, FATAL, "Impossible input kind");
   }
@@ -737,9 +832,13 @@
     uw_error(ctx, FATAL, "Tried to read a file form input as subform");
   case NORMAL:
     uw_error(ctx, FATAL, "Tried to read a normal form input as subform");
+  case SUBFORMS:
+    uw_error(ctx, FATAL, "Tried to read a subforms form input as subform");
+  case ENTRY:
+    uw_error(ctx, FATAL, "Tried to read an entry form input as subform");
   case SUBFORM:
-    INP(ctx)[n].data.subform.prev = ctx->cur_inputs;
-    ctx->cur_inputs = INP(ctx)[n].data.subform.fields;
+    INP(ctx)[n].data.subform.parent = ctx->cur_container;
+    ctx->cur_container = INP(ctx)[n].data.subform.fields;
     return;
   default:
     uw_error(ctx, FATAL, "Impossible input kind");
@@ -749,12 +848,72 @@
 void uw_leave_subform(uw_context ctx) {
   input *tmp;
 
-  if (ctx->cur_inputs == NULL)
+  if (ctx->cur_container == NULL)
     uw_error(ctx, FATAL, "Unmatched uw_leave_subform");
 
-  tmp = ctx->cur_inputs;
-  ctx->cur_inputs = tmp->data.subform.prev;
-  tmp->data.subform.prev = NULL;
+  tmp = ctx->cur_container;
+  ctx->cur_container = tmp->data.subform.parent;
+  tmp->data.subform.parent = NULL;
+}
+
+int uw_enter_subforms(uw_context ctx, int n) {
+  input *inps;
+
+  if (n < 0)
+    uw_error(ctx, FATAL, "Negative subforms index %d", n);
+  if (n >= uw_inputs_len)
+    uw_error(ctx, FATAL, "Out-of-bounds subforms index %d", n);
+
+  switch (INP(ctx)[n].kind) {
+  case UNSET:
+    uw_error(ctx, FATAL, "Missing subforms");
+  case FIL:
+    uw_error(ctx, FATAL, "Tried to read a file form input as subforms");
+  case NORMAL:
+    uw_error(ctx, FATAL, "Tried to read a normal form input %p as subforms", &INP(ctx)[n]);
+  case SUBFORM:
+    uw_error(ctx, FATAL, "Tried to read a subform form input as subforms");
+  case ENTRY:
+    uw_error(ctx, FATAL, "Tried to read an entry form input as subforms");
+  case SUBFORMS:
+    inps = INP(ctx)[n].data.subforms.entries;
+    if (inps) {
+      INP(ctx)[n].data.subforms.parent = ctx->cur_container;
+      ctx->cur_container = INP(ctx)[n].data.subforms.entries;
+      return 1;
+    } else
+      return 0;
+  default:
+    uw_error(ctx, FATAL, "Impossible input kind");
+  }
+}
+
+int uw_next_entry(uw_context ctx) {
+  if (ctx->cur_container == NULL)
+    uw_error(ctx, FATAL, "uw_next_entry(NULL)");
+
+  switch (ctx->cur_container->kind) {
+  case UNSET:
+    uw_error(ctx, FATAL, "Missing entry");
+  case FIL:
+    uw_error(ctx, FATAL, "Tried to read a file form input as entry");
+  case NORMAL:
+    uw_error(ctx, FATAL, "Tried to read a normal form input as entry");
+  case SUBFORM:
+    uw_error(ctx, FATAL, "Tried to read a subform form input as entry");
+  case SUBFORMS:
+    uw_error(ctx, FATAL, "Tried to read a subforms form input as entry");
+  case ENTRY:
+    if (ctx->cur_container->data.entry.next) {
+      ctx->cur_container = ctx->cur_container->data.entry.next;
+      return 1;
+    } else {
+      ctx->cur_container = ctx->cur_container->data.entry.parent->data.subforms.parent;
+      return 0;
+    }
+  default:
+    uw_error(ctx, FATAL, "Impossible input kind");
+  }
 }
 
 void uw_set_script_header(uw_context ctx, const char *s) {
--- a/src/cjr_print.sml	Thu Apr 30 11:48:56 2009 -0400
+++ b/src/cjr_print.sml	Thu Apr 30 13:47:46 2009 -0400
@@ -2425,6 +2425,14 @@
                 in
                     SOME (map #1 xts :: List.concat (List.mapPartial (flatFields o #2) xts))
                 end
+              | TList (_, i) =>
+                let
+                    val ts = E.lookupStruct env i
+                in
+                    case ts of
+                        [("1", t'), ("2", _)] => flatFields t'
+                      | _ => raise Fail "CjrPrint: Bad struct for TList"
+                end
               | _ => NONE
 
         val fields = foldl (fn ((ek, _, _, ts, _, _), fields) =>
@@ -2566,7 +2574,7 @@
         fun getInput (x, t) =
             let
                 val n = case SM.find (fnums, x) of
-                            NONE => raise Fail "CjrPrint: Can't find in fnums"
+                            NONE => raise Fail ("CjrPrint: Can't find " ^ x ^ " in fnums")
                           | SOME n => n
 
                 val f = case t of
@@ -2631,6 +2639,76 @@
                                   newline,
                                   string "uw_leave_subform(ctx);"]
                          end
+                       | TList (t', i) =>
+                         let
+                             val xts = E.lookupStruct env i
+                             val i' = case xts of
+                                          [("1", (TRecord i', loc)), ("2", _)] => i'
+                                        | _ => raise Fail "CjrPrint: Bad TList record [2]"
+                             val xts = E.lookupStruct env i'
+                         in
+                             box [string "{",
+                                  newline,
+                                  string "int status;",
+                                  newline,
+                                  string "uw_input_",
+                                  p_ident x,
+                                  space,
+                                  string "=",
+                                  space,
+                                  string "NULL;",
+                                  newline,
+                                  string "for (status = uw_enter_subforms(ctx, ",
+                                  string (Int.toString n),
+                                  string "); status; status = uw_next_entry(ctx)) {",
+                                  newline,
+                                  box [p_typ env t,
+                                       space,
+                                       string "result",
+                                       space,
+                                       string "=",
+                                       space,
+                                       string "uw_malloc(ctx, sizeof(struct __uws_",
+                                       string (Int.toString i),
+                                       string "));",
+                                       newline,
+                                       box [string "{",
+                                            p_list_sep (box [])
+                                                       (fn (x, t) =>
+                                                           box [p_typ env t,
+                                                                space,
+                                                                string "uw_input_",
+                                                                string x,
+                                                                string ";",
+                                                                newline])
+                                                       xts,
+                                            newline,
+                                            p_list_sep (box []) (fn (x, t) =>
+                                                                    box [getInput (x, t),
+                                                                         string "result->__uwf_1.__uwf_",
+                                                                         string x,
+                                                                         space,
+                                                                         string "=",
+                                                                         space,
+                                                                         string "uw_input_",
+                                                                         string x,
+                                                                         string ";",
+                                                                         newline])
+                                                       xts,
+                                            string "}",
+                                            newline],
+                                       newline,
+                                       string "result->__uwf_2 = uw_input_",
+                                       p_ident x,
+                                       string ";",
+                                       newline,
+                                       string "uw_input_",
+                                       p_ident x,
+                                       string " = result;",
+                                       newline],
+                                  string "}}",
+                                  newline]
+                         end
                        | _ =>
                          box [string "request = uw_get_",
                               string f,
--- a/tests/subforms.ur	Thu Apr 30 11:48:56 2009 -0400
+++ b/tests/subforms.ur	Thu Apr 30 13:47:46 2009 -0400
@@ -4,7 +4,9 @@
       | Cons (r, ls) => <xml><li>{[r.A]}, {[r.B]}, {[r.Sub]}</li>{handler' ls}</xml>
 
 fun handler r = return <xml><body>
-  {[r.A]}, {handler' r.Sub}, {[r.C]}
+  {[r.A]}<br/>
+  {handler' r.Sub}
+  {[r.C]}
 </body></xml>
 
 fun main () = return <xml><body>
@@ -16,6 +18,12 @@
         <textbox{#B}/><br/>
         <textbox{#Sub}/><br/>
       </entry>
+
+      <entry>
+        <textbox{#A}/><br/>
+        <textbox{#B}/><br/>
+        <textbox{#Sub}/><br/>
+      </entry>
     </subforms>
     <textbox{#C}/><br/>
     <submit action={handler}/>