Skip to content

Commit 8d1931e

Browse files
committed
Move all the signature param parsing logic out of perly.y into a helper API
Provide a subsignature_*() API Added: * subsignature_start() * subsignature_append_slurpy() * subsignature_append_positional() * subsignature_finish() Call these from code blocks in perly.y Make the actual parser signature struct opaque, hidden in toke.c. This gives it much more robustness against future modifications.
1 parent b41ec6c commit 8d1931e

File tree

12 files changed

+1669
-1583
lines changed

12 files changed

+1669
-1583
lines changed

embed.fnc

+10
Original file line numberDiff line numberDiff line change
@@ -3090,6 +3090,16 @@ ATdmp |bool |strict_utf8_to_uv \
30903090
CRp |NV |str_to_version |NN SV *sv
30913091
: Used in pp_ctl.c
30923092
p |void |sub_crush_depth|NN CV *cv
3093+
: Used in perly.y
3094+
p |void |subsignature_append_positional \
3095+
|NULLOK OP *varop \
3096+
|OPCODE defmode \
3097+
|NULLOK OP *defexpr
3098+
p |void |subsignature_append_slurpy \
3099+
|I32 sigil \
3100+
|NULLOK OP *varop
3101+
p |OP * |subsignature_finish
3102+
p |void |subsignature_start
30933103
Adp |void |suspend_compcv |NN struct suspended_compcv *buffer
30943104
ATdip |void |SvAMAGIC_off |NN SV *sv
30953105
ATdip |void |SvAMAGIC_on |NN SV *sv

embed.h

+4
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,10 @@
12131213
# define sighandler1 Perl_sighandler1
12141214
# define sighandler3 Perl_sighandler3
12151215
# define sub_crush_depth(a) Perl_sub_crush_depth(aTHX_ a)
1216+
# define subsignature_append_positional(a,b,c) Perl_subsignature_append_positional(aTHX_ a,b,c)
1217+
# define subsignature_append_slurpy(a,b) Perl_subsignature_append_slurpy(aTHX_ a,b)
1218+
# define subsignature_finish() Perl_subsignature_finish(aTHX)
1219+
# define subsignature_start() Perl_subsignature_start(aTHX)
12161220
# define sv_2num(a) Perl_sv_2num(aTHX_ a)
12171221
# define sv_clean_all() Perl_sv_clean_all(aTHX)
12181222
# define sv_clean_objs() Perl_sv_clean_objs(aTHX)

op.c

+219
Original file line numberDiff line numberDiff line change
@@ -16190,6 +16190,225 @@ Perl_rcpv_copy(pTHX_ char *pv) {
1619016190
return pv;
1619116191
}
1619216192

