/* 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, 2023-2024 -- leonerd@leonerd.org.uk
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "XSParseKeyword.h"
#include "perl-backcompat.c.inc"
#include "perl-additions.c.inc"
#include "newSVop.c.inc"
struct XPKFPHookdata {
/* Phase callbacks */
CV *permitcv;
CV *checkcv;
CV *buildcv;
SV *hookdata;
};
static const struct XSParseKeywordPieceType piece_zero = {0};
#define make_pieces_array(piecesav) S_make_pieces_array(aTHX_ piecesav)
static const struct XSParseKeywordPieceType *S_make_pieces_array(pTHX_ AV *piecesav)
{
U32 npieces = av_count(piecesav);
if(!npieces)
return NULL;
SV *arraypv = newSVpvn("", 0);
for(U32 i = 0; i < npieces; i++) {
dSP;
ENTER;
SAVETMPS;
EXTEND(SP, 1);
PUSHMARK(SP);
PUSHs(AvARRAY(piecesav)[i]);
PUTBACK;
call_method("to_array", G_SCALAR);
SPAGAIN;
sv_catsv(arraypv, POPs);
PUTBACK;
FREETMPS;
LEAVE;
}
sv_catpvn(arraypv, (char *)&piece_zero, sizeof(piece_zero));
return (struct XSParseKeywordPieceType *)SvPVX(arraypv); SvLEN(arraypv) = 0; /* steal */
}
static bool cb_permit(pTHX_ void *hookdata)
{
struct XPKFPHookdata *data = hookdata;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
if(data->hookdata)
XPUSHs(sv_mortalcopy(data->hookdata));
else
XPUSHs(&PL_sv_undef);
PUTBACK;
call_sv((SV *)data->permitcv, G_SCALAR);
SPAGAIN;
bool ret = SvTRUEx(POPs);
FREETMPS;
LEAVE;
return ret;
}
static void cb_check(pTHX_ void *hookdata)
{
struct XPKFPHookdata *data = hookdata;
dSP;
ENTER;
SAVETMPS;
PUSHMARK(SP);
if(data->hookdata)
XPUSHs(sv_mortalcopy(data->hookdata));
else
XPUSHs(&PL_sv_undef);
PUTBACK;
call_sv((SV *)data->checkcv, G_VOID);
FREETMPS;
LEAVE;
}
static int cb_build(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
struct XPKFPHookdata *data = hookdata;
dSP;
SV *outsv = newSV(0);
AV *argsav = newAV();
for(U32 i = 0; i < nargs; i++) {
SV *argsv = newSV(0);
sv_setiv(newSVrv(argsv, "XS::Parse::Keyword::FromPerl::_Arg"), PTR2IV(args[i]));
av_push(argsav, argsv);
}
ENTER;
SAVETMPS;
PUSHMARK(SP);
EXTEND(SP, 3);
mPUSHs(newRV_noinc(outsv));
mPUSHs(newRV_noinc((SV *)argsav));
if(data->hookdata)
PUSHs(sv_mortalcopy(data->hookdata));
else
PUSHs(&PL_sv_undef);
PUTBACK;
call_sv((SV *)data->buildcv, G_SCALAR);
SPAGAIN;
int ret = POPu;
if(SvOK(outsv)) {
*out = SvOPo(outsv);
}
FREETMPS;
LEAVE;
return ret;
}
static void S_setup_constants(pTHX)
{
HV *stash;
AV *export;
#define DO_CONSTANT(c) \
newCONSTSUB(stash, #c, newSViv(c)); \
av_push(export, newSVpv(#c, 0))
stash = gv_stashpvs("XS::Parse::Keyword::FromPerl", TRUE);
export = get_av("XS::Parse::Keyword::FromPerl::EXPORT_OK", TRUE);
DO_CONSTANT(KEYWORD_PLUGIN_EXPR);
DO_CONSTANT(KEYWORD_PLUGIN_STMT);
DO_CONSTANT(XPK_FLAG_EXPR);
DO_CONSTANT(XPK_FLAG_STMT);
DO_CONSTANT(XPK_FLAG_AUTOSEMI);
DO_CONSTANT(XPK_FLAG_BLOCKSCOPE);
DO_CONSTANT(XPK_LEXVAR_SCALAR);
DO_CONSTANT(XPK_LEXVAR_ARRAY);
DO_CONSTANT(XPK_LEXVAR_HASH);
DO_CONSTANT(XPK_LEXVAR_ANY);
}
MODULE = XS::Parse::Keyword::FromPerl PACKAGE = XS::Parse::Keyword::FromPerl::_Arg
SV *line(SV *self)
ALIAS:
op = 1
cv = 2
sv = 3
has_sv = 4
i = 5
padix = 6
line = 7
CODE:
XSParseKeywordPiece *arg = NUM2PTR(XSParseKeywordPiece *, SvIV(SvRV(self)));
switch(ix) {
case 1: RETVAL = newSVop(arg->op); break;
case 2: RETVAL = newRV_inc((SV *)arg->cv); break;
case 3: RETVAL = arg->sv ? SvREFCNT_inc(arg->sv) : &PL_sv_undef; break;
case 4: RETVAL = arg->sv ? &PL_sv_yes : &PL_sv_no; break;
case 5: RETVAL = newSViv(arg->i); break;
case 6: RETVAL = newSVuv(arg->padix); break;
case 7: RETVAL = newSViv(arg->line); break;
}
OUTPUT:
RETVAL
MODULE = XS::Parse::Keyword::FromPerl PACKAGE = XS::Parse::Keyword::FromPerl::_Piece
SV *to_array(SV *self)
CODE:
AV *selfav = AV_FROM_REF(self);
SV **svp = AvARRAY(selfav);
char *type = SvPV_nolen(svp[0]);
struct XSParseKeywordPieceType piece;
/* Simple */
if (strEQ(type, "XPK_BLOCK")) piece = (struct XSParseKeywordPieceType)XPK_BLOCK;
else if(strEQ(type, "XPK_ANONSUB")) piece = (struct XSParseKeywordPieceType)XPK_ANONSUB;
else if(strEQ(type, "XPK_ARITHEXPR")) piece = (struct XSParseKeywordPieceType)XPK_ARITHEXPR;
else if(strEQ(type, "XPK_ARITHEXPR_OPT")) piece = (struct XSParseKeywordPieceType)XPK_ARITHEXPR_OPT;
else if(strEQ(type, "XPK_TERMEXPR")) piece = (struct XSParseKeywordPieceType)XPK_TERMEXPR;
else if(strEQ(type, "XPK_TERMEXPR_OPT")) piece = (struct XSParseKeywordPieceType)XPK_TERMEXPR_OPT;
else if(strEQ(type, "XPK_LISTEXPR")) piece = (struct XSParseKeywordPieceType)XPK_LISTEXPR;
else if(strEQ(type, "XPK_LISTEXPR_OPT")) piece = (struct XSParseKeywordPieceType)XPK_LISTEXPR_OPT;
else if(strEQ(type, "XPK_IDENT")) piece = (struct XSParseKeywordPieceType)XPK_IDENT;
else if(strEQ(type, "XPK_IDENT_OPT")) piece = (struct XSParseKeywordPieceType)XPK_IDENT_OPT;
else if(strEQ(type, "XPK_PACKAGENAME")) piece = (struct XSParseKeywordPieceType)XPK_PACKAGENAME;
else if(strEQ(type, "XPK_PACKAGENAME_OPT")) piece = (struct XSParseKeywordPieceType)XPK_PACKAGENAME_OPT;
else if(strEQ(type, "XPK_VSTRING")) piece = (struct XSParseKeywordPieceType)XPK_VSTRING;
else if(strEQ(type, "XPK_VSTRING_OPT")) piece = (struct XSParseKeywordPieceType)XPK_VSTRING_OPT;
else if(strEQ(type, "XPK_COMMA")) piece = (struct XSParseKeywordPieceType)XPK_COMMA;
else if(strEQ(type, "XPK_COLON")) piece = (struct XSParseKeywordPieceType)XPK_COLON;
else if(strEQ(type, "XPK_EQUALS")) piece = (struct XSParseKeywordPieceType)XPK_EQUALS;
else if(strEQ(type, "XPK_INTRO_MY")) piece = (struct XSParseKeywordPieceType)XPK_INTRO_MY;
/* Single-SV parametric */
else if(strEQ(type, "XPK_LEXVARNAME"))
piece = (struct XSParseKeywordPieceType)XPK_LEXVARNAME(SvUV(svp[1]));
else if(strEQ(type, "XPK_LEXVAR"))
piece = (struct XSParseKeywordPieceType)XPK_LEXVAR(SvUV(svp[1]));
else if(strEQ(type, "XPK_LEXVAR_MY"))
piece = (struct XSParseKeywordPieceType)XPK_LEXVAR_MY(SvUV(svp[1]));
else if(strEQ(type, "XPK_LITERAL"))
piece = (struct XSParseKeywordPieceType)XPK_LITERAL(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_KEYWORD"))
piece = (struct XSParseKeywordPieceType)XPK_KEYWORD(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_FAILURE"))
piece = (struct XSParseKeywordPieceType)XPK_FAILURE(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_WARNING"))
piece = (struct XSParseKeywordPieceType)XPK_WARNING(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_WARNING_AMBIGUOUS"))
piece = (struct XSParseKeywordPieceType)XPK_WARNING_AMBIGUOUS(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_WARNING_DEPRECATED"))
piece = (struct XSParseKeywordPieceType)XPK_WARNING_DEPRECATED(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_WARNING_EXPERIMENTAL"))
piece = (struct XSParseKeywordPieceType)XPK_WARNING_EXPERIMENTAL(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_WARNING_PRECEDENCE"))
piece = (struct XSParseKeywordPieceType)XPK_WARNING_PRECEDENCE(savepv(SvPV_nolen(svp[1])));
else if(strEQ(type, "XPK_WARNING_SYNTAX"))
piece = (struct XSParseKeywordPieceType)XPK_WARNING_SYNTAX(savepv(SvPV_nolen(svp[1])));
/* Structural */
else if(strEQ(type, "XPK_SEQUENCE"))
piece = (struct XSParseKeywordPieceType)XPK_SEQUENCE_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_OPTIONAL"))
piece = (struct XSParseKeywordPieceType)XPK_OPTIONAL_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_REPEATED"))
piece = (struct XSParseKeywordPieceType)XPK_REPEATED_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_CHOICE"))
piece = (struct XSParseKeywordPieceType)XPK_CHOICE_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_PARENS"))
piece = (struct XSParseKeywordPieceType)XPK_PARENS_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_ARGS"))
piece = (struct XSParseKeywordPieceType)XPK_ARGS_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_BRACKETS"))
piece = (struct XSParseKeywordPieceType)XPK_BRACKETS_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_BRACES"))
piece = (struct XSParseKeywordPieceType)XPK_BRACES_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else if(strEQ(type, "XPK_CHEVRONS"))
piece = (struct XSParseKeywordPieceType)XPK_CHEVRONS_pieces(
make_pieces_array(AV_FROM_REF(svp[1]))
);
else
croak("Unrecognised type name %s", type);
RETVAL = newSVpvn((char *)&piece, sizeof(piece));
OUTPUT:
RETVAL
MODULE = XS::Parse::Keyword::FromPerl PACKAGE = XS::Parse::Keyword::FromPerl
void
register_xs_parse_keyword(const char *name, ...)
CODE:
dKWARG(1);
struct XPKFPHookdata data = {0};
U32 flags = 0;
SV *permit_hintkeysv = NULL;
const struct XSParseKeywordPieceType *pieces = NULL;
static const char *args[] = {
"flags",
"pieces",
"permit_hintkey",
"permit",
"check",
/* TODO: parse? */
"build",
"hookdata",
};
while(KWARG_NEXT(args))
switch(kwarg) {
case 0: /* flags */
flags = SvUV(kwval);
break;
case 1: /* pieces */
{
if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVAV)
croak("Expected 'pieces' to be an array ref");
pieces = make_pieces_array(AV_FROM_REF(kwval));
break;
}
case 2: /* permit_hintkey */
permit_hintkeysv = kwval;
break;
case 3: /* permit */
if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVCV)
croak("Expected 'permit' to be a CODE ref");
data.permitcv = (CV *)SvREFCNT_inc((SV *)CV_FROM_REF(kwval));
break;
case 4: /* check */
if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVCV)
croak("Expected 'check' to be a CODE ref");
data.checkcv = (CV *)SvREFCNT_inc((SV *)CV_FROM_REF(kwval));
break;
case 5: /* build */
if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVCV)
croak("Expected 'build' to be a CODE ref");
data.buildcv = (CV *)SvREFCNT_inc((SV *)CV_FROM_REF(kwval));
break;
case 6: /* hookdata */
data.hookdata = newSVsv(kwval);
break;
}
if(!data.buildcv)
croak("Require 'build' for register");
if(!permit_hintkeysv && !data.permitcv)
croak("Require at least one of 'permit_hintkey' or 'permit'");
if(!pieces) {
pieces = &piece_zero;
}
struct XSParseKeywordHooks *hooksptr;
Newx(hooksptr, 1, struct XSParseKeywordHooks);
*hooksptr = (struct XSParseKeywordHooks){
.flags = flags,
.pieces = pieces,
};
if(permit_hintkeysv)
hooksptr->permit_hintkey = savepv(SvPV_nolen(permit_hintkeysv));
if(data.permitcv)
hooksptr->permit = &cb_permit;
if(data.checkcv)
hooksptr->check = &cb_check;
if(data.buildcv)
hooksptr->build = &cb_build;
struct XPKFPHookdata *dataptr;
Newx(dataptr, 1, struct XPKFPHookdata);
*dataptr = data;
register_xs_parse_keyword(savepv(name), hooksptr, dataptr);
BOOT:
boot_xs_parse_keyword(0.39);
S_setup_constants(aTHX);