first cut at named parameters
[p5sagit/Function-Parameters.git] / herp.c
diff --git a/herp.c b/herp.c
new file mode 100644 (file)
index 0000000..2b3ee49
--- /dev/null
+++ b/herp.c
@@ -0,0 +1,164 @@
+
+#define DEFSTRUCT(T) typedef struct T T; struct T
+
+DEFSTRUCT(ParamInit) {
+       SV *name;
+       OP *init;
+};
+
+#define DEFVECTOR(T, B) DEFSTRUCT(T) { \
+       B (*data); \
+       size_t used, size; \
+}
+
+DEFVECTOR(ParamInitVec, ParamInit);
+DEFVECTOR(ParamVec, SV *);
+
+DEFSTRUCT(ParamSpec) {
+       SV *invocant;
+       ParamVec positional_required;
+       ParamInitVec positional_optional;
+       ParamVec named_required;
+       ParamInitVec named_optional;
+       SV *slurpy;
+};
+
+#define DEFVECTOR_CLEAR(T, N, F) static void N(T *p) { \
+       while (p->used) { \
+               p->used--; \
+               F(p->data[p->used]); \
+       } \
+       Safefree(p->data); \
+       p->data =NULL; \
+       p->size = 0; \
+} static void N(T *)
+
+static void pi_clear(ParamInit pi) {
+       if (pi.name) {
+               SvREFCNT_dec(pi.name);
+       }
+       if (pi.init) {
+               op_free(pi.init);
+       }
+}
+
+DEFVECTOR_CLEAR(ParamVec, pv_clear, SvREFCNT_dec);
+DEFVECTOR_CLEAR(ParamInitVec, piv_clear, pi_clear);
+
+static void ps_free(ParamSpec *ps) {
+       if (ps->invocant) {
+               SvREFCNT_dec(ps->invocant);
+               ps->invocant = NULL;
+       }
+       pv_clear(&ps->positional_required);
+       piv_clear(&ps->positional_optional);
+       pv_clear(&ps->named_required);
+       piv_clear(&ps->named_optional);
+       if (ps->slurpy) {
+               SvREFCNT_dec(ps->slurpy);
+               ps->slurpy = NULL;
+       }
+       Safefree(ps);
+}
+
+static int args_min(const ParamSpec *ps) {
+       int n = 0;
+       if (ps->invocant) {
+               n++;
+       }
+       n += ps->positional_required.used;
+       n += ps->named_required.used * 2;
+       return n;
+}
+
+static int args_max(const ParamSpec *ps) {
+       int n = 0;
+       if (ps->invocant) {
+               n++;
+       }
+       n += ps->positional_required.used;
+       n += ps->positional_optional.used;
+       if (ps->named_required.used || ps->named_optional.used || ps->slurpy) {
+               n = -1;
+       }
+       return n;
+}
+
+static size_t count_positional_params(const ParamSpec *ps) {
+       return ps->positional_required.used + ps->positional_optional.used;
+}
+
+static size_t count_named_params(const ParamSpec *ps) {
+       return ps->named_required.used + ps->named_optional.used;
+}
+
+static void gen(ParamSpec *ps) {
+       int amin = args_min(ps);
+       if (amin > 0) {
+               printf("croak 'not enough' if @_ < %d;\n", amin);
+       }
+       int amax = args_max(ps);
+       if (amax >= 0) {
+               printf("croak 'too many' if @_ > %d;\n", amax);
+       }
+       size_t named_params = count_named_params(ps);
+       if (named_params || (ps->slurpy && SvPV_nolen(ps->slurpy)[0] == '%')) {
+               size_t t = named_params + !!ps->invocant;
+               printf("croak 'odd' if (@_ - %zu) > 0 && (@_ - %zu) % 2;\n", t, t);
+       }
+       if (ps->invocant) {
+               printf("%s = shift @_;\n", SvPV_nolen(ps->invocant));
+       }
+       // XXX (...) = @_;
+       size_t positional_params = count_positional_params(ps);
+       for (size_t i = 0; i < ps->positional_optional.used; i++) {
+               printf("%*sif (@_ < %zu) {\n", (int)i * 2, "", positional_params - i);
+       }
+       for (size_t i = 0; i < ps->positional_optional.used; i++) {
+               ParamInit *pi = &ps->positional_optional.data[i];
+               printf("%*s  %s = %p;\n", (int)(ps->positional_optional.used - i - 1) * 2, "", SvPV_nolen(pi->name), pi->init);
+               printf("%*s}\n", (int)(ps->positional_optional.used - i - 1) * 2, "");
+       }
+       if (named_params) {
+               printf("if (@_ > %zu) {\n", positional_params);
+               printf("  my $_b = '';\n");
+               printf("  my $_i = %zu;\n", positional_params);
+               printf("  while ($_i < @_) {\n");
+               printf("    my $_k = $_[$_i];\n");
+               size_t req = ps->named_required.used;
+               for (size_t i = 0; i < named_params; i++) {
+                       printf("    ");
+                       if (i) {
+                               printf("els");
+                       }
+                       SV *param = i >= req ? ps->named_optional.data[i - req] : ps->named_required.data[i];
+                       printf("if ($_k eq '%s') {\n", SvPV_nolen(param) + 1);
+                       printf("      %s = $_[$_i + 1];\n", SvPV_nolen(param));
+                       printf("      vec($_b, %zu, 1) = 1;\n", i);
+                       printf("    }\n");
+               }
+               printf("    else {\n");
+               if (ps->slurpy) {
+                       const char *slurpy = SvPV_nolen(ps->slurpy);
+                       if (slurpy[0] == '%') {
+                               printf("      $%s{$_k} = $_[$_i + 1];\n", slurpy + 1);
+                       } else {
+                               printf("      push %s, $_k, $_[$_i + 1];\n", slurpy);
+                       }
+               } else {
+                       printf("      croak 'no such param ' . $_k;\n");
+               }
+               printf("    }\n");
+               printf("    $_i += 2;\n");
+               printf("  }\n");
+               if (ps->named_required.used) {
+                       printf("  if (($_b & pack('b*', '1' x %zu)) ne pack('b*', '1' x %zu)) {\n", ps->named_required.used, ps->named_required.used);
+                       printf("    croak 'missing required named args';\n"); // XXX
+                       printf("  }\n");
+               }
+               for (size_t i = 0; i < ps->named_optional.used; i++) {
+                       printf("  %s = %p unless vec $_k, %zu, 1;\n", SvPV_nolen(ps->named_optional[i].name), ps->named_optional[i].init, ps->named_required.used + i);
+               }
+               printf("}\n");
+       }
+}