File Coverage

lib/Syntax/Operator/Identical.xs
Criterion Covered Total %
statement 32 32 100.0
branch 23 28 82.1
condition n/a
subroutine n/a
pod n/a
total 55 60 91.6


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, 2022 -- leonerd@leonerd.org.uk
5             */
6             #define PERL_NO_GET_CONTEXT
7              
8             #include "EXTERN.h"
9             #include "perl.h"
10             #include "XSUB.h"
11              
12             #include "XSParseInfix.h"
13              
14             #define HAVE_PERL_VERSION(R, V, S) \
15             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
16              
17             #if HAVE_PERL_VERSION(5, 36, 0)
18             # define HAVE_SV_BOOL
19             #endif
20              
21             #include "sv_numeq.c.inc"
22             #include "sv_streq.c.inc"
23              
24             /* Any defined SV has atleast one of these flags */
25             #define SV_FLAGMASK_DEFINED (SVf_POK|SVf_IOK|SVf_NOK|SVf_ROK)
26              
27             #define sv_identical(lhs, rhs) S_sv_identical(aTHX_ lhs, rhs)
28 15           static bool S_sv_identical(pTHX_ SV *lhs, SV *rhs)
29             {
30 15 50         SvGETMAGIC(lhs);
31 15 50         SvGETMAGIC(rhs);
32              
33 15           U32 lflags = SvFLAGS(lhs);
34 15           U32 rflags = SvFLAGS(rhs);
35              
36 15           U32 anyflags = lflags | rflags;
37 15           U32 allflags = lflags & rflags;
38              
39 15 100         if(!(anyflags & SV_FLAGMASK_DEFINED))
40             /* both are undef */
41             return TRUE;
42 14 50         if(!(lflags & SV_FLAGMASK_DEFINED) || !(rflags & SV_FLAGMASK_DEFINED))
    100          
43             /* atleast one is not defined */
44             return FALSE;
45              
46             #ifdef HAVE_SV_BOOL
47             /* Boolean SVs have all of these flags */
48             # define SV_FLAGS_BOOL (SVf_POK|SVf_IOK|SVf_IsCOW|SVppv_STATIC)
49              
50             if((anyflags & SV_FLAGS_BOOL) == SV_FLAGS_BOOL) {
51             /* at least one SV is likely a boolean. the test doesn't have to be
52             * perfect because we're about to check properly anyway */
53             bool lbool = SvIsBOOL(lhs);
54             bool rbool = SvIsBOOL(rhs);
55              
56             if(lbool && rbool) {
57             /* both are definitely bools */
58             if(SvTRUE(lhs) ^ SvTRUE(rhs))
59             return FALSE;
60             else
61             return TRUE;
62             }
63              
64             if(lbool || rbool)
65             /* one was a bool, one was not */
66             return FALSE;
67              
68             /* neither was in fact a bool; no worries just fallthrough */
69             }
70             #endif
71              
72 11 100         if(anyflags & SVf_ROK) {
73             /* at least one SV is a reference */
74 3 100         if(!(allflags & SVf_ROK))
75             /* ... but not both */
76             return FALSE;
77              
78 2 100         if(SvRV(lhs) == SvRV(rhs))
79             return TRUE;
80             else
81 1           return FALSE;
82             }
83              
84             /* By now we know that both SVs are defined, non-boolean, non-references.
85             * This means that between them the must have atleast one of the following
86             * *private* flags. */
87             assert(anyflags & (SVp_IOK|SVp_NOK|SVp_POK));
88              
89 8 100         if(anyflags & (SVp_IOK|SVp_NOK))
90 6 100         if(!sv_numeq_flags(lhs, rhs, 0))
91             return FALSE;
92              
93 7 100         if(anyflags & (SVp_POK))
94 6 100         if(!sv_streq_flags(lhs, rhs, 0))
95             return FALSE;
96              
97             /* If neither of the above rejected then we're happy to be true */
98             return TRUE;
99             }
100              
101 7           static OP *pp_identical(pTHX)
102             {
103 7           dSP;
104             dTARG;
105 7           SV *lhs = TOPs, *rhs = TOPm1s;
106              
107 7           bool ret = sv_identical(lhs, rhs);
108              
109 7           POPs;
110 7 50         SETs(boolSV(ret));
111 7           RETURN;
112             }
113              
114 8           static OP *pp_notidentical(pTHX)
115             {
116 8           dSP;
117             dTARG;
118 8           SV *lhs = TOPs, *rhs = TOPm1s;
119              
120 8           bool ret = !sv_identical(lhs, rhs);
121              
122 8           POPs;
123 8 50         SETs(boolSV(ret));
124 8           RETURN;
125             }
126              
127             static const struct XSParseInfixHooks hooks_identical = {
128             .cls = XPI_CLS_EQUALITY,
129             .wrapper_func_name = "Syntax::Operator::Identical::is_identical",
130             .permit_hintkey = "Syntax::Operator::Identical/identical",
131             .ppaddr = &pp_identical,
132             };
133              
134             static const struct XSParseInfixHooks hooks_notidentical = {
135             .cls = XPI_CLS_RELATION,
136             .wrapper_func_name = "Syntax::Operator::Identical::is_not_identical",
137             .permit_hintkey = "Syntax::Operator::Identical/identical",
138             .ppaddr = &pp_notidentical,
139             };
140              
141             MODULE = Syntax::Operator::Identical PACKAGE = Syntax::Operator::Identical
142              
143             BOOT:
144 4           boot_xs_parse_infix(0.26);
145              
146             register_xs_parse_infix("≡", &hooks_identical, NULL);
147             register_xs_parse_infix("=:=", &hooks_identical, NULL);
148              
149             register_xs_parse_infix("≢", &hooks_notidentical, NULL);
150             register_xs_parse_infix("!:=", &hooks_notidentical, NULL);
151              
152             /* TODO: Consider adding some sort of rpeep integration into XPI so we can
153             * optimise not(identical) into notidentical or vice-versa
154             */