Commit | Line | Data |
e158cf8f |
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 | } |