Group
Extension

Syntax-Infix-Smartmatch/lib/Syntax/Infix/Smartmatch.xs

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"

#if PERL_VERSION_GE(5, 38, 0)
#include "XSParseInfix.h"
#endif

#ifndef cop_hints_fetch_pvn
#   define cop_hints_fetch_pvn(cop, key, len, hash, flags) Perl_refcounted_he_fetch(aTHX_ cop->cop_hints_hash, NULL, key, len, flags, hash)
#   define cop_hints_fetch_pvs(cop, key, flags) Perl_refcounted_he_fetch(aTHX_ cop->cop_hints_hash, NULL, STR_WITH_LEN(key), flags, 0)
#endif

#ifndef cop_hints_exists_pvn
#   if PERL_VERSION_GE(5, 16, 0)
#	   define cop_hints_exists_pvn(cop, key, len, hash, flags) cop_hints_fetch_pvn(cop, key, len, hash, flags | 0x02)
#   else
#	   define cop_hints_exists_pvn(cop, key, len, hash, flags) (cop_hints_fetch_pvn(cop, key, len, hash, flags) != &PL_sv_placeholder)
#   endif
#endif

#ifndef newSV_type_mortal
SV* S_newSV_type_mortal(pTHX_ svtype type) {
	SV* result = newSV(0);
	SvUPGRADE(result, type);
	return sv_2mortal(result);
}
#define newSV_type_mortal(type) S_newSV_type_mortal(aTHX_ type)
#endif

#ifndef OP_CHECK_MUTEX_LOCK
#define OP_CHECK_MUTEX_LOCK   NOOP
#define OP_CHECK_MUTEX_UNLOCK NOOP
#endif

#define pragma_base "Syntax::Infix::Smartmatch/"
#define pragma_name pragma_base "enabled"
#define pragma_name_length (sizeof(pragma_name) - 1)
static U32 pragma_hash;

#define smartermatch_enabled() cop_hints_exists_pvn(PL_curcop, pragma_name, pragma_name_length, pragma_hash, 0)

static Perl_ppaddr_t orig_smartmatch;

/* This version of do_smartmatch() implements an
   alternative table of matches.
 */
#define do_smartmatch(d, e) S_do_smartmatch(aTHX_ d, e)
STATIC bool S_do_smartmatch(pTHX_ SV* d, SV* e) {
	/* Take care only to invoke mg_get() once for each argument.
	 * Currently we do this by copying the SV if it's magical. */
	if (d) {
		if (SvGMAGICAL(d))
			d = sv_mortalcopy(d);
	}
	else
		d = &PL_sv_undef;

	assert(e);
	if (SvGMAGICAL(e))
		e = sv_mortalcopy(e);

	/* ~~ undef */
	if (!SvOK(e)) {
		return !SvOK(d);
	}
	else if (SvROK(e)) {
		/* First of all, handle overload magic of the rightmost argument */
		if (SvAMAGIC(e)) {
			SV* sv = amagic_call(d, e, smart_amg, AMGf_noleft);
			if (sv)
				return SvTRUEx(sv);
		}

		/* ~~ qr// */
		if (SvTYPE(SvRV(e)) == SVt_REGEXP) {
			dSP;
			REGEXP* re = (REGEXP*)SvRV(e);
			PMOP* const matcher = cPMOPx(newPMOP(OP_MATCH, OPf_WANT_SCALAR | OPf_STACKED));
			PM_SETRE(matcher, ReREFCNT_inc(re));

			ENTER_with_name("matcher");
			SAVEFREEOP((OP *) matcher);
			SAVEOP();
			PL_op = (OP *) matcher;

			XPUSHs(d);
			PUTBACK;
			(void) PL_ppaddr[OP_MATCH](aTHX);
			SPAGAIN;
			bool result = SvTRUEx(POPs);
			PUTBACK;
			LEAVE_with_name("matcher");
			return result;
		}
		/* Non-overloaded object */
		else if (SvOBJECT(SvRV(e)))
			return d == e;
		/* ~~ sub */
		else if (SvTYPE(SvRV(e)) == SVt_PVCV) {
			dSP;
			ENTER_with_name("smartmatch_array_elem_test");
			PUSHMARK(SP);
			PUSHs(d);
			PUTBACK;
			I32 c = call_sv(e, G_SCALAR);
			SPAGAIN;
			bool result = c == 0 ? FALSE : SvTRUEx(POPs);
			PUTBACK;
			LEAVE_with_name("smartmatch_array_elem_test");
			return result;
		}
		/* ~~ @array */
		else if (SvTYPE(SvRV(e)) == SVt_PVAV) {
			Size_t i;
			const Size_t this_len = av_count(MUTABLE_AV(SvRV(e)));

			for (i = 0; i < this_len; ++i) {
				SV * const * const svp = av_fetch(MUTABLE_AV(SvRV(e)), i, FALSE);
				if (!svp)
					continue;

				if (do_smartmatch(d, *svp))
					return TRUE;
			}
			return FALSE;
		}
	}
	/* As a last resort, use string comparison */
	return SvOK(d) && sv_eq_flags(d, e, 0);
}

static OP* pp_smartermatch(pTHX) {
	dSP;
	SV *e = POPs; /* e is for 'expression' */
	SV *d = POPs; /* d is for 'default', as in PL_defgv */
	PUTBACK;
	bool result = do_smartmatch(d, e);
	SPAGAIN;
	PUSHs(result ? &PL_sv_yes : &PL_sv_no);
	RETURN;
}

static OP* pp_smartermatch_switch(pTHX) {
	if (smartermatch_enabled())
		return pp_smartermatch(aTHX);
	else
		return orig_smartmatch(aTHX);
}

#if PERL_VERSION_GE(5, 38, 0)
static const struct XSParseInfixHooks hooks_smarter = {
	.cls            = XPI_CLS_MATCH_MISC,
	.permit_hintkey = "Syntax::Infix::Smartmatch/enabled",
	.ppaddr         = &pp_smartermatch,
};
#endif

static unsigned initialized;

MODULE = Syntax::Infix::Smartmatch				PACKAGE = Syntax::Infix::Smartmatch

PROTOTYPES: DISABLED

BOOT:
	OP_CHECK_MUTEX_LOCK;
	if (!initialized) {
		initialized = 1;
		orig_smartmatch = PL_ppaddr[OP_SMARTMATCH];
		PL_ppaddr[OP_SMARTMATCH] = pp_smartermatch_switch;
	}
	OP_CHECK_MUTEX_UNLOCK;
#	if PERL_VERSION_GE(5, 38, 0)
	boot_xs_parse_infix(0.26);
	register_xs_parse_infix("~~", &hooks_smarter, NULL);
#	endif


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.