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