XS-Parse-Sublike
view release on metacpan or search on metacpan
src/parse_subsignature_ex.c view on Meta::CPAN
bool need_comma = false;
if(param->flags & NAMEDPARAMf_UTF8)
opdump_printf(ctx, "%sUTF8", need_comma?",":""), need_comma = true;
if(param->flags & NAMEDPARAMf_REQUIRED)
opdump_printf(ctx, "%sREQUIRED", need_comma?",":""), need_comma = true;
if(param->flags & NAMEDPARAMf_REFALIAS)
opdump_printf(ctx, "%sREFALIAS", need_comma?",":""), need_comma = true;
opdump_printf(ctx, ")}\n");
}
}
#endif
static XOP xop_refargelem;
static OP *pp_refargelem(pTHX)
{
dSP;
U8 priv = PL_op->op_private;
IV argix = PTR2IV(cUNOP_AUX->op_aux);
SV *sv;
if(PL_op->op_flags & OPf_STACKED)
sv = POPs;
else {
SV **svp = av_fetch(GvAV(PL_defgv), argix, FALSE);
sv = svp ? *svp : NULL;
}
PUTBACK;
if(!check_refalias_arg(priv, sv)) {
const char *exp_reftype = NULL;
switch(priv & OPpARGELEM_MASK) {
case OPpARGELEM_SV: exp_reftype = "SCALAR"; break;
case OPpARGELEM_AV: exp_reftype = "ARRAY"; break;
case OPpARGELEM_HV: exp_reftype = "HASH"; break;
}
croak_from_caller("Expected argument %" IVdf " to %" SVf " to be a reference to %s",
argix + 1, SVfARG(S_find_runcv_name(aTHX)), exp_reftype);
}
/* Perform refaliasing into the pad */
SV **padentry = &(PAD_SVl(PL_op->op_targ));
save_clearsv(padentry);
SvREFCNT_dec(*padentry);
*padentry = SvREFCNT_inc(SvRV(sv));
return PL_op->op_next;
}
/* Parameter attribute extensions */
typedef struct SignatureAttributeRegistration SignatureAttributeRegistration;
struct SignatureAttributeRegistration {
SignatureAttributeRegistration *next;
const char *name;
STRLEN permit_hintkeylen;
const struct XPSSignatureAttributeFuncs *funcs;
void *funcdata;
};
static SignatureAttributeRegistration *sigattrs = NULL;
#define find_registered_attribute(name) S_find_registered_attribute(aTHX_ name)
static SignatureAttributeRegistration *S_find_registered_attribute(pTHX_ const char *name)
{
HV *hints = GvHV(PL_hintgv);
SignatureAttributeRegistration *reg;
for(reg = sigattrs; reg; reg = reg->next) {
if(!strEQ(name, reg->name))
continue;
if(reg->funcs->permit_hintkey &&
(!hints || !hv_fetch(hints, reg->funcs->permit_hintkey, reg->permit_hintkeylen, 0)))
continue;
return reg;
}
croak("Unrecognised signature parameter attribute :%s", name);
}
struct PendingSignatureFunc {
const struct XPSSignatureAttributeFuncs *funcs;
void *funcdata;
void *attrdata;
};
#define PENDING_FROM_SV(sv) ((struct PendingSignatureFunc *)SvPVX(sv))
static void pending_free(pTHX_ SV *sv)
{
struct PendingSignatureFunc *p = PENDING_FROM_SV(sv);
if(p->funcs->free)
(*p->funcs->free)(aTHX_ p->attrdata, p->funcdata);
}
#define NEW_SV_PENDING() newSV_with_free(sizeof(struct PendingSignatureFunc), &pending_free)
struct NamedParamDetails {
PADOFFSET padix;
U8 flags;
char sigil;
};
struct SignatureParsingContext {
OP *positional_elems; /* OP_LINESEQ of every positional element, in order */
OP *named_elem_defops; /* OP_LINESEQ of those named elements that have defaulting expressions */
HV *named_details; /* SV ptrs to NamedParamDetails of every named parameter */
OP *slurpy_elem;
/* Counters that replace what PL_parser->sig_* used to be */
IV n_elems;
IV n_optelems;
char slurpy_sigil;
};
static void free_parsing_ctx(pTHX_ void *_sigctx)
{
struct SignatureParsingContext *sigctx = _sigctx;
/* TODO the rest */
if(sigctx->named_details)
SvREFCNT_dec((SV *)sigctx->named_details);
}
#define sigctx_add_param(sigctx, paramctx) S_sigctx_add_param(aTHX_ sigctx, paramctx)
static void S_sigctx_add_param(pTHX_ struct SignatureParsingContext *sigctx, struct XPSSignatureParamContext *paramctx)
{
if(paramctx->is_named) {
/* A named scalar */
if(paramctx->namelen) {
if(!sigctx->named_details)
sigctx->named_details = newHV();
struct NamedParamDetails *details;
Newx(details, 1, struct NamedParamDetails);
*details = (struct NamedParamDetails){
.padix = paramctx->padix,
.flags = (!paramctx->defop) ? NAMEDPARAMf_REQUIRED : 0,
.sigil = paramctx->sigil,
};
if(paramctx->is_refalias) {
details->flags |= NAMEDPARAMf_REFALIAS;
switch(paramctx->sigil) {
case '$': details->flags |= NAMEDPARAMf_REFSCALAR; break;
case '@': details->flags |= NAMEDPARAMf_REFARRAY; break;
case '%': details->flags |= NAMEDPARAMf_REFHASH; break;
}
}
hv_store(sigctx->named_details, paramctx->namepv, paramctx->namelen, newSVpvx(details), 0);
}
sigctx->named_elem_defops = op_append_elem(OP_LINESEQ, sigctx->named_elem_defops,
src/parse_subsignature_ex.c view on Meta::CPAN
croak("Expected a signature element at <%s>\n", parser->bufptr);
}
char *lexname = parser->bufptr;
/* Consume sigil */
lex_read_unichar(0);
STRLEN lexname_len = 0;
if(isIDFIRST_uni(lex_peek_unichar(0))) {
lex_read_unichar(0);
while(isALNUM_uni(lex_peek_unichar(0)))
lex_read_unichar(0);
ENTER;
SAVEI16(PL_parser->in_my);
PL_parser->in_my = KEY_sigvar;
lexname_len = PL_parser->bufptr - lexname;
paramctx.padix = pad_add_name_pvn(lexname, lexname_len, 0, NULL, NULL);
if(paramctx.is_named) {
paramctx.namepv = lexname + 1;
paramctx.namelen = lexname_len - 1;
/* named params don't get an individual varop */
}
else {
if(paramctx.is_refalias) {
paramctx.varop = newUNOP_AUX(OP_CUSTOM, 0, NULL, INT2PTR(UNOP_AUX_item *, (sigctx->n_elems)));
paramctx.varop->op_ppaddr = &pp_refargelem;
}
else
paramctx.varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (sigctx->n_elems)));
paramctx.varop->op_private |= private;
paramctx.varop->op_targ = paramctx.padix;
}
LEAVE;
}
lex_read_space(0);
if(lex_peek_unichar(0) == ':') {
if(!permit_attributes)
croak("Attributes on signature parameters are not permitted");
lex_read_unichar(0);
lex_read_space(0);
SV *attrname = sv_newmortal(), *attrval = sv_newmortal();
while(lex_scan_attrval_into(attrname, attrval)) {
lex_read_space(0);
SignatureAttributeRegistration *reg = find_registered_attribute(SvPV_nolen(attrname));
void *attrdata = NULL;
if(reg->funcs->apply)
(*reg->funcs->apply)(aTHX_ ¶mctx, attrval, &attrdata, reg->funcdata);
if(attrdata || reg->funcs->post_defop) {
if(!pending) {
pending = newAV();
SAVEFREESV(pending);
}
SV *psv;
av_push(pending, psv = NEW_SV_PENDING());
PENDING_FROM_SV(psv)->funcs = reg->funcs;
PENDING_FROM_SV(psv)->funcdata = reg->funcdata;
PENDING_FROM_SV(psv)->attrdata = attrdata;
}
if(lex_peek_unichar(0) == ':') {
lex_read_unichar(0);
lex_read_space(0);
}
}
}
if(paramctx.sigil == '$' || paramctx.is_refalias) {
if(paramctx.is_named) {
}
else {
if(sigctx->slurpy_sigil)
yyerror("Slurpy parameters not last");
}
bool default_if_undef = false, default_if_false = false;
if(lex_consume("=") ||
(default_if_undef = lex_consume("//=")) ||
(default_if_false = lex_consume("||="))) {
OP *defexpr = parse_termexpr(PARSE_OPTIONAL);
if(PL_parser->error_count)
croak("Expected a defaulting expression for optional parameter");
if(!paramctx.is_named && !paramctx.varop) {
/* We permit `= undef` and the blank `=` but nothing else */
if(defexpr && defexpr->op_type != OP_UNDEF)
croak("Unnamed positional parameters cannot have defaulting expressions");
}
if(paramctx.is_named) {
OP *assignop = newUNOP(OP_CUSTOM, 0, defexpr);
assignop->op_ppaddr = &pp_namedargassign;
assignop->op_targ = paramctx.padix;
if(paramctx.is_refalias) {
assignop->op_private |= NAMEDPARAMf_REFALIAS;
switch(paramctx.sigil) {
case '$': assignop->op_private |= NAMEDPARAMf_REFSCALAR; break;
case '@': assignop->op_private |= NAMEDPARAMf_REFARRAY; break;
case '%': assignop->op_private |= NAMEDPARAMf_REFHASH; break;
}
}
OP *existsop = (OP *)alloc_LOGOP(OP_CUSTOM, assignop, LINKLIST(assignop));
existsop->op_ppaddr = &pp_namedargexists;
existsop->op_targ = paramctx.padix;
existsop->op_private =
default_if_undef ? OPp_NAMEDARGDEFELEM_IF_UNDEF :
default_if_false ? OPp_NAMEDARGDEFELEM_IF_FALSE :
0;
OP *defop = newUNOP(OP_NULL, 0, existsop);
LINKLIST(defop);
defop->op_next = existsop; /* start of this fragment */
assignop->op_next = defop; /* after assign, stop this fragment */
paramctx.op = defop;
paramctx.defop = defop;
}
else if(paramctx.varop) {
U8 private = 0;
#ifdef OPpARG_IF_UNDEF
if(default_if_undef) private |= OPpARG_IF_UNDEF;
if(default_if_false) private |= OPpARG_IF_FALSE;
#else
if(default_if_undef || default_if_false)
/* TODO: This would be possible with a custom op but we'd basically
* have to copy the behaviour of pp_argdefelem in that case
*/
yyerror("This Perl version cannot handle if_undef/if_false defaulting expressions on positional parameters");
#endif
if(!defexpr)
defexpr = newOP(OP_UNDEF, OPf_WANT_SCALAR);
OP *defop = (OP *)alloc_LOGOP(OP_ARGDEFELEM, defexpr, LINKLIST(defexpr));
defop->op_targ = (PADOFFSET)sigctx->n_elems;
defop->op_private = private;
paramctx.varop->op_flags |= OPf_STACKED;
op_sibling_splice(paramctx.varop, NULL, 0, defop);
defop = op_contextualize(defop, G_SCALAR);
LINKLIST(paramctx.varop);
paramctx.varop->op_next = defop;
defexpr->op_next = paramctx.varop;
paramctx.op = paramctx.varop;
paramctx.defop = defop;
}
/* else this is `= undef` on anonymous param; nothing to do */
}
else {
if(sigctx->n_optelems)
yyerror("Mandatory parameter follows optional parameter");
if(!paramctx.is_named)
paramctx.op = paramctx.varop;
}
}
else {
if(paramctx.is_named)
yyerror("Slurpy parameters may not be named");
if(sigctx->slurpy_sigil)
yyerror("Multiple slurpy parameters not allowed");
if(lex_peek_unichar(0) == '=')
yyerror("A slurpy parameter may not have a default value");
}
if(pending) {
for(int i = 0; i <= AvFILL(pending); i++) {
struct PendingSignatureFunc *p = PENDING_FROM_SV(AvARRAY(pending)[i]);
if(p->funcs->post_defop)
(*p->funcs->post_defop)(aTHX_ ¶mctx, p->attrdata, p->funcdata);
}
}
/* Only after we've run the post_defop hooks can we actually consume the
* result in paramctx.op
*/
sigctx_add_param(sigctx, ¶mctx);
}
OP *XPS_parse_subsignature_ex(pTHX_ int flags,
struct XPSContextWithPointer *ctx,
struct HooksAndData hooksanddata[],
size_t nhooks)
{
/* Mostly reconstructed logic from perl 5.28.0's toke.c and perly.y
*/
yy_parser *parser = PL_parser;
struct SignatureParsingContext sigctx_ = { 0 };
struct SignatureParsingContext *const sigctx = &sigctx_;
if(ctx)
ctx->sigctx = sigctx;
assert((flags & ~(PARSE_SUBSIGNATURE_NAMED_PARAMS|PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES|PARSE_SUBSIGNATURE_REFALIAS)) == 0);
ENTER;
SAVEDESTRUCTOR_X(&free_parsing_ctx, sigctx);
IV hooki;
const struct XSParseSublikeHooks *hooks;
void *hookdata;
FOREACH_HOOKS_FORWARD {
if(hooks->ver >= 7 && hooks->start_signature)
(*hooks->start_signature)(aTHX_ &(ctx->ctx), hookdata);
}
while(lex_peek_unichar(0) != ')') {
lex_read_space(0);
parse_sigelem(sigctx, flags);
if(PL_parser->error_count) {
LEAVE;
return NULL;
}
lex_read_space(0);
switch(lex_peek_unichar(0)) {
case ')': goto endofelems;
case ',': break;
default:
fprintf(stderr, "ARGH unsure how to proceed parse_subsignature at <%s>\n",
parser->bufptr);
croak("ARGH");
break;
}
lex_read_unichar(0);
lex_read_space(0);
}
src/parse_subsignature_ex.c view on Meta::CPAN
}
if(aux->n_params > 1) {
/* Sort the params by hash value */
qsort(&aux->params, aux->n_params, sizeof(aux->params[0]),
&cmp_argelemsnamedparam);
}
OP *argelems_named_op = newUNOP_AUX(OP_CUSTOM, 0, NULL, (UNOP_AUX_item *)aux);
argelems_named_op->op_ppaddr = &pp_argelems_named;
if(sigctx->slurpy_sigil) {
if(sigctx->slurpy_elem && sigctx->slurpy_elem->op_type == OP_LINESEQ) {
/* A real named slurpy variable */
OP *o = OpSIBLING(cLISTOPx(sigctx->slurpy_elem)->op_first);
assert(o);
assert(o->op_type == OP_ARGELEM);
/* Steal the slurpy's targ and private flags */
argelems_named_op->op_targ = o->op_targ;
argelems_named_op->op_private |= o->op_private & OPpARGELEM_MASK;
}
else {
/* The slurpy is unnamed. Don't steal its targ but still set the
* private flags
*/
argelems_named_op->op_targ = 0;
argelems_named_op->op_private = (sigctx->slurpy_sigil == '%') ? OPpARGELEM_HV :
(sigctx->slurpy_sigil == '@') ? OPpARGELEM_AV :
0;
}
if(sigctx->slurpy_elem) {
op_free(sigctx->slurpy_elem);
sigctx->slurpy_elem = NULL;
}
}
ops = op_append_list(OP_LINESEQ, ops,
newSTATEOP(0, NULL, NULL));
ops = op_append_list(OP_LINESEQ, ops,
argelems_named_op);
if(sigctx->named_elem_defops)
/* TODO: append each elem individually */
ops = op_append_list(OP_LINESEQ, ops,
sigctx->named_elem_defops);
}
else if(sigctx->slurpy_elem) {
ops = op_append_list(OP_LINESEQ, ops, sigctx->slurpy_elem);
}
/* a nextstate at the end handles context correctly for an empty
* sub body */
ops = op_append_elem(OP_LINESEQ, ops, newSTATEOP(0, NULL, NULL));
LEAVE;
return ops;
}
void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
{
SignatureAttributeRegistration *reg;
Newx(reg, 1, struct SignatureAttributeRegistration);
*reg = (struct SignatureAttributeRegistration){
.name = name,
.funcs = funcs,
.funcdata = funcdata,
};
if(funcs->permit_hintkey)
reg->permit_hintkeylen = strlen(funcs->permit_hintkey);
reg->next = sigattrs;
sigattrs = reg;
}
void XPS_boot_parse_subsignature_ex(pTHX)
{
XopENTRY_set(&xop_namedargexists, xop_name, "namedargexists");
XopENTRY_set(&xop_namedargexists, xop_desc, "named argument element exists test");
XopENTRY_set(&xop_namedargexists, xop_class, OA_LOGOP);
Perl_custom_op_register(aTHX_ &pp_namedargexists, &xop_namedargexists);
XopENTRY_set(&xop_namedargassign, xop_name, "namedargassign");
XopENTRY_set(&xop_namedargassign, xop_desc, "named argument element assignment");
XopENTRY_set(&xop_namedargassign, xop_class, OA_UNOP);
Perl_custom_op_register(aTHX_ &pp_namedargassign, &xop_namedargassign);
XopENTRY_set(&xop_argelems_named, xop_name, "argelems_named");
XopENTRY_set(&xop_argelems_named, xop_desc, "named parameter elements");
XopENTRY_set(&xop_argelems_named, xop_class, OA_UNOP_AUX);
#ifdef HAVE_XOP_DUMP
XopENTRY_set(&xop_argelems_named, xop_dump, &opdump_argelems_named);
#endif
Perl_custom_op_register(aTHX_ &pp_argelems_named, &xop_argelems_named);
XopENTRY_set(&xop_refargelem, xop_name, "refargelem");
XopENTRY_set(&xop_refargelem, xop_desc, "refalias argument element");
XopENTRY_set(&xop_refargelem, xop_class, OA_UNOP_AUX);
Perl_custom_op_register(aTHX_ &pp_refargelem, &xop_refargelem);
}
#else /* !HAVE_PERL_VERSION(5, 26, 0) */
void XPS_signature_add_param(pTHX_ struct XSParseSublikeContext *ctx, struct XPSSignatureParamDetails *details)
{
}
IV XPS_signature_query(pTHX_ struct XSParseSublikeContext *ctx, int q)
{
return 0;
}
void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
{
croak("Custom subroutine signature attributes are not supported on this verison of Perl");
}
void XPS_boot_parse_subsignature_ex(pTHX)
{
}
#endif
( run in 1.866 second using v1.01-cache-2.11-cpan-75ffa21a3d4 )