File Coverage

lib/Syntax/Operator/In.xs
Criterion Covered Total %
statement 42 61 68.8
branch 14 58 24.1
condition n/a
subroutine n/a
pod n/a
total 56 119 47.0


line stmt bran cond sub pod time code
1             /* You may distribute under the terms of either the GNU General Public License
2             * or the Artistic License (the same terms as Perl itself)
3             *
4             * (C) Paul Evans, 2021 -- leonerd@leonerd.org.uk
5             */
6             #include "EXTERN.h"
7             #include "perl.h"
8             #include "XSUB.h"
9              
10             #define HAVE_PERL_VERSION(R, V, S) \
11             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
12              
13             #include "newOP_CUSTOM.c.inc"
14              
15             #include "XSParseInfix.h"
16              
17             enum Inop_Operator {
18             INOP_NUMBER,
19             INOP_STRING,
20             };
21              
22 4           static OP *pp_in(pTHX)
23             {
24 4           dSP;
25 8           dMARK;
26             SV **svp;
27 4           enum Inop_Operator type = PL_op->op_private;
28              
29             OP cmpop;
30 4           switch(type) {
31             case INOP_NUMBER:
32 2           cmpop.op_type = OP_EQ;
33 2           cmpop.op_flags = 0;
34 2           cmpop.op_ppaddr = PL_ppaddr[OP_EQ];
35 2           break;
36              
37             case INOP_STRING:
38 2           cmpop.op_type = OP_SEQ;
39 2           cmpop.op_flags = 0;
40 2           cmpop.op_ppaddr = PL_ppaddr[OP_SEQ];
41 2           break;
42             }
43              
44 4           SV *lhs = *MARK;
45             SV **listend = SP;
46              
47 4           SP = MARK - 1;
48 4           PUTBACK;
49              
50 4           ENTER;
51 4           SAVEVPTR(PL_op);
52 4           PL_op = &cmpop;
53 4 50         EXTEND(SP, 2);
54              
55 18 100         for(svp = MARK + 1; svp <= listend; svp++) {
56 16           SV *rhs = *svp;
57              
58 16           PUSHs(lhs);
59 16           PUSHs(rhs);
60 16           PUTBACK;
61              
62 16           (*cmpop.op_ppaddr)(aTHX);
63              
64 16           SPAGAIN;
65              
66 16           SV *ret = POPs;
67              
68 16 50         if(SvTRUE(ret)) {
    50          
    50          
    0          
    0          
    50          
    50          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
69 2           LEAVE;
70              
71 2           PUSHs(&PL_sv_yes);
72 2           RETURN;
73             }
74             }
75              
76 2           LEAVE;
77              
78 2           PUSHs(&PL_sv_no);
79 2           RETURN;
80             }
81              
82             #ifndef isIDCONT_utf8_safe
83             /* It doesn't really matter that this is not "safe", because the function is
84             * only ever called on perls new enough to have PL_infix_plugin, and in that
85             * case they'll have the _safe version anyway
86             */
87             # define isIDCONT_utf8_safe(s, e) isIDCONT_utf8(s)
88             #endif
89              
90 0           static void parse_in(pTHX_ U32 flags, ANY *parsedata, void *hookdata)
91             {
92 0 0         if(lex_peek_unichar(0) != '<')
93 0           croak("Expected '<'");
94 0           lex_read_unichar(0);
95              
96 0           lex_read_space(0);
97              
98             /* TODO: Need to parse this via XS::Parse::Infix ourselves so we can pick up
99             * other custom infix equality operators like `equ`
100             */
101 0 0         if(strnEQ(PL_parser->bufptr, "==", 2)) {
102 0           parsedata->any_iv = INOP_NUMBER;
103 0           lex_read_to(PL_parser->bufptr + 2);
104             }
105 0 0         else if(strnEQ(PL_parser->bufptr, "eq", 2) && !isIDCONT_utf8_safe(PL_parser->bufptr + 2, PL_parser->bufend)) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
106 0           parsedata->any_iv = INOP_STRING;
107 0           lex_read_to(PL_parser->bufptr + 2);
108             }
109             else
110 0           croak("Expected an equality test operator name");
111              
112 0 0         if(lex_peek_unichar(0) != '>')
113 0           croak("Expected '>'");
114 0           lex_read_unichar(0);
115 0           }
116              
117 0           static OP *newop_in(pTHX_ U32 flags, OP *lhs, OP *rhs, ANY *parsedata, void *hookdata)
118             {
119             OP *ret = newBINOP_CUSTOM(&pp_in, 0, lhs, rhs);
120 0           ret->op_private = parsedata->any_iv;
121              
122 0           return ret;
123             }
124              
125 6           static OP *newop_in_str(pTHX_ U32 flags, OP *lhs, OP *rhs, ANY *parsedata, void *hookdata)
126             {
127             OP *ret = newBINOP_CUSTOM(&pp_in, 0, lhs, rhs);
128 6           ret->op_private = INOP_STRING;
129              
130 6           return ret;
131             }
132              
133 6           static OP *newop_in_num(pTHX_ U32 flags, OP *lhs, OP *rhs, ANY *parsedata, void *hookdata)
134             {
135             OP *ret = newBINOP_CUSTOM(&pp_in, 0, lhs, rhs);
136 6           ret->op_private = INOP_NUMBER;
137              
138 6           return ret;
139             }
140              
141             struct XSParseInfixHooks infix_in = {
142             .cls = XPI_CLS_MATCH_MISC,
143             .rhs_flags = XPI_OPERAND_LIST,
144             .permit_hintkey = "Syntax::Operator::In/in",
145              
146             .parse = &parse_in,
147             .new_op = &newop_in,
148             };
149              
150             struct XSParseInfixHooks infix_elem_str = {
151             .cls = XPI_CLS_MATCH_MISC,
152             .rhs_flags = XPI_OPERAND_LIST,
153             .permit_hintkey = "Syntax::Operator::Elem/elem",
154              
155             .wrapper_func_name = "Syntax::Operator::Elem::elem_str",
156              
157             .new_op = &newop_in_str,
158             };
159              
160             struct XSParseInfixHooks infix_elem_num = {
161             .cls = XPI_CLS_MATCH_MISC,
162             .rhs_flags = XPI_OPERAND_LIST,
163             .permit_hintkey = "Syntax::Operator::Elem/elem",
164              
165             .wrapper_func_name = "Syntax::Operator::Elem::elem_num",
166              
167             .new_op = &newop_in_num,
168             };
169              
170             MODULE = Syntax::Operator::In PACKAGE = Syntax::Operator::In
171              
172             BOOT:
173 6           boot_xs_parse_infix(0.26);
174              
175             register_xs_parse_infix("in", &infix_in, NULL);
176              
177             register_xs_parse_infix("elem", &infix_elem_str, NULL);
178             register_xs_parse_infix("∈", &infix_elem_num, NULL);