16193+
/* Subroutine signature parsing */
16194+
16195+
struct yy_parser_signature {
16196+
UV elems; /* number of signature elements seen so far */
16197+
UV optelems; /* number of optional signature elems seen */
16198+
char slurpy; /* the sigil of the slurpy var (or null) */
16199+
OP *elemops; /* NULL, or an OP_LINESEQ of individual element ops */
16200+
};
16201+
16202+
static void
16203+
destroy_subsignature_context(pTHX_ void *p)
16204+
{
16205+
yy_parser_signature *signature = (yy_parser_signature *)p;
16206+
16207+
if(signature->elemops)
16208+
op_free(signature->elemops);
16209+
16210+
Safefree(signature);
16211+
}
16212+
16213+
/* Called from perly.y on encountering the '(' of a subroutine signature.
16214+
* Does not return anything useful, but sets up the memory structure in
16215+
* `PL_parser->signature` that the following functions make use of.
16216+
*/
16217+
16218+
void
16219+
Perl_subsignature_start(pTHX)
16220+
{
16221+
PERL_ARGS_ASSERT_SUBSIGNATURE_START;
16222+
assert(PL_parser);
16223+
16224+
yy_parser_signature *signature;
16225+
Newx(signature, 1, yy_parser_signature);
16226+
SAVEDESTRUCTOR_X(&destroy_subsignature_context, signature);
16227+
16228+
signature->elems = 0;
16229+
signature->optelems = 0;
16230+
signature->slurpy = 0;
16231+
16232+
signature->elemops = NULL;
16233+
16234+
SAVEVPTR(PL_parser->signature);
16235+
PL_parser->signature = signature;
16236+
}
16237+
16238+
/* Appends another positional scalar parameter to the accumulated set of
16239+
* subroutine params. `varop` may be NULL, but if not it must be an OP_ARGELEM
16240+
* whose op_targ refers to an already-declared pad lexical. That lexical must
16241+
* be a scalar. It is not necessary to set the argument index in the op_aux
16242+
* field; that will be filled in by this function.
16243+
* If `defexpr` is not NULL, it gives a defaulting expression to be evaluated
16244+
* if required, according to `defmode` - one of zero, `OP_DORASSIGN` or
16245+
* `OP_ORASSIGN`.
16246+
*/
16247+
16248+
void
16249+
Perl_subsignature_append_positional(pTHX_ OP *varop, OPCODE defmode, OP *defexpr)
16250+
{
16251+
PERL_ARGS_ASSERT_SUBSIGNATURE_APPEND_POSITIONAL;
16252+
assert(PL_parser);
16253+
yy_parser_signature *signature = PL_parser->signature;
16254+
assert(signature);
16255+
16256+
if(signature->slurpy)
16257+
yyerror("Slurpy parameter not last");
16258+
16259+
UV argix = signature->elems;
16260+
16261+
if(varop) {
16262+
assert(varop->op_type == OP_ARGELEM);
16263+
assert((varop->op_private & OPpARGELEM_MASK) == OPpARGELEM_SV);
16264+
assert(varop->op_targ);
16265+
assert(PadnamePV(PadnamelistARRAY(PL_comppad_name)[varop->op_targ])[0] == '$');
16266+
16267+
/* Now fill in the argix */
16268+
cUNOP_AUXx(varop)->op_aux = INT2PTR(UNOP_AUX_item *, argix);
16269+
}
16270+
16271+
signature->elems++;
16272+
16273+
if(defexpr) {
16274+
signature->optelems++;
16275+
16276+
I32 flags = 0;
16277+
if(defmode == OP_DORASSIGN)
16278+
flags |= OPpARG_IF_UNDEF << 8;
16279+
if(defmode == OP_ORASSIGN)
16280+
flags |= OPpARG_IF_FALSE << 8;
16281+
16282+
if(defexpr->op_type == OP_NULL && !(defexpr->op_flags & OPf_KIDS))
16283+
{
16284+
/* handle '$=' special case */
16285+
if(varop)
16286+
yyerror("Optional parameter lacks default expression");
16287+
}
16288+
else {
16289+
/* a normal '=default' expression */
16290+
OP *defop = newARGDEFELEMOP(flags, defexpr, argix);
16291+
16292+
if(varop) {
16293+
varop->op_flags |= OPf_STACKED;
16294+
(void)op_sibling_splice(varop, NULL, 0, defop);
16295+
scalar(defop);
16296+
}
16297+
else
16298+
varop = newUNOP(OP_NULL, 0, defop);
16299+
16300+
LINKLIST(varop);
16301+
/* NB: normally the first child of a logop is executed before the
16302+
* logop, and it pushes a boolean result ready for the logop. For
16303+
* ARGDEFELEM, the op itself does the boolean calculation, so set
16304+
* the first op to it instead.
16305+
*/
16306+
varop->op_next = defop;
16307+
defexpr->op_next = varop;
16308+
}
16309+
}
16310+
else
16311+
if(signature->optelems)
16312+
yyerror("Mandatory parameter follows optional parameter");
16313+
16314+
if(varop) {
16315+
signature->elemops = op_append_list(OP_LINESEQ, signature->elemops,
16316+
newSTATEOP(0, NULL, varop));
16317+
}
16318+
}
16319+
16320+
/* Appends a final slurpy parameter to the accumulated set of subroutine
16321+
* params. `varop` may be NULL, but if not it must be an OP_ARGELEM whose
16322+
* op_targ refers to an already-declared pad lexical. That lexical must match
16323+
* the `sigil` parameter. It is not necessary to set the argument index in the
16324+
* op_aux field; that will be filled in by this function.
16325+
*/
16326+
16327+
void
16328+
Perl_subsignature_append_slurpy(pTHX_ I32 sigil, OP *varop)
16329+
{
16330+
PERL_ARGS_ASSERT_SUBSIGNATURE_APPEND_SLURPY;
16331+
assert(PL_parser);
16332+
yy_parser_signature *signature = PL_parser->signature;
16333+
assert(signature);
16334+
assert(sigil == '@' || sigil == '%');
16335+
16336+
if(signature->slurpy)
16337+
yyerror("Multiple slurpy parameters not allowed");
16338+
16339+
UV argix = signature->elems;
16340+
16341+
if(varop) {
16342+
assert(varop->op_type == OP_ARGELEM);
16343+
assert((varop->op_private & OPpARGELEM_MASK) ==
16344+
((sigil == '@') ? OPpARGELEM_AV : OPpARGELEM_HV));
16345+
assert(varop->op_targ);
16346+
assert(PadnamePV(PadnamelistARRAY(PL_comppad_name)[varop->op_targ])[0] == sigil);
16347+
16348+
/* Now fill in the argix */
16349+
cUNOP_AUXx(varop)->op_aux = INT2PTR(UNOP_AUX_item *, argix);
16350+
}
16351+
16352+
signature->slurpy = (char)sigil;
16353+
16354+
if(varop) {
16355+
/* TODO: assert() the sigil of the pad variable matches */
16356+
signature->elemops = op_append_list(OP_LINESEQ, signature->elemops,
16357+
newSTATEOP(0, NULL, varop));
16358+
}
16359+
}
16360+
16361+
/* Called from perly.y on encountering the closing `)` of a subroutine
16362+
* signature. This creates the optree fragment responsible for processing all
16363+
* the accumulated subroutine params, to be inserted at the start of the
16364+
* subroutine's optree.
16365+
*/
16366+
16367+
OP *
16368+
Perl_subsignature_finish(pTHX)
16369+
{
16370+
PERL_ARGS_ASSERT_SUBSIGNATURE_FINISH;
16371+
assert(PL_parser);
16372+
yy_parser_signature *signature = PL_parser->signature;
16373+
assert(signature);
16374+
16375+
OP *sigops = signature->elemops;
16376+
signature->elemops = NULL;
16377+
16378+
struct op_argcheck_aux *aux = (struct op_argcheck_aux *)
16379+
PerlMemShared_malloc( sizeof(struct op_argcheck_aux));
16380+
16381+
aux->params = signature->elems;
16382+
aux->opt_params = signature->optelems;
16383+
aux->slurpy = signature->slurpy;
16384+
16385+
OP *check = newUNOP_AUX(OP_ARGCHECK, 0, NULL, (UNOP_AUX_item *)aux);
16386+
16387+
sigops = op_prepend_elem(OP_LINESEQ,
16388+
check,
16389+
sigops);
16390+
16391+
/* a nextstate right at the beginning */
16392+
sigops = op_prepend_elem(OP_LINESEQ,
16393+
newSTATEOP(0, NULL, NULL),
16394+
sigops);
16395+
16396+
/* a nextstate at the end handles context correctly for an empty sub body */
16397+
sigops = op_append_elem(OP_LINESEQ, sigops,
16398+
newSTATEOP(0, NULL, NULL));
16399+
16400+
/* wrap the list of arg ops in a NULL aux op.
16401+
This serves two purposes. First, it makes the arg list a separate
16402+
subtree from the body of the sub, and secondly the null op may in future
16403+
be upgraded to an OP_SIGNATURE when implemented. For now leave it as
16404+
ex-argcheck */
16405+
16406+
OP *ret = newUNOP_AUX(OP_ARGCHECK, 0, sigops, NULL);
16407+
op_null(ret);
16408+
16409+
return ret;
16410+
}
16411+
1619316412
/*
1619416413
* ex: set ts=8 sts=4 sw=4 et:
1619516414
*/

parser.h

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ typedef struct yy_lexshared {
3131
SV *re_eval_str; /* "(?{...})" text */
3232
} LEXSHARED;
3333

34+
/* Opaque struct of data relevant during parsing and construction of a
35+
* subroutine signature. Defined and used exclusively by op.c */
36+
typedef struct yy_parser_signature yy_parser_signature;
37+
3438
typedef struct yy_parser {
3539

3640
/* parser state */
@@ -112,10 +116,7 @@ typedef struct yy_parser {
112116
line_t herelines; /* number of lines in here-doc */
113117
line_t preambling; /* line # when processing $ENV{PERL5DB} */
114118

115-
/* these are valid while parsing a subroutine signature */
116-
UV sig_elems; /* number of signature elements seen so far */
117-
UV sig_optelems; /* number of optional signature elems seen */
118-
char sig_slurpy; /* the sigil of the slurpy var (or null) */
119+
yy_parser_signature *signature; /* parser state of a subroutine signature */
119120
bool sig_seen; /* the currently parsing sub has a signature */
120121

121122
bool recheck_charset_validity;

0 commit comments

Comments
 (0)