File Coverage

Declare.xs
Criterion Covered Total %
statement 444 482 92.1
branch 216 360 60.0
condition n/a
subroutine n/a
pod n/a
total 660 842 78.3


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "callparser1.h"
4             #include "XSUB.h"
5             #include "object_types.h"
6              
7             #ifndef XS_INTERNAL
8             #define XS_INTERNAL(name) static XSPROTO(name)
9             #endif
10              
11 168           static int has_attr(pTHX_ SV *attr_sv, const char *name) {
12             const char *p, *end;
13             STRLEN alen, nlen;
14 168 100         if (!SvOK(attr_sv)) return 0;
15 106           p = SvPV(attr_sv, alen);
16 106           end = p + alen;
17 106           nlen = strlen(name);
18 202 100         while (p < end) {
19 133           const char *comma = (const char *)memchr(p, ',', end - p);
20 133 100         STRLEN seg = comma ? (STRLEN)(comma - p) : (STRLEN)(end - p);
21 133 100         if (seg == nlen && memEQ(p, name, nlen)) return 1;
    50          
22 96           p += seg + 1;
23             }
24 69           return 0;
25             }
26              
27             typedef struct {
28             HV *val2name; /* value -> name lookup (for check) */
29             HV *lc_name2val; /* lc(name) -> value lookup (for coerce) */
30             IV flags_mask; /* combined bitmask for :Flags enums, -1 if not flags */
31             } EnumTypeData;
32              
33 24           static bool enum_type_check(pTHX_ SV *val, void *data_ptr) {
34 24           EnumTypeData *etd = (EnumTypeData *)data_ptr;
35             const char *pv;
36             STRLEN len;
37 24 50         if (!SvOK(val)) return false;
38 24 100         if (etd->flags_mask >= 0) {
39             IV iv;
40 6 50         if (!SvIOK(val) && !(SvPOK(val) && looks_like_number(val)))
    0          
    0          
41 0           return false;
42 6           iv = SvIV(val);
43 6 50         return iv >= 0 && (iv & ~etd->flags_mask) == 0;
    100          
44             }
45 18           pv = SvPV(val, len);
46 18           return hv_exists(etd->val2name, pv, len);
47             }
48              
49 24           static SV *enum_type_coerce(pTHX_ SV *val, void *data_ptr) {
50 24           EnumTypeData *etd = (EnumTypeData *)data_ptr;
51             const char *pv;
52             STRLEN len;
53             SV **found;
54             char *lc;
55             STRLEN i;
56             SV *lc_sv;
57 24 50         if (!SvOK(val)) return val;
58 24           pv = SvPV(val, len);
59 24 100         if (hv_exists(etd->val2name, pv, len))
60 10           return val;
61 14           lc_sv = newSVpvn(pv, len);
62 14           lc = SvPVX(lc_sv);
63 52 100         for (i = 0; i < len; i++) lc[i] = toLOWER(lc[i]);
    100          
64 14           found = hv_fetch(etd->lc_name2val, lc, len, 0);
65 14           SvREFCNT_dec(lc_sv);
66 14 100         if (found) return sv_mortalcopy(*found);
67 9           return val;
68             }
69              
70 219           static SV *lex_read_ident(pTHX) {
71 219           SV *buf = newSVpvs("");
72             I32 c;
73             while (1) {
74 1288           c = lex_peek_unichar(0);
75 1288 50         if (c == -1) break;
76 1288 50         if (!isALNUM(c) && c != '_') break;
    100          
    50          
77 1069           sv_catpvf(buf, "%c", (int)c);
78 1069           lex_read_unichar(0);
79             }
80 219 50         if (SvCUR(buf) == 0) {
81 0           SvREFCNT_dec(buf);
82 0           return NULL;
83             }
84 219           return buf;
85             }
86              
87 47           static SV *lex_read_attrs(pTHX) {
88 47           SV *attr_sv = &PL_sv_undef;
89 47           I32 c = lex_peek_unichar(0);
90 94 100         while (c == ':') {
91             SV *a;
92 47           lex_read_unichar(0);
93 47           lex_read_space(0);
94 47           a = lex_read_ident(aTHX);
95 47 50         if (!a) croak("Expected attribute name after ':'");
96 47 100         if (attr_sv == &PL_sv_undef) {
97 35           attr_sv = a;
98             } else {
99 12           sv_catpvs(attr_sv, ",");
100 12           sv_catsv(attr_sv, a);
101 12           SvREFCNT_dec(a);
102             }
103 47           lex_read_space(0);
104 47           c = lex_peek_unichar(0);
105             }
106 47           return attr_sv;
107             }
108              
109 11           static SV *lex_read_quoted_string(pTHX) {
110 11           I32 quote = lex_peek_unichar(0);
111             SV *sv;
112             I32 c;
113 11           lex_read_unichar(0);
114 11           sv = newSVpvs("");
115             while (1) {
116 64           c = lex_read_unichar(0);
117 64 50         if (c == -1) croak("Unterminated string in enum declaration");
118 64 50         if (c == '\\' && quote == '"') {
    0          
119 0           I32 next = lex_read_unichar(0);
120 0 0         if (next == -1) croak("Unterminated string in enum declaration");
121 0           sv_catpvf(sv, "%c", (int)next);
122 64 100         } else if (c == quote) {
123 11           break;
124             } else {
125 53           sv_catpvf(sv, "%c", (int)c);
126             }
127             }
128 11           return sv;
129             }
130              
131 14           static SV *lex_read_integer(pTHX) {
132 14           int is_neg = 0;
133             SV *buf;
134             IV ival;
135 14           I32 c = lex_peek_unichar(0);
136 14 100         if (c == '-') {
137 1           is_neg = 1;
138 1           lex_read_unichar(0);
139 1           lex_read_space(0);
140 1           c = lex_peek_unichar(0);
141             }
142 14 50         if (!isDIGIT(c))
143 0           croak("Expected integer or string value after '=' in enum declaration");
144 14           buf = newSVpvs("");
145             while (1) {
146 49           c = lex_peek_unichar(0);
147 49 50         if (c == -1 || !isDIGIT(c)) break;
    100          
148 35           sv_catpvf(buf, "%c", (int)c);
149 35           lex_read_unichar(0);
150             }
151 14           ival = SvIV(buf);
152 14           SvREFCNT_dec(buf);
153 14 100         if (is_neg) ival = -ival;
154 14           return newSViv(ival);
155             }
156              
157 37           static AV *lex_read_variants(pTHX) {
158 37           AV *av = newAV();
159             I32 c;
160 37           lex_read_space(0);
161 37           c = lex_peek_unichar(0);
162 37 50         if (c != '{') croak("Expected '{' after enum name");
163 37           lex_read_unichar(0);
164 120           while (1) {
165             SV *vname;
166 157           lex_read_space(0);
167 157           c = lex_peek_unichar(0);
168 157 100         if (c == '}') { lex_read_unichar(0); break; }
169 120 50         if (c == -1) croak("Unexpected end of input in enum declaration");
170 120           vname = lex_read_ident(aTHX);
171 120 50         if (!vname) croak("Expected variant name in enum declaration");
172 120           av_push(av, vname);
173 120           lex_read_space(0);
174 120           c = lex_peek_unichar(0);
175 120 100         if (c == '=') {
176 25           lex_read_unichar(0);
177 25           lex_read_space(0);
178 25           c = lex_peek_unichar(0);
179 25 100         if (c == '"' || c == '\'') {
    100          
180 11           av_push(av, lex_read_quoted_string(aTHX));
181             } else {
182 14           av_push(av, lex_read_integer(aTHX));
183             }
184             } else {
185 95           av_push(av, &PL_sv_undef);
186             }
187 120           lex_read_space(0);
188 120 100         if (lex_peek_unichar(0) == ',') lex_read_unichar(0);
189             }
190 37           return av;
191             }
192              
193             typedef struct {
194             AV *names;
195             AV *values;
196             HV *name2val;
197             HV *val2name;
198             } EnumData;
199              
200 37           static EnumData build_enum_data(pTHX_ AV *variants, int is_str, int is_flags, const char *pkg, STRLEN pkg_len) {
201             EnumData d;
202 37           IV next_ival = is_flags ? 1 : 0;
203 37           I32 pair_count = av_len(variants) + 1;
204             I32 i;
205 37           d.names = newAV();
206 37           d.values = newAV();
207 37           d.name2val = newHV();
208 37           d.val2name = newHV();
209 157 100         for (i = 0; i < pair_count; i += 2) {
210 120           SV **name_p = av_fetch(variants, i, 0);
211 120           SV **val_p = av_fetch(variants, i + 1, 0);
212 120 50         SV *vname = name_p ? *name_p : &PL_sv_undef;
213 120 50         SV *vval = val_p ? *val_p : &PL_sv_undef;
214             SV *resolved;
215             const char *vname_pv;
216             STRLEN vname_len;
217 120           vname_pv = SvPV(vname, vname_len);
218 120 100         if (SvOK(vval)) {
219 25 100         if (is_str) {
220 11           resolved = newSVsv(vval);
221             } else {
222 14           next_ival = SvIV(vval);
223 14           resolved = newSViv(next_ival);
224             }
225 95 100         } else if (is_str) {
226             char *lc;
227             STRLEN j;
228 24           resolved = newSVpvn(vname_pv, vname_len);
229 24           lc = SvPVX(resolved);
230 142 100         for (j = 0; j < vname_len; j++) lc[j] = toLOWER(lc[j]);
    100          
231             } else {
232 71           resolved = newSViv(next_ival);
233             }
234 120           av_push(d.names, newSVpvn(vname_pv, vname_len));
235 120           av_push(d.values, newSVsv(resolved));
236 120           hv_store(d.name2val, vname_pv, vname_len, newSVsv(resolved), 0);
237             {
238             const char *val_pv;
239             STRLEN val_len;
240 120           SV *val_key = newSVsv(resolved);
241 120           val_pv = SvPV(val_key, val_len);
242 120           hv_store(d.val2name, val_pv, val_len,
243             newSVpvn(vname_pv, vname_len), 0);
244 120           SvREFCNT_dec(val_key);
245             }
246             {
247 120           SV *val_copy = newSVsv(resolved);
248 120           newCONSTSUB(gv_stashpvn(pkg, pkg_len, GV_ADD),
249             vname_pv, val_copy);
250             }
251 120 100         if (!is_str)
252 85 100         next_ival = is_flags ? next_ival << 1 : next_ival + 1;
253 120           SvREFCNT_dec(resolved);
254             }
255 37           return d;
256             }
257              
258 8           static void merge_enum_exports(pTHX_ const char *pkg) {
259 8           SV *buf = sv_newmortal();
260             AV *pending, *exp, *exp_ok;
261             I32 j;
262              
263 8           sv_setpvf(buf, "%s::_ENUM_EXPORTS", pkg);
264 8           pending = get_av(SvPV_nolen(buf), 0);
265 8 50         if (!pending || av_len(pending) < 0) return;
    100          
266              
267 4           sv_setpvf(buf, "%s::EXPORT", pkg);
268 4           exp = get_av(SvPV_nolen(buf), GV_ADD);
269              
270 4           sv_setpvf(buf, "%s::EXPORT_OK", pkg);
271 4           exp_ok = get_av(SvPV_nolen(buf), GV_ADD);
272              
273 25 100         for (j = 0; j <= av_len(pending); j++) {
274 21           SV **n = av_fetch(pending, j, 0);
275 21 50         if (n) {
276 21           av_push(exp, newSVsv(*n));
277 21           av_push(exp_ok, newSVsv(*n));
278             }
279             }
280 4           av_clear(pending);
281             }
282              
283 8           XS_INTERNAL(xs_enum_pkg_import) {
284 8           dXSARGS;
285             const char *pkg;
286             CV *exp_import;
287             I32 j;
288              
289 8 50         if (items < 1) croak("Usage: PKG->import(...)");
290 8           pkg = SvPV_nolen(ST(0));
291              
292 8           merge_enum_exports(aTHX_ pkg);
293              
294 8           exp_import = get_cv("Exporter::import", 0);
295 8 50         if (!exp_import) {
296 0           load_module(PERL_LOADMOD_NOIMPORT, newSVpvs("Exporter"), NULL);
297 0           exp_import = get_cv("Exporter::import", 0);
298             }
299 8 50         if (!exp_import) croak("Cannot find Exporter::import");
300              
301             {
302 8           dSP;
303 8           ENTER;
304 8           SAVETMPS;
305 8 50         PUSHMARK(SP);
306 21 100         for (j = 0; j < items; j++) {
307 13 50         XPUSHs(ST(j));
308             }
309 8           PUTBACK;
310 8           call_sv((SV*)exp_import, G_VOID | G_DISCARD);
311 8 50         FREETMPS;
312 8           LEAVE;
313             }
314              
315 8           XSRETURN_EMPTY;
316             }
317              
318 5           static void setup_exports(pTHX_ const char *pkg, STRLEN pkg_len, AV *names, SV *enum_name) {
319 5           SV *buf = sv_newmortal();
320             AV *pending;
321             AV *tag_list;
322             HV *export_tags;
323             I32 j;
324 5           sv_setpvf(buf, "%s::_ENUM_EXPORTS", pkg);
325 5           pending = get_av(SvPV_nolen(buf), GV_ADD);
326 21 100         for (j = 0; j <= av_len(names); j++) {
327 16           SV **n = av_fetch(names, j, 0);
328 16 50         if (n) av_push(pending, newSVsv(*n));
329             }
330 5 50         if (enum_name) av_push(pending, newSVsv(enum_name));
331 5 50         if (enum_name) {
332 5           sv_setpvf(buf, "%s::EXPORT_TAGS", pkg);
333 5           export_tags = get_hv(SvPV_nolen(buf), GV_ADD);
334              
335 5           tag_list = newAV();
336 21 100         for (j = 0; j <= av_len(names); j++) {
337 16           SV **n = av_fetch(names, j, 0);
338 16 50         if (n) av_push(tag_list, newSVsv(*n));
339             }
340 5           av_push(tag_list, newSVsv(enum_name));
341 5           (void)hv_store_ent(export_tags, enum_name, newRV_noinc((SV*)tag_list), 0);
342             }
343 5           sv_setpvf(buf, "%s::import", pkg);
344 5 100         if (!get_cv(SvPV_nolen(buf), 0)) {
345 4           newXS(SvPV_nolen(buf), xs_enum_pkg_import, __FILE__);
346             }
347 5           }
348              
349 37           static void install_meta(pTHX_ SV *name_sv, const char *pkg, STRLEN pkg_len, EnumData *d, int is_flags, int is_type) {
350 37           dSP;
351             SV *meta;
352 37           const char *ename = SvPV_nolen(name_sv);
353 37           EnumTypeData *etd = NULL;
354 37 100         if (is_type) {
355             HE *he;
356             I32 j;
357 9           Newxz(etd, 1, EnumTypeData);
358 9           etd->val2name = newHV();
359 9           etd->lc_name2val = newHV();
360 9           hv_iterinit(d->val2name);
361 36 100         while ((he = hv_iternext(d->val2name))) {
362             STRLEN klen;
363 27 50         const char *key = HePV(he, klen);
364 27           SV *val = HeVAL(he);
365 27           hv_store(etd->val2name, key, klen, newSVsv(val), 0);
366             }
367 9           hv_iterinit(d->name2val);
368 36 100         while ((he = hv_iternext(d->name2val))) {
369             STRLEN klen;
370 27 50         const char *key = HePV(he, klen);
371 27           SV *val = HeVAL(he);
372             char *lc_key;
373             STRLEN k;
374 27           Newx(lc_key, klen + 1, char);
375 158 100         for (k = 0; k < klen; k++) lc_key[k] = toLOWER(key[k]);
    100          
376 27           lc_key[klen] = '\0';
377 27           hv_store(etd->lc_name2val, lc_key, klen, newSVsv(val), 0);
378 27           Safefree(lc_key);
379             }
380 9 100         if (is_flags) {
381 3           IV mask = 0;
382 12 100         for (j = 0; j <= av_len(d->values); j++) {
383 9           SV **svp = av_fetch(d->values, j, 0);
384 9 50         if (svp) mask |= SvIV(*svp);
385             }
386 3           etd->flags_mask = mask;
387             } else {
388 6           etd->flags_mask = -1;
389             }
390             }
391 37           ENTER;
392 37           SAVETMPS;
393 37 50         PUSHMARK(SP);
394 37 50         mXPUSHs(newSVpvs("Enum::Declare::Meta"));
395 37 50         mXPUSHs(newSVpvs("enum_name")); mXPUSHs(newSVsv(name_sv));
    50          
396 37 50         mXPUSHs(newSVpvs("package")); mXPUSHs(newSVpvn(pkg, pkg_len));
    50          
397 37 50         mXPUSHs(newSVpvs("names")); mXPUSHs(newRV_noinc((SV*)d->names));
    50          
398 37 50         mXPUSHs(newSVpvs("values")); mXPUSHs(newRV_noinc((SV*)d->values));
    50          
399 37 50         mXPUSHs(newSVpvs("name2val")); mXPUSHs(newRV_noinc((SV*)d->name2val));
    50          
400 37 50         mXPUSHs(newSVpvs("val2name")); mXPUSHs(newRV_noinc((SV*)d->val2name));
    50          
401 37           PUTBACK;
402 37           call_method("new", G_SCALAR);
403 37           SPAGAIN;
404 37           meta = SvREFCNT_inc(POPs);
405 37           PUTBACK;
406 37 50         FREETMPS;
407 37           LEAVE;
408             {
409 37           HV *registry = get_hv("Enum::Declare::_registry", GV_ADD);
410 37           SV *key = newSVpvf("%s::%s", pkg, ename);
411 37           hv_store_ent(registry, key, newSVsv(meta), 0);
412 37           SvREFCNT_dec(key);
413             }
414 37           newCONSTSUB(gv_stashpvn(pkg, pkg_len, GV_ADD), ename, meta);
415 37 100         if (is_type && etd) {
    50          
416 9           object_register_type_xs_ex(aTHX_ ename,
417             enum_type_check, enum_type_coerce, (void*)etd);
418             }
419 37           }
420              
421 47           XS_INTERNAL(xs_enum_stub) {
422 47           dXSARGS;
423             PERL_UNUSED_VAR(items);
424 47           XSRETURN_EMPTY;
425             }
426              
427             typedef struct {
428             SV *meta; /* the Enum::Declare::Meta object this set type is bound to */
429             const char *set_name;
430             SV *set_instance; /* the singleton Set used as the type constraint */
431             } EnumSetTypeData;
432              
433 14           static bool enumset_type_check(pTHX_ SV *val, void *data_ptr) {
434 14           EnumSetTypeData *estd = (EnumSetTypeData *)data_ptr;
435             SV *result_sv;
436             bool result;
437 14           dSP;
438 14 100         if (sv_isobject(val))
439 1           return false;
440 13           ENTER; SAVETMPS;
441 13 50         PUSHMARK(SP);
442 13 50         XPUSHs(estd->set_instance);
443 13 50         XPUSHs(val);
444 13           PUTBACK;
445 13           call_method("has", G_SCALAR);
446 13           SPAGAIN;
447 13           result_sv = POPs;
448 13           result = SvTRUE(result_sv);
449 13           PUTBACK;
450 13 50         FREETMPS; LEAVE;
451 13           return result;
452             }
453              
454 14           static SV *enumset_type_coerce(pTHX_ SV *val, void *data_ptr) {
455             PERL_UNUSED_ARG(data_ptr);
456 14           return val;
457             }
458              
459 0           XS_INTERNAL(xs_enumset_constructor) {
460 0           dXSARGS;
461 0           SV *meta_sv = (SV*)XSANY.any_ptr;
462             SV *set;
463             AV *vals_av;
464             I32 i;
465              
466 0           vals_av = newAV();
467 0 0         for (i = 0; i < items; i++) {
468 0           av_push(vals_av, newSVsv(ST(i)));
469             }
470              
471 0           ENTER; SAVETMPS;
472 0 0         PUSHMARK(SP);
473 0 0         mXPUSHs(newSVpvs("Enum::Declare::Set"));
474 0 0         mXPUSHs(newSVpvs("meta")); XPUSHs(meta_sv);
    0          
475 0 0         mXPUSHs(newSVpvs("name")); mXPUSHs(newSVpvn(GvNAME(CvGV(cv)), GvNAMELEN(CvGV(cv))));
    0          
476 0 0         mXPUSHs(newSVpvs("values")); mXPUSHs(newRV_noinc((SV*)vals_av));
    0          
477 0           PUTBACK;
478 0           call_method("new", G_SCALAR);
479 0           SPAGAIN;
480 0           set = SvREFCNT_inc(POPs);
481 0           PUTBACK;
482 0 0         FREETMPS; LEAVE;
483              
484 0           ST(0) = sv_2mortal(set);
485 0           XSRETURN(1);
486             }
487              
488 10           static SV * lookup_enum_meta(pTHX_ const char *pkg, const char *enum_name) {
489 10           HV *registry = get_hv("Enum::Declare::_registry", 0);
490             SV *key;
491             HE *he;
492 10 50         if (!registry) return NULL;
493 10           key = newSVpvf("%s::%s", pkg, enum_name);
494 10           he = hv_fetch_ent(registry, key, 0, 0);
495 10           SvREFCNT_dec(key);
496 10 50         if (!he) return NULL;
497 10           return HeVAL(he);
498             }
499              
500 4           static AV *lex_read_set_members(pTHX) {
501 4           AV *av = newAV();
502             I32 c;
503 4           lex_read_space(0);
504 4           c = lex_peek_unichar(0);
505 4 50         if (c != '{') croak("Expected '{' in enumSet declaration");
506 4           lex_read_unichar(0);
507 5           while (1) {
508             SV *vname;
509 9           lex_read_space(0);
510 9           c = lex_peek_unichar(0);
511 9 100         if (c == '}') { lex_read_unichar(0); break; }
512 5 50         if (c == -1) croak("Unexpected end of input in enumSet declaration");
513 5           vname = lex_read_ident(aTHX);
514 5 50         if (!vname) croak("Expected variant name in enumSet declaration");
515 5           av_push(av, vname);
516 5           lex_read_space(0);
517 5 100         if (lex_peek_unichar(0) == ',') lex_read_unichar(0);
518             }
519 4           return av;
520             }
521              
522 10           static SV *split_enum_from_attrs(pTHX_ SV **attr_sv_ptr) {
523 10           SV *attr_sv = *attr_sv_ptr;
524             SV *enum_name_sv;
525             const char *attr_pv;
526             STRLEN attr_len;
527             char *last_comma;
528              
529 10 50         if (attr_sv == &PL_sv_undef)
530 0           croak("Expected ':EnumName' binding in enumSet declaration");
531              
532 10           attr_pv = SvPV(attr_sv, attr_len);
533 10           last_comma = strrchr(attr_pv, ',');
534              
535 10 100         if (last_comma) {
536 3           enum_name_sv = newSVpv(last_comma + 1, 0);
537 3           SvCUR_set(attr_sv, last_comma - attr_pv);
538 3           *SvEND(attr_sv) = '\0';
539             } else {
540 7           enum_name_sv = newSVsv(attr_sv);
541 7           SvREFCNT_dec(attr_sv);
542 7           *attr_sv_ptr = &PL_sv_undef;
543             }
544 10           return enum_name_sv;
545             }
546              
547 4           static SV *build_frozen_set(pTHX_ AV *member_names, SV *meta, SV *set_name_sv, SV *enum_name_sv) {
548 4           AV *vals_av = newAV();
549             SV *set_obj;
550             I32 i;
551 4           dSP;
552 9 100         for (i = 0; i <= av_len(member_names); i++) {
553 5           SV **name_p = av_fetch(member_names, i, 0);
554 5 50         if (name_p) {
555             SV *val, *val_copy;
556 5           const char *vname = SvPV_nolen(*name_p);
557 5           ENTER; SAVETMPS;
558 5 50         PUSHMARK(SP);
559 5 50         XPUSHs(meta);
560 5 50         mXPUSHs(newSVpv(vname, 0));
561 5           PUTBACK;
562 5           call_method("value", G_SCALAR);
563 5           SPAGAIN;
564 5           val = POPs;
565 5           PUTBACK;
566 5 50         if (!SvOK(val)) {
567 0 0         FREETMPS; LEAVE;
568 0           croak("enumSet %s: '%s' is not a variant of %s",
569             SvPV_nolen(set_name_sv), vname, SvPV_nolen(enum_name_sv));
570             }
571 5           val_copy = newSVsv(val);
572 5 50         FREETMPS; LEAVE;
573 5           av_push(vals_av, val_copy);
574             }
575             }
576 4           ENTER; SAVETMPS;
577 4 50         PUSHMARK(SP);
578 4 50         mXPUSHs(newSVpvs("Enum::Declare::Set"));
579 4 50         mXPUSHs(newSVpvs("meta")); XPUSHs(meta);
    50          
580 4 50         mXPUSHs(newSVpvs("name")); XPUSHs(set_name_sv);
    50          
581 4 50         mXPUSHs(newSVpvs("values")); mXPUSHs(newRV_noinc((SV*)vals_av));
    50          
582 4 50         mXPUSHs(newSVpvs("frozen")); mXPUSHs(newSViv(1));
    50          
583 4           PUTBACK;
584 4           call_method("new", G_SCALAR);
585 4           SPAGAIN;
586 4           set_obj = SvREFCNT_inc(POPs);
587 4           PUTBACK;
588 4 50         FREETMPS; LEAVE;
589 4           return set_obj;
590             }
591              
592 6           static SV *build_mutable_set(pTHX_ SV *meta, SV *set_name_sv) {
593             SV *set_obj;
594 6           dSP;
595 6           ENTER; SAVETMPS;
596 6 50         PUSHMARK(SP);
597 6 50         mXPUSHs(newSVpvs("Enum::Declare::Set"));
598 6 50         mXPUSHs(newSVpvs("meta")); XPUSHs(meta);
    50          
599 6 50         mXPUSHs(newSVpvs("name")); XPUSHs(set_name_sv);
    50          
600 6           PUTBACK;
601 6           call_method("new", G_SCALAR);
602 6           SPAGAIN;
603 6           set_obj = SvREFCNT_inc(POPs);
604 6           PUTBACK;
605 6 50         FREETMPS; LEAVE;
606 6           return set_obj;
607             }
608              
609 3           static void register_enumset_type(pTHX_ const char *pkg, SV *set_name_sv, SV *meta) {
610             EnumSetTypeData *estd;
611             SV *fqn, *singleton;
612             GV *gv;
613 3           dSP;
614 3           Newxz(estd, 1, EnumSetTypeData);
615 3           estd->meta = SvREFCNT_inc(meta);
616 3           estd->set_name = savepv(SvPV_nolen(set_name_sv));
617 3           fqn = newSVpvf("%s::%s", pkg, SvPV_nolen(set_name_sv));
618 3           gv = gv_fetchpvn_flags(SvPV_nolen(fqn), SvCUR(fqn), 0, SVt_PV);
619 3           SvREFCNT_dec(fqn);
620 3 50         if (gv && GvCV(gv)) {
    50          
621 3           ENTER; SAVETMPS;
622 3 50         PUSHMARK(SP);
623 3           PUTBACK;
624 3           call_sv((SV*)GvCV(gv), G_SCALAR);
625 3           SPAGAIN;
626 3           singleton = SvREFCNT_inc(POPs);
627 3           PUTBACK;
628 3 50         FREETMPS; LEAVE;
629             } else {
630 0           croak("enumSet %s: failed to retrieve singleton", SvPV_nolen(set_name_sv));
631             }
632 3           estd->set_instance = singleton;
633 3           object_register_type_xs_ex(aTHX_ SvPV_nolen(set_name_sv), enumset_type_check, enumset_type_coerce, (void*)estd);
634 3           }
635              
636 10           static OP *enumset_parser_callback(pTHX_ GV *namegv, SV *psobj, U32 *flagsp) {
637             SV *set_name_sv;
638             SV *attr_sv;
639             SV *enum_name_sv;
640             SV *meta;
641             const char *pkg;
642             STRLEN pkg_len;
643             int is_export, is_type;
644             PERL_UNUSED_ARG(namegv);
645             PERL_UNUSED_ARG(psobj);
646 10           lex_read_space(0);
647 10           set_name_sv = lex_read_ident(aTHX);
648 10 50         if (!set_name_sv) croak("Expected enumSet name");
649 10           lex_read_space(0);
650 10           attr_sv = lex_read_attrs(aTHX);
651 10           enum_name_sv = split_enum_from_attrs(aTHX_ &attr_sv);
652 10 50         pkg = HvNAME(PL_curstash);
    50          
    50          
    0          
    50          
    50          
653 10           pkg_len = strlen(pkg);
654 10           is_export = has_attr(aTHX_ attr_sv, "Export");
655 10           is_type = has_attr(aTHX_ attr_sv, "Type");
656 10           meta = lookup_enum_meta(aTHX_ pkg, SvPV_nolen(enum_name_sv));
657 10 50         if (!meta)
658 0           croak("enumSet %s: unknown enum '%s' in package %s",
659             SvPV_nolen(set_name_sv), SvPV_nolen(enum_name_sv), pkg);
660 10           lex_read_space(0);
661 10 100         if (lex_peek_unichar(0) == '{') {
662 4           AV *member_names = lex_read_set_members(aTHX);
663 4           SV *set_obj = build_frozen_set(aTHX_ member_names, meta, set_name_sv, enum_name_sv);
664 4           newCONSTSUB(gv_stashpvn(pkg, pkg_len, GV_ADD),
665             SvPV_nolen(set_name_sv), set_obj);
666 4           SvREFCNT_dec((SV*)member_names);
667             } else {
668 6           SV *set_obj = build_mutable_set(aTHX_ meta, set_name_sv);
669 6           newCONSTSUB(gv_stashpvn(pkg, pkg_len, GV_ADD),
670             SvPV_nolen(set_name_sv), set_obj);
671             }
672 10 50         if (is_export) {
673 0           AV *name_list = newAV();
674 0           av_push(name_list, newSVsv(set_name_sv));
675 0           setup_exports(aTHX_ pkg, pkg_len, name_list, NULL);
676 0           SvREFCNT_dec((SV*)name_list);
677             }
678 10 100         if (is_type)
679 3           register_enumset_type(aTHX_ pkg, set_name_sv, meta);
680 10 100         if (attr_sv != &PL_sv_undef) SvREFCNT_dec(attr_sv);
681 10           SvREFCNT_dec(set_name_sv);
682 10           SvREFCNT_dec(enum_name_sv);
683 10           *flagsp |= CALLPARSER_STATEMENT;
684 10           return newNULLLIST();
685             }
686              
687 37           static OP *enum_parser_callback(pTHX_ GV *namegv, SV *psobj, U32 *flagsp) {
688             SV *name_sv;
689             SV *attr_sv;
690             AV *variants;
691             const char *pkg;
692             STRLEN pkg_len;
693             EnumData data;
694             int is_flags;
695             PERL_UNUSED_ARG(namegv);
696             PERL_UNUSED_ARG(psobj);
697 37           lex_read_space(0);
698 37           name_sv = lex_read_ident(aTHX);
699 37 50         if (!name_sv) croak("Expected enum name");
700 37           lex_read_space(0);
701 37           attr_sv = lex_read_attrs(aTHX);
702 37           variants = lex_read_variants(aTHX);
703 37 50         pkg = HvNAME(PL_curstash);
    50          
    50          
    0          
    50          
    50          
704 37           pkg_len = strlen(pkg);
705 37           is_flags = has_attr(aTHX_ attr_sv, "Flags");
706 37           data = build_enum_data(aTHX_ variants,
707             has_attr(aTHX_ attr_sv, "Str"),
708             is_flags,
709             pkg, pkg_len
710             );
711 37 100         if (has_attr(aTHX_ attr_sv, "Export"))
712 5           setup_exports(aTHX_ pkg, pkg_len, data.names, name_sv);
713 37           install_meta(aTHX_ name_sv, pkg, pkg_len, &data, is_flags,
714             has_attr(aTHX_ attr_sv, "Type"));
715 37 100         if (attr_sv != &PL_sv_undef) SvREFCNT_dec(attr_sv);
716 37           SvREFCNT_dec(name_sv);
717 37           SvREFCNT_dec((SV*)variants);
718 37           *flagsp |= CALLPARSER_STATEMENT;
719 37           return newNULLLIST();
720             }
721              
722             MODULE = Enum::Declare PACKAGE = Enum::Declare
723             PROTOTYPES: DISABLE
724              
725             void import(...)
726             CODE:
727             {
728 37 50         const char *caller = CopSTASHPV(PL_curcop);
    50          
    50          
    50          
    0          
    50          
    50          
729 37           SV *fqn = newSVpvf("%s::enum", caller);
730 37           CV *cv = newXS(SvPV_nolen(fqn), xs_enum_stub, __FILE__);
731 37           cv_set_call_parser(cv, enum_parser_callback, &PL_sv_undef);
732 37           SvREFCNT_dec(fqn);
733 37           fqn = newSVpvf("%s::enumSet", caller);
734 37           cv = newXS(SvPV_nolen(fqn), xs_enum_stub, __FILE__);
735 37           cv_set_call_parser(cv, enumset_parser_callback, &PL_sv_undef);
736 37           SvREFCNT_dec(fqn);
737             }