File Coverage

lib/Syntax/Operator/In.xs
Criterion Covered Total %
statement 42 82 51.2
branch 14 58 24.1
condition n/a
subroutine n/a
pod n/a
total 56 140 40.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 "perl-backcompat.c.inc"
14              
15             #include "newOP_CUSTOM.c.inc"
16             #include "BINOP_ANY.c.inc"
17              
18             #include "XSParseInfix.h"
19              
20             enum Inop_Operator {
21             INOP_CUSTOM,
22             INOP_NUMBER,
23             INOP_STRING,
24             };
25              
26 4           static OP *pp_in(pTHX)
27             {
28 4           dSP;
29 8           dMARK;
30             SV **svp;
31 4           enum Inop_Operator type = PL_op->op_private;
32              
33             OP cmpop;
34 4           switch(type) {
35             case INOP_CUSTOM:
36             {
37 0           ANY *op_any = cBINOP_ANY->op_any;
38 0           cmpop.op_type = OP_CUSTOM;
39 0           cmpop.op_flags = 0;
40 0           cmpop.op_ppaddr = op_any[0].any_ptr;
41 0           break;
42             }
43              
44             case INOP_NUMBER:
45 2           cmpop.op_type = OP_EQ;
46 2           cmpop.op_flags = 0;
47 2           cmpop.op_ppaddr = PL_ppaddr[OP_EQ];
48 2           break;
49              
50             case INOP_STRING:
51 2           cmpop.op_type = OP_SEQ;
52 2           cmpop.op_flags = 0;
53 2           cmpop.op_ppaddr = PL_ppaddr[OP_SEQ];
54 2           break;
55             }
56              
57 4           SV *lhs = *MARK;
58             SV **listend = SP;
59              
60 4           SP = MARK - 1;
61 4           PUTBACK;
62              
63 4           ENTER;
64 4           SAVEVPTR(PL_op);
65 4           PL_op = &cmpop;
66 4 50         EXTEND(SP, 2);
67              
68 18 100         for(svp = MARK + 1; svp <= listend; svp++) {
69 16           SV *rhs = *svp;
70              
71 16           PUSHs(lhs);
72 16           PUSHs(rhs);
73 16           PUTBACK;
74              
75 16           (*cmpop.op_ppaddr)(aTHX);
76              
77 16           SPAGAIN;
78              
79 16           SV *ret = POPs;
80              
81 16 50         if(SvTRUE(ret)) {
    50          
    50          
    0          
    0          
    50          
    50          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
82 2           LEAVE;
83              
84 2           PUSHs(&PL_sv_yes);
85 2           RETURN;
86             }
87             }
88              
89 2           LEAVE;
90              
91 2           PUSHs(&PL_sv_no);
92 2           RETURN;
93             }
94              
95             #ifndef isIDCONT_utf8_safe
96             /* It doesn't really matter that this is not "safe", because the function is
97             * only ever called on perls new enough to have PL_infix_plugin, and in that
98             * case they'll have the _safe version anyway
99             */
100             # define isIDCONT_utf8_safe(s, e) isIDCONT_utf8(s)
101             #endif
102              
103 0           static void parse_in(pTHX_ U32 flags, SV **parsedata, void *hookdata)
104             {
105             bool using_circumfix = false;
106 0 0         if(lex_peek_unichar(0) == '<')
107             using_circumfix = true;
108 0 0         else if(lex_peek_unichar(0) != ':')
109 0           croak("Expected ':' or '<'");
110 0           lex_read_unichar(0);
111              
112 0           lex_read_space(0);
113              
114             struct XSParseInfixInfo *info;
115 0 0         if(!parse_infix(XPI_SELECT_EQUALITY, &info))
116 0           croak("Expected an equality test operator");
117              
118             /* parsedata will be an AV containing
119             * [0] IV = enum Inop_Operator
120             * [1] UV = PTR to pp_addr if CUSTOM
121             */
122 0           AV *parsedata_av = newAV();
123 0           *parsedata = newRV_noinc((SV *)parsedata_av);
124              
125             /* See if we got one of the core ones */
126 0 0         if(info->opcode == OP_EQ) {
127 0           av_push(parsedata_av, newSViv(INOP_NUMBER));
128             }
129 0 0         else if(info->opcode == OP_SEQ) {
130 0           av_push(parsedata_av, newSViv(INOP_STRING));
131             }
132 0 0         else if(info->opcode == OP_CUSTOM) {
133 0 0         if(info->hooks->new_op)
134 0           croak("TODO: handle custom op using the new_op function for '%s'", info->opname);
135              
136 0           av_push(parsedata_av, newSViv(INOP_CUSTOM));
137 0           av_push(parsedata_av, newSVuv(PTR2UV(info->hooks->ppaddr)));
138             }
139             else
140 0           croak("Expected an equality test operator name but found '%s'", info->opname);
141              
142 0 0         if(using_circumfix) {
143 0 0         if(lex_peek_unichar(0) != '>')
144 0           croak("Expected '>'");
145 0           lex_read_unichar(0);
146             }
147 0           }
148              
149 0           static OP *newop_in(pTHX_ U32 flags, OP *lhs, OP *rhs, SV **parsedata, void *hookdata)
150             {
151 0           AV *parsedata_av = AV_FROM_REF(*parsedata);
152              
153 0 0         enum Inop_Operator operator = SvIV(AvARRAY(parsedata_av)[0]);
154              
155             OP *ret;
156 0           switch(operator) {
157             case INOP_CUSTOM:
158             ret = newBINOP_ANY_CUSTOM(&pp_in, 0, lhs, rhs, 1);
159 0 0         cBINOP_ANYx(ret)->op_any[0].any_ptr = INT2PTR(void *, SvUV(AvARRAY(parsedata_av)[1]));
160 0           ret->op_private = INOP_CUSTOM;
161 0           break;
162              
163             case INOP_NUMBER:
164             case INOP_STRING:
165             ret = newBINOP_CUSTOM(&pp_in, 0, lhs, rhs);
166 0           ret->op_private = operator;
167 0           break;
168             }
169              
170 0           return ret;
171             }
172              
173 7           static OP *newop_in_str(pTHX_ U32 flags, OP *lhs, OP *rhs, SV **parsedata, void *hookdata)
174             {
175             OP *ret = newBINOP_CUSTOM(&pp_in, 0, lhs, rhs);
176 7           ret->op_private = INOP_STRING;
177              
178 7           return ret;
179             }
180              
181 7           static OP *newop_in_num(pTHX_ U32 flags, OP *lhs, OP *rhs, SV **parsedata, void *hookdata)
182             {
183             OP *ret = newBINOP_CUSTOM(&pp_in, 0, lhs, rhs);
184 7           ret->op_private = INOP_NUMBER;
185              
186 7           return ret;
187             }
188              
189             struct XSParseInfixHooks infix_in = {
190             .cls = XPI_CLS_MATCH_MISC,
191             .rhs_flags = XPI_OPERAND_LIST,
192             .permit_hintkey = "Syntax::Operator::In/in",
193              
194             .parse = &parse_in,
195             .new_op = &newop_in,
196             };
197              
198             struct XSParseInfixHooks infix_elem_str = {
199             .cls = XPI_CLS_MATCH_MISC,
200             .rhs_flags = XPI_OPERAND_LIST,
201             .permit_hintkey = "Syntax::Operator::Elem/elem",
202              
203             .wrapper_func_name = "Syntax::Operator::Elem::elem_str",
204              
205             .new_op = &newop_in_str,
206             };
207              
208             struct XSParseInfixHooks infix_elem_num = {
209             .cls = XPI_CLS_MATCH_MISC,
210             .rhs_flags = XPI_OPERAND_LIST,
211             .permit_hintkey = "Syntax::Operator::Elem/elem",
212              
213             .wrapper_func_name = "Syntax::Operator::Elem::elem_num",
214              
215             .new_op = &newop_in_num,
216             };
217              
218             MODULE = Syntax::Operator::In PACKAGE = Syntax::Operator::In
219              
220             BOOT:
221 7           boot_xs_parse_infix(0.27);
222              
223             register_xs_parse_infix("in", &infix_in, NULL);
224              
225             register_xs_parse_infix("elem", &infix_elem_str, NULL);
226             register_xs_parse_infix("∈", &infix_elem_num, NULL);