first cut at named parameters
[p5sagit/Function-Parameters.git] / herp.c
CommitLineData
e158cf8f 1
2#define DEFSTRUCT(T) typedef struct T T; struct T
3
4DEFSTRUCT(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
14DEFVECTOR(ParamInitVec, ParamInit);
15DEFVECTOR(ParamVec, SV *);
16
17DEFSTRUCT(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
36static 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
45DEFVECTOR_CLEAR(ParamVec, pv_clear, SvREFCNT_dec);
46DEFVECTOR_CLEAR(ParamInitVec, piv_clear, pi_clear);
47
48static 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
64static 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
74static 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
87static size_t count_positional_params(const ParamSpec *ps) {
88 return ps->positional_required.used + ps->positional_optional.used;
89}
90
91static size_t count_named_params(const ParamSpec *ps) {
92 return ps->named_required.used + ps->named_optional.used;
93}
94
95static 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}