/* 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, 2021-2022 -- leonerd@leonerd.org.uk
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "XSParseKeyword.h"
#include "XSParseInfix.h"
#include "perl-backcompat.c.inc"
static const char hintkey[] = "t::pieces/permit";
static int build_expr(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
OP *expr = arg0->op;
if(!expr) {
*out = newOP(OP_STUB, 0);
return KEYWORD_PLUGIN_EXPR;
}
/* wrap the result in "("...")" parens so we can unit-test how it parsed */
*out = newBINOP(OP_CONCAT, 0,
newBINOP(OP_CONCAT, 0, newSVOP(OP_CONST, 0, newSVpvs("(")), op_scope(expr)),
newSVOP(OP_CONST, 0, newSVpvs(")")));
return KEYWORD_PLUGIN_EXPR;
}
static int build_prefixedblock(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
PADOFFSET padix = args[0]->padix;
OP *value = args[1]->op;
OP *body = args[2]->op;
OP *padsvop = newOP(OP_PADSV, OPf_MOD|(OPpLVAL_INTRO<<8));
padsvop->op_targ = padix;
*out = op_prepend_elem(OP_LINESEQ,
newBINOP(OP_SASSIGN, 0, value, padsvop),
body);
return KEYWORD_PLUGIN_EXPR;
}
static int build_anonsub(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
*out = newUNOP(OP_REFGEN, 0,
newSVOP(OP_ANONCODE, 0, (SV *)arg0->cv));
return KEYWORD_PLUGIN_EXPR;
}
static int build_list(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
OP *list = arg0->op;
if(!list) {
*out = newOP(OP_STUB, 0);
return KEYWORD_PLUGIN_EXPR;
}
/* TODO: Consider always doing this? */
if(list->op_type != OP_LIST)
list = newLISTOP(OP_LIST, 0, list, NULL);
/* unshift $sep */
#if HAVE_PERL_VERSION(5, 22, 0)
op_sibling_splice(list, cUNOPx(list)->op_first, 0, newSVOP(OP_CONST, 0, newSVpvs(",")));
#else
{
OP *o = newSVOP(OP_CONST, 0, newSVpvs(","));
o->op_sibling = cUNOPx(list)->op_first->op_sibling;
cUNOPx(list)->op_first->op_sibling = o;
}
#endif
*out = op_convert_list(OP_JOIN, 0, list);
return KEYWORD_PLUGIN_EXPR;
}
static int build_constsv(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
*out = newSVOP(OP_CONST, 0, arg0->sv);
return KEYWORD_PLUGIN_EXPR;
}
static int build_constsv_or_undef(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
if(arg0->sv)
*out = newSVOP(OP_CONST, 0, arg0->sv);
else
*out = newOP(OP_UNDEF, OPf_WANT_SCALAR);
return KEYWORD_PLUGIN_EXPR;
}
static int build_constpadix(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
*out = newSVOP(OP_CONST, 0, newSVuv(arg0->padix));
return KEYWORD_PLUGIN_EXPR;
}
static int build_literal(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
/* ignore arg0 */
*out = newSVOP(OP_CONST, 0, SvREFCNT_inc((SV *)hookdata));
return KEYWORD_PLUGIN_EXPR;
}
static int build_attrs(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
int nattrs = args[0]->i;
SV *retsv = newSV(0);
sv_setpvs(retsv, "");
int argi;
for(argi = 0; argi < nattrs; argi++)
sv_catpvf(retsv, ":%s(%s)",
SvPV_nolen(args[argi+1]->attr.name),
SvPOK(args[argi+1]->attr.value) ? SvPV_nolen(args[argi+1]->attr.value) : "");
*out = newSVOP(OP_CONST, 0, retsv);
return KEYWORD_PLUGIN_EXPR;
}
static int build_infix_opname(pTHX_ OP **out, XSParseKeywordPiece *arg0, void *hookdata)
{
const char *opname = PL_op_name[arg0->infix->opcode];
*out = newSVOP(OP_CONST, 0, newSVpvn(opname, strlen(opname)));
return KEYWORD_PLUGIN_EXPR;
}
static int build_lexvar_intro(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
{
PADOFFSET padix = args[0]->padix;
OP *expr = args[1]->op;
OP *varop = newOP(OP_PADSV, OPf_MOD|OPf_REF | (OPpLVAL_INTRO << 8));
varop->op_targ = padix;
OP *assignop = newASSIGNOP(OPf_WANT_VOID, varop, 0, newSVOP(OP_CONST, 0, newSViv(1)));
*out = newLISTOP(OP_LINESEQ, 0, assignop, expr);
return KEYWORD_PLUGIN_EXPR;
}
static void setup_block_VAR(pTHX_ void *hookdata)
{
char *varname = hookdata;
PADOFFSET padix = pad_add_name_pvn(varname, strlen(varname), 0, NULL, NULL);
intro_my();
sv_setpvs(PAD_SVl(padix), "Hello");
}
static void callback_catpv_stages(pTHX_ const char *pv)
{
SV *sv = get_sv("main::STAGES", GV_ADD);
if(!SvPOK(sv))
sv_setpvs(sv, "");
if(SvCUR(sv))
sv_catpvs(sv, ",");
sv_catpv(sv, pv);
}
static void callback_PREPARE(pTHX_ void *hookdata)
{
callback_catpv_stages(aTHX_ "PREPARE");
}
static void callback_START(pTHX_ void *hookdata)
{
callback_catpv_stages(aTHX_ "START");
}
static OP *callback_END(pTHX_ OP *o, void *hookdata)
{
callback_catpv_stages(aTHX_ "END");
return o;
}
static OP *callback_WRAP(pTHX_ OP *o, void *hookdata)
{
callback_catpv_stages(aTHX_ "WRAP");
return o;
}
static const struct XSParseKeywordHooks hooks_block = {
.permit_hintkey = hintkey,
.piece1 = XPK_BLOCK,
.build1 = &build_expr,
};
static const struct XSParseKeywordHooks hooks_block_scalar = {
.permit_hintkey = hintkey,
.piece1 = XPK_BLOCK_SCALARCTX,
.build1 = &build_list,
};
static const struct XSParseKeywordHooks hooks_block_list = {
.permit_hintkey = hintkey,
.piece1 = XPK_BLOCK_LISTCTX,
.build1 = &build_list,
};
static const struct XSParseKeywordHooks hooks_prefixedblock = {
.permit_hintkey = hintkey,
.pieces = (const struct XSParseKeywordPieceType []){
XPK_PREFIXED_BLOCK( XPK_LEXVAR_MY(XPK_LEXVAR_SCALAR), XPK_EQUALS, XPK_TERMEXPR_SCALARCTX, XPK_COMMA ),
{0}
},
.build = &build_prefixedblock,
};
static const struct XSParseKeywordHooks hooks_prefixedblock_VAR = {
.permit_hintkey = hintkey,
.piece1 = XPK_PREFIXED_BLOCK( XPK_SETUP(&setup_block_VAR) ),
.build1 = &build_expr,
};
static const struct XSParseKeywordHooks hooks_anonsub = {
.permit_hintkey = hintkey,
.piece1 = XPK_ANONSUB,
.build1 = &build_anonsub,
};
static const struct XSParseKeywordHooks hooks_stagedanonsub = {
.permit_hintkey = hintkey,
.piece1 = XPK_STAGED_ANONSUB(
XPK_ANONSUB_PREPARE(&callback_PREPARE),
XPK_ANONSUB_START(&callback_START),
XPK_ANONSUB_START(&setup_block_VAR),
XPK_ANONSUB_END(&callback_END),
XPK_ANONSUB_WRAP(&callback_WRAP)
),
.build1 = &build_anonsub,
};
static const struct XSParseKeywordHooks hooks_arithexpr = {
.permit_hintkey = hintkey,
.piece1 = XPK_ARITHEXPR,
.build1 = &build_expr,
};
static const struct XSParseKeywordHooks hooks_arithexpr_opt = {
.permit_hintkey = hintkey,
.piece1 = XPK_ARITHEXPR_OPT,
.build1 = &build_expr,
};
static const struct XSParseKeywordHooks hooks_termexpr = {
.permit_hintkey = hintkey,
.piece1 = XPK_TERMEXPR,
.build1 = &build_expr,
};
static const struct XSParseKeywordHooks hooks_termexpr_opt = {
.permit_hintkey = hintkey,
.piece1 = XPK_TERMEXPR_OPT,
.build1 = &build_expr,
};
static const struct XSParseKeywordHooks hooks_prefixedtermexpr_VAR = {
.permit_hintkey = hintkey,
.piece1 = XPK_PREFIXED_TERMEXPR_ENTERLEAVE( XPK_SETUP(&setup_block_VAR) ),
.build1 = &build_expr,
};
static const struct XSParseKeywordHooks hooks_listexpr = {
.permit_hintkey = hintkey,
.piece1 = XPK_LISTEXPR,
.build1 = &build_list,
};
static const struct XSParseKeywordHooks hooks_listexpr_opt = {
.permit_hintkey = hintkey,
.piece1 = XPK_LISTEXPR_OPT,
.build1 = &build_list,
};
static const struct XSParseKeywordHooks hooks_ident = {
.permit_hintkey = hintkey,
.piece1 = XPK_IDENT,
.build1 = &build_constsv,
};
static const struct XSParseKeywordHooks hooks_ident_opt = {
.permit_hintkey = hintkey,
.piece1 = XPK_IDENT_OPT,
.build1 = &build_constsv_or_undef,
};
static const struct XSParseKeywordHooks hooks_packagename = {
.permit_hintkey = hintkey,
.piece1 = XPK_PACKAGENAME,
.build1 = &build_constsv,
};
static const struct XSParseKeywordHooks hooks_lexvar_name = {
.permit_hintkey = hintkey,
.piece1 = XPK_LEXVARNAME(XPK_LEXVAR_ANY),
.build1 = &build_constsv,
};
static const struct XSParseKeywordHooks hooks_lexvar = {
.permit_hintkey = hintkey,
.piece1 = XPK_LEXVAR(XPK_LEXVAR_ANY),
.build1 = &build_constpadix,
};
static const struct XSParseKeywordHooks hooks_lexvar_my = {
.permit_hintkey = hintkey,
.piece1 = XPK_LEXVAR_MY(XPK_LEXVAR_ANY),
.build1 = &build_constpadix,
};
static const struct XSParseKeywordHooks hooks_lexvar_my_intro = {
.flags = XPK_FLAG_BLOCKSCOPE,
.permit_hintkey = hintkey,
.pieces = (const struct XSParseKeywordPieceType []){
XPK_LEXVAR_MY(XPK_LEXVAR_ANY),
XPK_KEYWORD("in"),
XPK_INTRO_MY,
XPK_TERMEXPR,
0
},
.build = &build_lexvar_intro,
};
static const struct XSParseKeywordHooks hooks_attrs = {
.permit_hintkey = hintkey,
.pieces = (const struct XSParseKeywordPieceType []){
XPK_ATTRIBUTES,
{0},
},
.build = &build_attrs,
};
static const struct XSParseKeywordHooks hooks_vstring = {
.permit_hintkey = hintkey,
.piece1 = XPK_VSTRING,
.build1 = &build_constsv,
};
static const struct XSParseKeywordHooks hooks_vstring_opt = {
.permit_hintkey = hintkey,
.piece1 = XPK_VSTRING_OPT,
.build1 = &build_constsv_or_undef,
};
static const struct XSParseKeywordHooks hooks_infix_relation = {
.permit_hintkey = hintkey,
.piece1 = XPK_INFIX_RELATION,
.build1 = &build_infix_opname,
};
static const struct XSParseKeywordHooks hooks_infix_equality = {
.permit_hintkey = hintkey,
.piece1 = XPK_INFIX_EQUALITY,
.build1 = &build_infix_opname,
};
static const struct XSParseKeywordHooks hooks_colon = {
.permit_hintkey = hintkey,
.piece1 = XPK_COLON,
.build1 = &build_literal,
};
static const struct XSParseKeywordHooks hooks_str = {
.permit_hintkey = hintkey,
.piece1 = XPK_LITERAL("foo"),
.build1 = &build_literal,
};
static const struct XSParseKeywordHooks hooks_kw = {
.permit_hintkey = hintkey,
.piece1 = XPK_KEYWORD("bar"),
.build1 = &build_literal,
};
static const struct XSParseKeywordHooks hooks_autosemi = {
.permit_hintkey = hintkey,
.piece1 = XPK_AUTOSEMI,
.build1 = &build_literal,
};
static const struct XSParseKeywordHooks hooks_warning = {
.permit_hintkey = hintkey,
.piece1 = XPK_WARNING("A warning here\n"),
.build1 = &build_literal,
};
static const struct XSParseKeywordHooks hooks_warning_deprecated = {
.permit_hintkey = hintkey,
.piece1 = XPK_WARNING_DEPRECATED("A deprecated warning here\n"),
.build1 = &build_literal,
};
MODULE = t::pieces PACKAGE = t::pieces
BOOT:
boot_xs_parse_keyword(0);
register_xs_parse_keyword("pieceblock", &hooks_block, NULL);
register_xs_parse_keyword("pieceblock_scalar", &hooks_block_scalar, NULL);
register_xs_parse_keyword("pieceblock_list", &hooks_block_list, NULL);
register_xs_parse_keyword("pieceprefixedblock", &hooks_prefixedblock, NULL);
register_xs_parse_keyword("pieceprefixedblock_VAR", &hooks_prefixedblock_VAR, "$VAR");
register_xs_parse_keyword("pieceanonsub", &hooks_anonsub, NULL);
register_xs_parse_keyword("piecestagedanonsub", &hooks_stagedanonsub, "$VAR");
register_xs_parse_keyword("piecearithexpr", &hooks_arithexpr, NULL);
register_xs_parse_keyword("piecearithexpr_opt", &hooks_arithexpr_opt, NULL);
register_xs_parse_keyword("piecetermexpr", &hooks_termexpr, NULL);
register_xs_parse_keyword("piecetermexpr_opt", &hooks_termexpr_opt, NULL);
register_xs_parse_keyword("piecelistexpr", &hooks_listexpr, NULL);
register_xs_parse_keyword("piecelistexpr_opt", &hooks_listexpr_opt, NULL);
register_xs_parse_keyword("pieceprefixedtermexpr_VAR", &hooks_prefixedtermexpr_VAR, "$VAR");
register_xs_parse_keyword("pieceident", &hooks_ident, NULL);
register_xs_parse_keyword("pieceident_opt", &hooks_ident_opt, NULL);
register_xs_parse_keyword("piecepkg", &hooks_packagename, NULL);
register_xs_parse_keyword("piecelexvarname", &hooks_lexvar_name, NULL);
register_xs_parse_keyword("piecelexvar", &hooks_lexvar, NULL);
register_xs_parse_keyword("piecelexvarmy", &hooks_lexvar_my, NULL);
register_xs_parse_keyword("piecelexvarmyintro", &hooks_lexvar_my_intro, NULL);
register_xs_parse_keyword("pieceattrs", &hooks_attrs, NULL);
register_xs_parse_keyword("piecevstring", &hooks_vstring, NULL);
register_xs_parse_keyword("piecevstring_opt", &hooks_vstring_opt, NULL);
register_xs_parse_keyword("pieceinfix", &hooks_infix_relation, NULL);
register_xs_parse_keyword("pieceinfixeq", &hooks_infix_equality, NULL);
register_xs_parse_keyword("piececolon", &hooks_colon, newSVpvs("colon"));
register_xs_parse_keyword("piecestr", &hooks_str, newSVpvs("foo"));
register_xs_parse_keyword("piecekw", &hooks_kw, newSVpvs("bar"));
register_xs_parse_keyword("pieceautosemi", &hooks_autosemi, newSVpvs("EOS"));
register_xs_parse_keyword("piecewarning", &hooks_warning, &PL_sv_undef);
register_xs_parse_keyword("piecewarndep", &hooks_warning_deprecated, &PL_sv_undef);