File Coverage

lib/Syntax/Operator/Identical.xs
Criterion Covered Total %
statement 41 41 100.0
branch 27 32 84.3
condition n/a
subroutine n/a
pod n/a
total 68 73 93.1


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-2024 -- 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 38           static bool S_sv_identical(pTHX_ SV *lhs, SV *rhs)
29             {
30 38           SvGETMAGIC(lhs);
31 38           SvGETMAGIC(rhs);
32              
33 38           U32 lflags = SvFLAGS(lhs);
34 38           U32 rflags = SvFLAGS(rhs);
35              
36 38           U32 anyflags = lflags | rflags;
37 38           U32 allflags = lflags & rflags;
38              
39 38 100         if(!(anyflags & SV_FLAGMASK_DEFINED))
40             /* both are undef */
41             return TRUE;
42 35 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 28 100         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 10           bool lbool = SvIsBOOL(lhs);
54 10           bool rbool = SvIsBOOL(rhs);
55              
56 10 100         if(lbool && rbool) {
57             /* both are definitely bools */
58 4 50         if(SvTRUE(lhs) ^ SvTRUE(rhs))
59             return FALSE;
60             else
61             return TRUE;
62             }
63              
64 6 50         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 18 100         if(anyflags & SVf_ROK) {
73             /* at least one SV is a reference */
74 6 100         if(!(allflags & SVf_ROK))
75             /* ... but not both */
76             return FALSE;
77              
78 4 100         if(SvRV(lhs) == SvRV(rhs))
79             return TRUE;
80             else
81             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 12 100         if(anyflags & (SVp_IOK|SVp_NOK))
90 8 100         if(!sv_numeq_flags(lhs, rhs, 0))
91             return FALSE;
92              
93 10 100         if(anyflags & (SVp_POK))
94 8 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 15           static OP *pp_identical(pTHX)
102             {
103 15           dSP;
104             dTARG;
105 15           SV *lhs = TOPs, *rhs = TOPm1s;
106              
107 15           bool ret = sv_identical(lhs, rhs);
108              
109 15           POPs;
110 15 50         SETs(boolSV(ret));
111 15           RETURN;
112             }
113              
114 23           static OP *pp_notidentical(pTHX)
115             {
116 23           dSP;
117             dTARG;
118 23           SV *lhs = TOPs, *rhs = TOPm1s;
119              
120 23           bool ret = !sv_identical(lhs, rhs);
121              
122 23           POPs;
123 23 50         SETs(boolSV(ret));
124 23           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             .ppaddr = &pp_identical,
131             };
132              
133             static const struct XSParseInfixHooks hooks_notidentical = {
134             .cls = XPI_CLS_RELATION,
135             .wrapper_func_name = "Syntax::Operator::Identical::is_not_identical",
136             .ppaddr = &pp_notidentical,
137             };
138              
139             MODULE = Syntax::Operator::Identical PACKAGE = Syntax::Operator::Identical
140              
141             BOOT:
142 4           boot_xs_parse_infix(0.44);
143              
144 4           register_xs_parse_infix("Syntax::Operator::Identical::≡", &hooks_identical, NULL);
145 4           register_xs_parse_infix("Syntax::Operator::Identical::=:=", &hooks_identical, NULL);
146              
147 4           register_xs_parse_infix("Syntax::Operator::Identical::≢", &hooks_notidentical, NULL);
148 4           register_xs_parse_infix("Syntax::Operator::Identical::!:=", &hooks_notidentical, NULL);
149              
150             /* TODO: Consider adding some sort of rpeep integration into XPI so we can
151             * optimise not(identical) into notidentical or vice-versa
152             */