/* You may distribute under the terms of either the GNU General Public License
* or the Artistic License (the same terms as Perl itself)
*
* (C) Paul Evans, 2022-2024 -- leonerd@leonerd.org.uk
*/
#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "XSParseInfix.h"
#define HAVE_PERL_VERSION(R, V, S) \
(PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
#if HAVE_PERL_VERSION(5, 36, 0)
# define HAVE_SV_BOOL
#endif
#include "sv_numeq.c.inc"
#include "sv_streq.c.inc"
/* Any defined SV has atleast one of these flags */
#define SV_FLAGMASK_DEFINED (SVf_POK|SVf_IOK|SVf_NOK|SVf_ROK)
#define sv_identical(lhs, rhs) S_sv_identical(aTHX_ lhs, rhs)
static bool S_sv_identical(pTHX_ SV *lhs, SV *rhs)
{
SvGETMAGIC(lhs);
SvGETMAGIC(rhs);
U32 lflags = SvFLAGS(lhs);
U32 rflags = SvFLAGS(rhs);
U32 anyflags = lflags | rflags;
U32 allflags = lflags & rflags;
if(!(anyflags & SV_FLAGMASK_DEFINED))
/* both are undef */
return TRUE;
if(!(lflags & SV_FLAGMASK_DEFINED) || !(rflags & SV_FLAGMASK_DEFINED))
/* atleast one is not defined */
return FALSE;
#ifdef HAVE_SV_BOOL
/* Boolean SVs have all of these flags */
# define SV_FLAGS_BOOL (SVf_POK|SVf_IOK|SVf_IsCOW|SVppv_STATIC)
if((anyflags & SV_FLAGS_BOOL) == SV_FLAGS_BOOL) {
/* at least one SV is likely a boolean. the test doesn't have to be
* perfect because we're about to check properly anyway */
bool lbool = SvIsBOOL(lhs);
bool rbool = SvIsBOOL(rhs);
if(lbool && rbool) {
/* both are definitely bools */
if(SvTRUE(lhs) ^ SvTRUE(rhs))
return FALSE;
else
return TRUE;
}
if(lbool || rbool)
/* one was a bool, one was not */
return FALSE;
/* neither was in fact a bool; no worries just fallthrough */
}
#endif
if(anyflags & SVf_ROK) {
/* at least one SV is a reference */
if(!(allflags & SVf_ROK))
/* ... but not both */
return FALSE;
if(SvRV(lhs) == SvRV(rhs))
return TRUE;
else
return FALSE;
}
/* By now we know that both SVs are defined, non-boolean, non-references.
* This means that between them the must have atleast one of the following
* *private* flags. */
assert(anyflags & (SVp_IOK|SVp_NOK|SVp_POK));
if(anyflags & (SVp_IOK|SVp_NOK))
if(!sv_numeq_flags(lhs, rhs, 0))
return FALSE;
if(anyflags & (SVp_POK))
if(!sv_streq_flags(lhs, rhs, 0))
return FALSE;
/* If neither of the above rejected then we're happy to be true */
return TRUE;
}
static OP *pp_identical(pTHX)
{
dSP;
dTARG;
SV *lhs = TOPs, *rhs = TOPm1s;
bool ret = sv_identical(lhs, rhs);
POPs;
SETs(boolSV(ret));
RETURN;
}
static OP *pp_notidentical(pTHX)
{
dSP;
dTARG;
SV *lhs = TOPs, *rhs = TOPm1s;
bool ret = !sv_identical(lhs, rhs);
POPs;
SETs(boolSV(ret));
RETURN;
}
static const struct XSParseInfixHooks hooks_identical = {
.cls = XPI_CLS_EQUALITY,
.wrapper_func_name = "Syntax::Operator::Identical::is_identical",
.ppaddr = &pp_identical,
};
static const struct XSParseInfixHooks hooks_notidentical = {
.cls = XPI_CLS_RELATION,
.wrapper_func_name = "Syntax::Operator::Identical::is_not_identical",
.ppaddr = &pp_notidentical,
};
MODULE = Syntax::Operator::Identical PACKAGE = Syntax::Operator::Identical
BOOT:
boot_xs_parse_infix(0.44);
register_xs_parse_infix("Syntax::Operator::Identical::≡", &hooks_identical, NULL);
register_xs_parse_infix("Syntax::Operator::Identical::=:=", &hooks_identical, NULL);
register_xs_parse_infix("Syntax::Operator::Identical::≢", &hooks_notidentical, NULL);
register_xs_parse_infix("Syntax::Operator::Identical::!:=", &hooks_notidentical, NULL);
/* TODO: Consider adding some sort of rpeep integration into XPI so we can
* optimise not(identical) into notidentical or vice-versa
*/