steal more tests from other modules
[p5sagit/Function-Parameters.git] / herp.c
1
2 #define DEFSTRUCT(T) typedef struct T T; struct T
3
4 DEFSTRUCT(ParamInit) {
5         SV *name;
6         OP *init;
7 };
8
9 #define DEFVECTOR(T, B) DEFSTRUCT(T) { \
10         B (*data); \
11         size_t used, size; \
12 }
13
14 DEFVECTOR(ParamInitVec, ParamInit);
15 DEFVECTOR(ParamVec, SV *);
16
17 DEFSTRUCT(ParamSpec) {
18         SV *invocant;
19         ParamVec positional_required;
20         ParamInitVec positional_optional;
21         ParamVec named_required;
22         ParamInitVec named_optional;
23         SV *slurpy;
24 };
25
26 #define DEFVECTOR_CLEAR(T, N, F) static void N(T *p) { \
27         while (p->used) { \
28                 p->used--; \
29                 F(p->data[p->used]); \
30         } \
31         Safefree(p->data); \
32         p->data =NULL; \
33         p->size = 0; \
34 } static void N(T *)
35
36 static void pi_clear(ParamInit pi) {
37         if (pi.name) {
38                 SvREFCNT_dec(pi.name);
39         }
40         if (pi.init) {
41                 op_free(pi.init);
42         }
43 }
44
45 DEFVECTOR_CLEAR(ParamVec, pv_clear, SvREFCNT_dec);
46 DEFVECTOR_CLEAR(ParamInitVec, piv_clear, pi_clear);
47
48 static void ps_free(ParamSpec *ps) {
49         if (ps->invocant) {
50                 SvREFCNT_dec(ps->invocant);
51                 ps->invocant = NULL;
52         }
53         pv_clear(&ps->positional_required);
54         piv_clear(&ps->positional_optional);
55         pv_clear(&ps->named_required);
56         piv_clear(&ps->named_optional);
57         if (ps->slurpy) {
58                 SvREFCNT_dec(ps->slurpy);
59                 ps->slurpy = NULL;
60         }
61         Safefree(ps);
62 }
63
64 static int args_min(const ParamSpec *ps) {
65         int n = 0;
66         if (ps->invocant) {
67                 n++;
68         }
69         n += ps->positional_required.used;
70         n += ps->named_required.used * 2;
71         return n;
72 }
73
74 static int args_max(const ParamSpec *ps) {
75         int n = 0;
76         if (ps->invocant) {
77                 n++;
78         }
79         n += ps->positional_required.used;
80         n += ps->positional_optional.used;
81         if (ps->named_required.used || ps->named_optional.used || ps->slurpy) {
82                 n = -1;
83         }
84         return n;
85 }
86
87 static size_t count_positional_params(const ParamSpec *ps) {
88         return ps->positional_required.used + ps->positional_optional.used;
89 }
90
91 static size_t count_named_params(const ParamSpec *ps) {
92         return ps->named_required.used + ps->named_optional.used;
93 }
94
95 static void gen(ParamSpec *ps) {
96         int amin = args_min(ps);
97         if (amin > 0) {
98                 printf("croak 'not enough' if @_ < %d;\n", amin);
99         }
100         int amax = args_max(ps);
101         if (amax >= 0) {
102                 printf("croak 'too many' if @_ > %d;\n", amax);
103         }
104         size_t named_params = count_named_params(ps);
105         if (named_params || (ps->slurpy && SvPV_nolen(ps->slurpy)[0] == '%')) {
106                 size_t t = named_params + !!ps->invocant;
107                 printf("croak 'odd' if (@_ - %zu) > 0 && (@_ - %zu) % 2;\n", t, t);
108         }
109         if (ps->invocant) {
110                 printf("%s = shift @_;\n", SvPV_nolen(ps->invocant));
111         }
112         // XXX (...) = @_;
113         size_t positional_params = count_positional_params(ps);
114         for (size_t i = 0; i < ps->positional_optional.used; i++) {
115                 printf("%*sif (@_ < %zu) {\n", (int)i * 2, "", positional_params - i);
116         }
117         for (size_t i = 0; i < ps->positional_optional.used; i++) {
118                 ParamInit *pi = &ps->positional_optional.data[i];
119                 printf("%*s  %s = %p;\n", (int)(ps->positional_optional.used - i - 1) * 2, "", SvPV_nolen(pi->name), pi->init);
120                 printf("%*s}\n", (int)(ps->positional_optional.used - i - 1) * 2, "");
121         }
122         if (named_params) {
123                 printf("if (@_ > %zu) {\n", positional_params);
124                 printf("  my $_b = '';\n");
125                 printf("  my $_i = %zu;\n", positional_params);
126                 printf("  while ($_i < @_) {\n");
127                 printf("    my $_k = $_[$_i];\n");
128                 size_t req = ps->named_required.used;
129                 for (size_t i = 0; i < named_params; i++) {
130                         printf("    ");
131                         if (i) {
132                                 printf("els");
133                         }
134                         SV *param = i >= req ? ps->named_optional.data[i - req] : ps->named_required.data[i];
135                         printf("if ($_k eq '%s') {\n", SvPV_nolen(param) + 1);
136                         printf("      %s = $_[$_i + 1];\n", SvPV_nolen(param));
137                         printf("      vec($_b, %zu, 1) = 1;\n", i);
138                         printf("    }\n");
139                 }
140                 printf("    else {\n");
141                 if (ps->slurpy) {
142                         const char *slurpy = SvPV_nolen(ps->slurpy);
143                         if (slurpy[0] == '%') {
144                                 printf("      $%s{$_k} = $_[$_i + 1];\n", slurpy + 1);
145                         } else {
146                                 printf("      push %s, $_k, $_[$_i + 1];\n", slurpy);
147                         }
148                 } else {
149                         printf("      croak 'no such param ' . $_k;\n");
150                 }
151                 printf("    }\n");
152                 printf("    $_i += 2;\n");
153                 printf("  }\n");
154                 if (ps->named_required.used) {
155                         printf("  if (($_b & pack('b*', '1' x %zu)) ne pack('b*', '1' x %zu)) {\n", ps->named_required.used, ps->named_required.used);
156                         printf("    croak 'missing required named args';\n"); // XXX
157                         printf("  }\n");
158                 }
159                 for (size_t i = 0; i < ps->named_optional.used; i++) {
160                         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);
161                 }
162                 printf("}\n");
163         }
164 }