mirror of
https://github.com/Perl/perl5.git
synced 2026-01-26 00:27:57 +00:00
As noted by https://github.com/Perl/perl5/issues/24027, while multivar foreach and refalias foreach have each been available separately for some time now, it wasn't possible to combine the two until now. This current implementation does have limits on the number of variables in total are allowed in a multivariable foreach loop if any are refaliases (no more than 256), and which ones can be refaliases (none past the 24th) that are all to do with the way I am abusing the U32 `op_targ` field to store the variable count in the lower 8 bits, and a bitmask of which vars are refaliases in the upper 24 bits. I decided to do that as it means I don't have to make the `OP_ITER` op any larger - such as by expanding it to an UNOP_AUX to give it that extra storage space. In practice I don't imagine those limits will be a problem for any practical use-case. If in a future version we did want to expand on those limits, I think it would be possible by moving the `refalias_mask` storage to a PV stored in the UNOP_AUX vector, or somesuch.
1894 lines
52 KiB
Plaintext
1894 lines
52 KiB
Plaintext
/* perly.y
|
|
*
|
|
* Copyright (c) 1991-2002, 2003, 2004, 2005, 2006 Larry Wall
|
|
* Copyright (c) 2007, 2008, 2009, 2010, 2011 by Larry Wall and others
|
|
*
|
|
* You may distribute under the terms of either the GNU General Public
|
|
* License or the Artistic License, as specified in the README file.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* 'I see,' laughed Strider. 'I look foul and feel fair. Is that it?
|
|
* All that is gold does not glitter, not all those who wander are lost.'
|
|
*
|
|
* [p.171 of _The Lord of the Rings_, I/x: "Strider"]
|
|
*/
|
|
|
|
/*
|
|
* This file holds the grammar for the Perl language. If edited, you need
|
|
* to run regen_perly.pl, which re-creates the files perly.h, perly.tab
|
|
* and perly.act which are derived from this.
|
|
*
|
|
* The main job of this grammar is to call the various newFOO()
|
|
* functions in op.c to build a syntax tree of OP structs.
|
|
* It relies on the lexer in toke.c to do the tokenizing.
|
|
*
|
|
* Note: due to the way that the cleanup code works WRT to freeing ops on
|
|
* the parse stack, it is dangerous to assign to the $n variables within
|
|
* an action.
|
|
*/
|
|
|
|
/* Make the parser re-entrant. */
|
|
|
|
%define api.pure
|
|
|
|
%start grammar
|
|
|
|
%union {
|
|
I32 ival; /* __DEFAULT__ (marker for regen_perly.pl;
|
|
must always be 1st union member) */
|
|
void *pval;
|
|
OP *opval;
|
|
GV *gvval;
|
|
}
|
|
|
|
%token <ival> GRAMPROG GRAMEXPR GRAMBLOCK GRAMBARESTMT GRAMFULLSTMT GRAMSTMTSEQ GRAMSUBSIGNATURE
|
|
|
|
/* Tokens emitted by toke.c for simple punctiation characters - &, {, }, etc... */
|
|
%token <ival> PERLY_AMPERSAND
|
|
%token <ival> PERLY_BRACE_OPEN
|
|
%token <ival> PERLY_BRACE_CLOSE
|
|
%token <ival> PERLY_BRACKET_OPEN
|
|
%token <ival> PERLY_BRACKET_CLOSE
|
|
%token <ival> PERLY_COMMA
|
|
%token <ival> PERLY_DOLLAR
|
|
%token <ival> PERLY_DOT
|
|
%token <ival> PERLY_EQUAL_SIGN
|
|
%token <ival> PERLY_MINUS
|
|
%token <ival> PERLY_PERCENT_SIGN
|
|
%token <ival> PERLY_PLUS
|
|
%token <ival> PERLY_SEMICOLON
|
|
%token <ival> PERLY_SLASH
|
|
%token <ival> PERLY_SNAIL
|
|
%token <ival> PERLY_STAR
|
|
|
|
/* Tokens emitted by toke.c on simple keywords */
|
|
%token <ival> KW_FORMAT KW_PACKAGE KW_CLASS
|
|
%token <ival> KW_LOCAL KW_MY KW_FIELD
|
|
%token <ival> KW_IF KW_ELSE KW_ELSIF KW_UNLESS
|
|
%token <ival> KW_FOR KW_UNTIL KW_WHILE KW_CONTINUE
|
|
%token <ival> KW_GIVEN KW_WHEN KW_DEFAULT
|
|
%token <ival> KW_TRY KW_CATCH KW_FINALLY KW_DEFER
|
|
%token <ival> KW_REQUIRE KW_DO
|
|
|
|
/* The 'use' and 'no' keywords both emit this */
|
|
%token <ival> KW_USE_or_NO
|
|
|
|
/* The 'sub' keyword is a bit special; four different tokens depending on
|
|
* named-vs-anon, and whether signatures are in effect */
|
|
%token <ival> KW_SUB_named KW_SUB_named_sig KW_SUB_anon KW_SUB_anon_sig
|
|
%token <ival> KW_METHOD_named KW_METHOD_anon
|
|
|
|
/* Tokens emitted in other situations */
|
|
%token <opval> BAREWORD METHCALL0 METHCALL ATTRLIST THING PMFUNC PRIVATEREF QWLIST
|
|
%token <opval> FUNC0OP FUNC0SUB UNIOPSUB LSTOPSUB
|
|
%token <opval> PLUGEXPR PLUGSTMT
|
|
%token <opval> LABEL PROTOTYPE
|
|
%token <ival> LOOPEX DOTDOT YADAYADA
|
|
%token <ival> FUNC0 FUNC1 FUNC UNIOP LSTOP BLKLSTOP
|
|
%token <ival> POWOP MULOP ADDOP
|
|
%token <ival> DOLSHARP HASHBRACK NOAMP
|
|
%token <ival> COLONATTR FORMLBRACK FORMRBRACK
|
|
%token <ival> SUBLEXSTART SUBLEXEND
|
|
%token <ival> PHASER
|
|
|
|
%type <ival> grammar remember mremember
|
|
|
|
%type <opval> bare_statement_block
|
|
%type <opval> bare_statement_class_declaration
|
|
%type <opval> bare_statement_class_definition
|
|
%type <opval> bare_statement_default
|
|
%type <opval> bare_statement_defer
|
|
%type <opval> bare_statement_expression
|
|
%type <opval> bare_statement_field_declaration
|
|
%type <opval> bare_statement_for
|
|
%type <opval> bare_statement_format
|
|
%type <opval> bare_statement_given
|
|
%type <opval> bare_statement_if
|
|
%type <opval> bare_statement_null
|
|
%type <opval> bare_statement_package_declaration
|
|
%type <opval> bare_statement_package_definition
|
|
%type <opval> bare_statement_phaser
|
|
%type <opval> bare_statement_sub_signature
|
|
%type <opval> bare_statement_sub_traditional
|
|
%type <opval> bare_statement_try_catch
|
|
%type <opval> bare_statement_unless
|
|
%type <opval> bare_statement_until
|
|
%type <opval> bare_statement_utilize
|
|
%type <opval> bare_statement_when
|
|
%type <opval> bare_statement_while
|
|
%type <opval> bare_statement_yadayada
|
|
|
|
%type <ival> startsub startanonsub startanonmethod startformsub
|
|
|
|
%type <ival> mintro
|
|
|
|
%type <ival> sigsub_or_method_named
|
|
%type <opval> stmtseq fullstmt labfullstmt barestmt block mblock else finally
|
|
%type <opval> expr term subscripted scalar ary hsh arylen star amper sideff
|
|
%type <opval> condition
|
|
%type <opval> catch_paren
|
|
%type <opval> empty
|
|
%type <opval> sliceme kvslice gelem
|
|
%type <opval> listexpr nexpr texpr iexpr mexpr mnexpr
|
|
%type <opval> optlistexpr optexpr optrepl indirob listop methodname
|
|
%type <opval> formname subname proto cont my_scalar my_var
|
|
%type <opval> list_of_itervars my_list_of_itervars refgen_topic formblock
|
|
%type <opval> subattrlist attrlist optattrlist myattrterm myterm
|
|
%type <pval> fieldvar /* pval is PADNAME */
|
|
%type <opval> fielddecl
|
|
%type <opval> termbinop termunop anonymous termdo
|
|
%type <opval> termrelop relopchain termeqop eqopchain
|
|
%type <ival> sigslurpsigil sigvar
|
|
%type <opval> sigscalarelem optsigscalardefault sigslurpelem
|
|
%type <ival> optcolon
|
|
%type <opval> sigelem siglist optsiglist subsigguts subsignature optsubsignature
|
|
%type <opval> subbody optsubbody sigsubbody optsigsubbody
|
|
%type <opval> formstmtseq formline formarg
|
|
|
|
%nonassoc <ival> PREC_LOW
|
|
%nonassoc LOOPEX
|
|
|
|
%nonassoc <pval> PLUGIN_LOW_OP
|
|
%left <ival> OROP <pval> PLUGIN_LOGICAL_OR_LOW_OP
|
|
%left <ival> ANDOP <pval> PLUGIN_LOGICAL_AND_LOW_OP
|
|
%right <ival> NOTOP
|
|
%nonassoc LSTOP LSTOPSUB BLKLSTOP
|
|
%left PERLY_COMMA
|
|
%right <ival> ASSIGNOP <pval> PLUGIN_ASSIGN_OP
|
|
%right <ival> PERLY_QUESTION_MARK PERLY_COLON
|
|
%nonassoc DOTDOT
|
|
%left <ival> OROR DORDOR <pval> PLUGIN_LOGICAL_OR_OP
|
|
%left <ival> ANDAND <pval> PLUGIN_LOGICAL_AND_OP
|
|
%left <ival> BITOROP
|
|
%left <ival> BITANDOP
|
|
%left <ival> CHEQOP NCEQOP
|
|
%left <ival> CHRELOP NCRELOP
|
|
%nonassoc <pval> PLUGIN_REL_OP
|
|
%nonassoc UNIOP UNIOPSUB
|
|
%nonassoc KW_REQUIRE
|
|
%left <ival> SHIFTOP
|
|
%left ADDOP <pval> PLUGIN_ADD_OP
|
|
%left MULOP <pval> PLUGIN_MUL_OP
|
|
%left <ival> MATCHOP
|
|
%right <ival> PERLY_EXCLAMATION_MARK PERLY_TILDE UMINUS REFGEN
|
|
%right POWOP <pval> PLUGIN_POW_OP
|
|
%nonassoc <ival> PREINC PREDEC POSTINC POSTDEC POSTJOIN
|
|
%nonassoc <pval> PLUGIN_HIGH_OP
|
|
%left <ival> ARROW
|
|
%nonassoc <ival> PERLY_PAREN_CLOSE
|
|
%left <ival> PERLY_PAREN_OPEN
|
|
%left PERLY_BRACKET_OPEN PERLY_BRACE_OPEN
|
|
|
|
%% /* RULES */
|
|
|
|
/* Top-level choice of what kind of thing yyparse was called to parse */
|
|
grammar : GRAMPROG
|
|
{
|
|
parser->expect = XSTATE;
|
|
$<ival>$ = 0;
|
|
}
|
|
remember stmtseq
|
|
{
|
|
newPROG(block_end($remember,$stmtseq));
|
|
PL_compiling.cop_seq = 0;
|
|
$$ = 0;
|
|
}
|
|
| GRAMEXPR
|
|
{
|
|
parser->expect = XTERM;
|
|
$<ival>$ = 0;
|
|
}
|
|
optexpr
|
|
{
|
|
PL_eval_root = $optexpr;
|
|
$$ = 0;
|
|
}
|
|
| GRAMBLOCK
|
|
{
|
|
parser->expect = XBLOCK;
|
|
$<ival>$ = 0;
|
|
}
|
|
block
|
|
{
|
|
PL_pad_reset_pending = TRUE;
|
|
PL_eval_root = $block;
|
|
$$ = 0;
|
|
yyunlex();
|
|
parser->yychar = yytoken = YYEOF;
|
|
}
|
|
| GRAMBARESTMT
|
|
{
|
|
parser->expect = XSTATE;
|
|
$<ival>$ = 0;
|
|
}
|
|
barestmt
|
|
{
|
|
PL_pad_reset_pending = TRUE;
|
|
PL_eval_root = $barestmt;
|
|
$$ = 0;
|
|
yyunlex();
|
|
parser->yychar = yytoken = YYEOF;
|
|
}
|
|
| GRAMFULLSTMT
|
|
{
|
|
parser->expect = XSTATE;
|
|
$<ival>$ = 0;
|
|
}
|
|
fullstmt
|
|
{
|
|
PL_pad_reset_pending = TRUE;
|
|
PL_eval_root = $fullstmt;
|
|
$$ = 0;
|
|
yyunlex();
|
|
parser->yychar = yytoken = YYEOF;
|
|
}
|
|
| GRAMSTMTSEQ
|
|
{
|
|
parser->expect = XSTATE;
|
|
$<ival>$ = 0;
|
|
}
|
|
stmtseq
|
|
{
|
|
PL_eval_root = $stmtseq;
|
|
$$ = 0;
|
|
}
|
|
| GRAMSUBSIGNATURE
|
|
{
|
|
parser->expect = XSTATE;
|
|
$<ival>$ = 0;
|
|
}
|
|
subsigguts
|
|
{
|
|
PL_eval_root = $subsigguts;
|
|
$$ = 0;
|
|
}
|
|
;
|
|
|
|
bare_statement_block
|
|
: block
|
|
cont
|
|
{
|
|
$$ = new_block_statement ($block, $cont);
|
|
}
|
|
;
|
|
|
|
bare_statement_class_declaration
|
|
: KW_CLASS
|
|
BAREWORD[version]
|
|
BAREWORD[package]
|
|
optattrlist
|
|
PERLY_SEMICOLON
|
|
{
|
|
package ($package, $version);
|
|
$$ = NULL;
|
|
class_setup_stash(PL_curstash);
|
|
if ($optattrlist) {
|
|
class_apply_attributes(PL_curstash, $optattrlist);
|
|
}
|
|
}
|
|
;
|
|
|
|
bare_statement_class_definition
|
|
: KW_CLASS
|
|
BAREWORD[version]
|
|
BAREWORD[package]
|
|
optattrlist
|
|
PERLY_BRACE_OPEN
|
|
remember
|
|
{
|
|
package ($package, $version);
|
|
class_setup_stash(PL_curstash);
|
|
if ($optattrlist) {
|
|
class_apply_attributes(PL_curstash, $optattrlist);
|
|
}
|
|
}
|
|
stmtseq
|
|
PERLY_BRACE_CLOSE
|
|
{
|
|
$$ = new_block_statement (block_end ($remember, $stmtseq), NULL);
|
|
if (parser->copline > (line_t)$PERLY_BRACE_OPEN)
|
|
parser->copline = (line_t)$PERLY_BRACE_OPEN;
|
|
}
|
|
;
|
|
|
|
bare_statement_default
|
|
: KW_DEFAULT
|
|
block
|
|
{
|
|
$$ = newWHENOP(0, op_scope($block));
|
|
}
|
|
;
|
|
|
|
bare_statement_defer
|
|
: KW_DEFER mblock
|
|
{
|
|
$$ = newDEFEROP(0, op_scope($mblock));
|
|
}
|
|
;
|
|
|
|
bare_statement_expression
|
|
/* expression when used as a statement */
|
|
: sideff PERLY_SEMICOLON
|
|
{
|
|
$$ = $sideff;
|
|
}
|
|
;
|
|
|
|
bare_statement_field_declaration
|
|
: fielddecl
|
|
PERLY_SEMICOLON
|
|
{
|
|
$$ = $fielddecl;
|
|
}
|
|
;
|
|
|
|
bare_statement_for
|
|
: KW_FOR
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mnexpr[init_mnexpr]
|
|
PERLY_SEMICOLON
|
|
{
|
|
parser->expect = XTERM;
|
|
}
|
|
texpr
|
|
PERLY_SEMICOLON
|
|
{
|
|
parser->expect = XTERM;
|
|
}
|
|
mintro
|
|
mnexpr[iterate_mnexpr]
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
{
|
|
OP *initop = $init_mnexpr;
|
|
OP *forop = newWHILEOP(0, 1, NULL, scalar($texpr), $mblock, $iterate_mnexpr, $mintro);
|
|
if (initop) {
|
|
forop = op_prepend_elem(
|
|
OP_LINESEQ,
|
|
initop,
|
|
op_append_elem(OP_LINESEQ, newOP(OP_UNSTACK, OPf_SPECIAL), forop)
|
|
);
|
|
}
|
|
PL_hints |= HINT_BLOCK_SCOPE;
|
|
$$ = block_end($remember, forop);
|
|
parser->copline = (line_t)$KW_FOR;
|
|
}
|
|
| KW_FOR
|
|
KW_MY
|
|
remember
|
|
my_scalar
|
|
PERLY_PAREN_OPEN
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
cont
|
|
{
|
|
$$ = block_end($remember, newFOROP(0, $my_scalar, $mexpr, $mblock, $cont));
|
|
parser->copline = (line_t)$KW_FOR;
|
|
}
|
|
| KW_FOR
|
|
KW_MY
|
|
remember
|
|
PERLY_PAREN_OPEN
|
|
my_list_of_itervars
|
|
PERLY_PAREN_CLOSE
|
|
PERLY_PAREN_OPEN
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
cont
|
|
{
|
|
if ($my_list_of_itervars->op_type == OP_PADSV)
|
|
/* degenerate case of 1 var: for my ($x) ....
|
|
Flag it so it can be special-cased in newFOROP */
|
|
$my_list_of_itervars->op_flags |= OPf_PARENS;
|
|
$$ = block_end($remember, newFOROP(0, $my_list_of_itervars, $mexpr, $mblock, $cont));
|
|
parser->copline = (line_t)$KW_FOR;
|
|
}
|
|
| KW_FOR
|
|
scalar
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
cont
|
|
{
|
|
$$ = block_end($remember, newFOROP(0, op_lvalue($scalar, OP_ENTERLOOP), $mexpr, $mblock, $cont));
|
|
parser->copline = (line_t)$KW_FOR;
|
|
}
|
|
| KW_FOR
|
|
my_refgen
|
|
remember
|
|
my_var
|
|
{
|
|
parser->in_my = 0;
|
|
$<opval>$ = my($my_var);
|
|
}[variable]
|
|
PERLY_PAREN_OPEN
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
cont
|
|
{
|
|
$$ = block_end(
|
|
$remember,
|
|
newFOROP(
|
|
0,
|
|
op_lvalue(
|
|
newUNOP(OP_REFGEN, 0, $<opval>variable),
|
|
OP_ENTERLOOP
|
|
),
|
|
$mexpr,
|
|
$mblock,
|
|
$cont
|
|
)
|
|
);
|
|
parser->copline = (line_t)$KW_FOR;
|
|
}
|
|
| KW_FOR
|
|
REFGEN
|
|
refgen_topic
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
cont
|
|
{
|
|
$$ = block_end (
|
|
$remember,
|
|
newFOROP (
|
|
0,
|
|
op_lvalue (newUNOP(OP_REFGEN, 0, $refgen_topic), OP_ENTERLOOP),
|
|
$mexpr,
|
|
$mblock,
|
|
$cont
|
|
)
|
|
);
|
|
parser->copline = (line_t)$KW_FOR;
|
|
}
|
|
| KW_FOR
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
cont
|
|
{
|
|
$$ = block_end($remember, newFOROP(0, NULL, $mexpr, $mblock, $cont));
|
|
parser->copline = (line_t)$KW_FOR;
|
|
}
|
|
;
|
|
|
|
bare_statement_format
|
|
: KW_FORMAT
|
|
startformsub
|
|
formname
|
|
formblock
|
|
{
|
|
CV *fmtcv = PL_compcv;
|
|
newFORM($startformsub, $formname, $formblock);
|
|
if (CvOUTSIDE(fmtcv) && !CvEVAL(CvOUTSIDE(fmtcv))) {
|
|
pad_add_weakref(fmtcv);
|
|
}
|
|
parser->parsed_sub = 1;
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
bare_statement_given
|
|
: KW_GIVEN
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
{
|
|
$$ = block_end($remember, newGIVENOP($mexpr, op_scope($mblock), 0));
|
|
parser->copline = (line_t)$KW_GIVEN;
|
|
}
|
|
;
|
|
|
|
bare_statement_if
|
|
: KW_IF
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
else
|
|
{
|
|
$$ = block_end($remember, newCONDOP(0, $mexpr, op_scope($mblock), $else));
|
|
parser->copline = (line_t)$KW_IF;
|
|
}
|
|
;
|
|
|
|
bare_statement_null
|
|
: PERLY_SEMICOLON
|
|
{
|
|
$$ = NULL;
|
|
parser->copline = NOLINE;
|
|
}
|
|
;
|
|
|
|
bare_statement_package_declaration
|
|
: KW_PACKAGE
|
|
BAREWORD[version]
|
|
BAREWORD[package]
|
|
PERLY_SEMICOLON
|
|
/* version and package appear in the reverse order to what may be
|
|
* expected, because toke.c has already pushed both of them to a stack
|
|
* by calling force_next() from within force_version().
|
|
* When the parser pops them back out again they appear swapped
|
|
*/
|
|
{
|
|
package ($package, $version);
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
bare_statement_package_definition
|
|
: KW_PACKAGE
|
|
BAREWORD[version]
|
|
BAREWORD[package]
|
|
PERLY_BRACE_OPEN
|
|
remember
|
|
{
|
|
package ($package, $version);
|
|
}
|
|
stmtseq
|
|
PERLY_BRACE_CLOSE
|
|
{
|
|
$$ = new_block_statement (block_end ($remember, $stmtseq), NULL);
|
|
if (parser->copline > (line_t)$PERLY_BRACE_OPEN)
|
|
parser->copline = (line_t)$PERLY_BRACE_OPEN;
|
|
}
|
|
;
|
|
|
|
bare_statement_phaser
|
|
: PHASER
|
|
startsub
|
|
{
|
|
switch($PHASER) {
|
|
case KEY_ADJUST:
|
|
croak_kw_unless_class("ADJUST");
|
|
class_prepare_method_parse(PL_compcv);
|
|
break;
|
|
default:
|
|
NOT_REACHED;
|
|
}
|
|
}
|
|
optsubbody
|
|
{
|
|
OP *body = $optsubbody;
|
|
SvREFCNT_inc_simple_void(PL_compcv);
|
|
|
|
CV *cv;
|
|
|
|
switch($PHASER) {
|
|
case KEY_ADJUST:
|
|
cv = newATTRSUB($startsub, NULL, NULL, NULL, body);
|
|
class_add_ADJUST(PL_curstash, cv);
|
|
break;
|
|
}
|
|
$$ = NULL;
|
|
parser->parsed_sub = 1;
|
|
}
|
|
;
|
|
|
|
bare_statement_sub_signature
|
|
/* sub declaration or definition under 'use feature * "signatures"'.
|
|
* (Note that a signature isn't * allowed in a declaration)
|
|
*/
|
|
: sigsub_or_method_named
|
|
subname
|
|
startsub
|
|
{
|
|
init_named_cv(PL_compcv, $subname);
|
|
if($sigsub_or_method_named == KW_METHOD_named) {
|
|
croak_kw_unless_class("method");
|
|
class_prepare_method_parse(PL_compcv);
|
|
}
|
|
parser->in_my = 0;
|
|
parser->in_my_stash = NULL;
|
|
}
|
|
subattrlist
|
|
optsigsubbody
|
|
{
|
|
OP *body = $optsigsubbody;
|
|
|
|
SvREFCNT_inc_simple_void(PL_compcv);
|
|
$subname->op_type == OP_CONST
|
|
? newATTRSUB($startsub, $subname, NULL, $subattrlist, body)
|
|
: newMYSUB( $startsub, $subname, NULL, $subattrlist, body)
|
|
;
|
|
intro_my();
|
|
parser->parsed_sub = 1;
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
bare_statement_sub_traditional
|
|
/* sub declaration or definition not within scope of 'use feature "signatures"'*/
|
|
: KW_SUB_named
|
|
subname
|
|
startsub
|
|
{
|
|
init_named_cv(PL_compcv, $subname);
|
|
parser->in_my = 0;
|
|
parser->in_my_stash = NULL;
|
|
}
|
|
proto
|
|
subattrlist
|
|
optsubbody
|
|
{
|
|
SvREFCNT_inc_simple_void(PL_compcv);
|
|
$subname->op_type == OP_CONST
|
|
? newATTRSUB($startsub, $subname, $proto, $subattrlist, $optsubbody)
|
|
: newMYSUB($startsub, $subname, $proto, $subattrlist, $optsubbody)
|
|
;
|
|
intro_my();
|
|
parser->parsed_sub = 1;
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
bare_statement_try_catch
|
|
: KW_TRY
|
|
mblock[try]
|
|
KW_CATCH
|
|
remember
|
|
catch_paren[scalar]
|
|
{
|
|
if(!$scalar) {
|
|
yyerror("catch block requires a (VAR)");
|
|
YYERROR;
|
|
}
|
|
}
|
|
mblock[catch]
|
|
finally
|
|
{
|
|
$$ = newTRYCATCHOP(0, $try, $scalar, block_end($remember, op_scope($catch)));
|
|
if($finally)
|
|
$$ = op_wrap_finally($$, $finally);
|
|
parser->copline = (line_t)$KW_TRY;
|
|
}
|
|
;
|
|
|
|
bare_statement_unless
|
|
: KW_UNLESS
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
else
|
|
{
|
|
$$ = block_end($remember, newCONDOP(0, $mexpr, $else, op_scope($mblock)));
|
|
parser->copline = (line_t)$KW_UNLESS;
|
|
}
|
|
;
|
|
|
|
bare_statement_until
|
|
: KW_UNTIL
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
iexpr
|
|
PERLY_PAREN_CLOSE
|
|
mintro
|
|
mblock
|
|
cont
|
|
{
|
|
$$ = block_end($remember, newWHILEOP(0, 1, NULL, $iexpr, $mblock, $cont, $mintro));
|
|
parser->copline = (line_t)$KW_UNTIL;
|
|
}
|
|
;
|
|
|
|
bare_statement_utilize
|
|
: KW_USE_or_NO
|
|
startsub
|
|
{ CvSPECIAL_on(PL_compcv); /* It's a BEGIN {} */ }
|
|
BAREWORD[version]
|
|
BAREWORD[module]
|
|
optlistexpr
|
|
PERLY_SEMICOLON
|
|
/* version and package appear in reverse order for the same reason as
|
|
* KW_PACKAGE; see comment above */
|
|
{
|
|
SvREFCNT_inc_simple_void(PL_compcv);
|
|
utilize($KW_USE_or_NO, $startsub, $version, $module, $optlistexpr);
|
|
parser->parsed_sub = 1;
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
bare_statement_when
|
|
: KW_WHEN
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
mexpr
|
|
PERLY_PAREN_CLOSE
|
|
mblock
|
|
{
|
|
$$ = block_end($remember, newWHENOP($mexpr, op_scope($mblock)));
|
|
}
|
|
;
|
|
|
|
bare_statement_while
|
|
: KW_WHILE
|
|
PERLY_PAREN_OPEN
|
|
remember
|
|
texpr
|
|
PERLY_PAREN_CLOSE
|
|
mintro
|
|
mblock
|
|
cont
|
|
{
|
|
$$ = block_end($remember, newWHILEOP(0, 1, NULL, $texpr, $mblock, $cont, $mintro));
|
|
parser->copline = (line_t)$KW_WHILE;
|
|
}
|
|
;
|
|
|
|
bare_statement_yadayada
|
|
: YADAYADA PERLY_SEMICOLON
|
|
{
|
|
/* diag_listed_as: Unimplemented */
|
|
$$ = newLISTOP(OP_DIE, 0, newOP(OP_PUSHMARK, 0), newSVOP(OP_CONST, 0, newSVpvs("Unimplemented")));
|
|
}
|
|
;
|
|
|
|
/* Either a signatured 'sub' or 'method' keyword */
|
|
sigsub_or_method_named
|
|
: KW_SUB_named_sig
|
|
{ $$ = KW_SUB_named_sig; }
|
|
| KW_METHOD_named
|
|
{ $$ = KW_METHOD_named; }
|
|
;
|
|
|
|
/* An ordinary block */
|
|
block : PERLY_BRACE_OPEN remember stmtseq PERLY_BRACE_CLOSE
|
|
{ if (parser->copline > (line_t)$PERLY_BRACE_OPEN)
|
|
parser->copline = (line_t)$PERLY_BRACE_OPEN;
|
|
$$ = block_end($remember, $stmtseq);
|
|
}
|
|
;
|
|
|
|
empty
|
|
: %empty { $$ = NULL; }
|
|
;
|
|
|
|
/* format body */
|
|
formblock: PERLY_EQUAL_SIGN remember PERLY_SEMICOLON FORMRBRACK formstmtseq PERLY_SEMICOLON PERLY_DOT
|
|
{ if (parser->copline > (line_t)$PERLY_EQUAL_SIGN)
|
|
parser->copline = (line_t)$PERLY_EQUAL_SIGN;
|
|
$$ = block_end($remember, $formstmtseq);
|
|
}
|
|
;
|
|
|
|
remember: %empty /* start a full lexical scope */
|
|
{ $$ = block_start(TRUE);
|
|
parser->parsed_sub = 0; }
|
|
;
|
|
|
|
mblock : PERLY_BRACE_OPEN mremember stmtseq PERLY_BRACE_CLOSE
|
|
{ if (parser->copline > (line_t)$PERLY_BRACE_OPEN)
|
|
parser->copline = (line_t)$PERLY_BRACE_OPEN;
|
|
$$ = block_end($mremember, $stmtseq);
|
|
}
|
|
;
|
|
|
|
mremember: %empty /* start a partial lexical scope */
|
|
{ $$ = block_start(FALSE);
|
|
parser->parsed_sub = 0; }
|
|
;
|
|
|
|
/* The parenthesized variable of a catch block */
|
|
catch_paren: empty
|
|
/* not really valid grammar but we detect it in the
|
|
* action block to throw a nicer error message */
|
|
| PERLY_PAREN_OPEN
|
|
{ parser->in_my = KEY_catch; }
|
|
scalar
|
|
{ parser->in_my = 0; intro_my(); }
|
|
PERLY_PAREN_CLOSE
|
|
{ $$ = $scalar; }
|
|
;
|
|
|
|
/* A sequence of statements in the program */
|
|
stmtseq
|
|
: empty
|
|
| stmtseq[list] fullstmt
|
|
{ $$ = op_append_list(OP_LINESEQ, $list, $fullstmt);
|
|
PL_pad_reset_pending = TRUE;
|
|
if ($list && $fullstmt)
|
|
PL_hints |= HINT_BLOCK_SCOPE;
|
|
}
|
|
;
|
|
|
|
/* A sequence of format lines */
|
|
formstmtseq
|
|
: empty
|
|
| formstmtseq[list] formline
|
|
{ $$ = op_append_list(OP_LINESEQ, $list, $formline);
|
|
PL_pad_reset_pending = TRUE;
|
|
if ($list && $formline)
|
|
PL_hints |= HINT_BLOCK_SCOPE;
|
|
}
|
|
;
|
|
|
|
/* A statement in the program, including optional labels */
|
|
fullstmt: barestmt
|
|
{
|
|
$$ = $barestmt ? newSTATEOP(0, NULL, $barestmt) : NULL;
|
|
}
|
|
| labfullstmt
|
|
{ $$ = $labfullstmt; }
|
|
;
|
|
|
|
labfullstmt: LABEL barestmt
|
|
{
|
|
SV *label = cSVOPx_sv($LABEL);
|
|
$$ = newSTATEOP(SvFLAGS(label) & SVf_UTF8,
|
|
savepv(SvPVX_const(label)), $barestmt);
|
|
op_free($LABEL);
|
|
}
|
|
| LABEL labfullstmt[list]
|
|
{
|
|
SV *label = cSVOPx_sv($LABEL);
|
|
$$ = newSTATEOP(SvFLAGS(label) & SVf_UTF8,
|
|
savepv(SvPVX_const(label)), $list);
|
|
op_free($LABEL);
|
|
}
|
|
;
|
|
|
|
/* A bare statement, lacking label and other aspects of state op
|
|
*
|
|
* Maintain rule as simple alternatives of single nonterminals,
|
|
* leveraging bison's default action: $$ = $1.
|
|
*
|
|
* Try to keep nonterminals alphabetically sorted.
|
|
*/
|
|
barestmt
|
|
: PLUGSTMT
|
|
| bare_statement_block
|
|
| bare_statement_class_declaration
|
|
| bare_statement_class_definition
|
|
| bare_statement_default
|
|
| bare_statement_defer
|
|
| bare_statement_expression
|
|
| bare_statement_field_declaration
|
|
| bare_statement_for
|
|
| bare_statement_format
|
|
| bare_statement_given
|
|
| bare_statement_if
|
|
| bare_statement_null
|
|
| bare_statement_package_declaration
|
|
| bare_statement_package_definition
|
|
| bare_statement_phaser
|
|
| bare_statement_sub_signature
|
|
| bare_statement_sub_traditional
|
|
| bare_statement_try_catch
|
|
| bare_statement_unless
|
|
| bare_statement_until
|
|
| bare_statement_utilize
|
|
| bare_statement_when
|
|
| bare_statement_while
|
|
| bare_statement_yadayada
|
|
;
|
|
|
|
/* Format line */
|
|
formline: THING formarg
|
|
{ OP *list;
|
|
if ($formarg) {
|
|
OP *term = $formarg;
|
|
list = op_append_elem(OP_LIST, $THING, term);
|
|
}
|
|
else {
|
|
list = $THING;
|
|
}
|
|
if (parser->copline == NOLINE)
|
|
parser->copline = CopLINE(PL_curcop)-1;
|
|
else parser->copline--;
|
|
$$ = newSTATEOP(0, NULL,
|
|
op_convert_list(OP_FORMLINE, 0, list));
|
|
}
|
|
;
|
|
|
|
formarg
|
|
: empty
|
|
| FORMLBRACK stmtseq FORMRBRACK
|
|
{ $$ = op_unscope($stmtseq); }
|
|
;
|
|
|
|
condition: expr
|
|
;
|
|
|
|
/* An expression which may have a side-effect */
|
|
sideff : error
|
|
{ $$ = NULL; }
|
|
| expr[body]
|
|
{ $$ = $body; }
|
|
| expr[body] KW_IF condition
|
|
{ $$ = newLOGOP(OP_AND, 0, $condition, $body); }
|
|
| expr[body] KW_UNLESS condition
|
|
{ $$ = newLOGOP(OP_OR, 0, $condition, $body); }
|
|
| expr[body] KW_WHILE condition
|
|
{ $$ = newLOOPOP(OPf_PARENS, 1, scalar($condition), $body); }
|
|
| expr[body] KW_UNTIL iexpr
|
|
{ $$ = newLOOPOP(OPf_PARENS, 1, $iexpr, $body); }
|
|
| expr[body] KW_FOR condition
|
|
{ $$ = newFOROP(0, NULL, $condition, $body, NULL);
|
|
parser->copline = (line_t)$KW_FOR; }
|
|
| expr[body] KW_WHEN condition
|
|
{ $$ = newWHENOP($condition, op_scope($body)); }
|
|
;
|
|
|
|
/* else and elsif blocks */
|
|
else
|
|
: empty
|
|
| KW_ELSE mblock
|
|
{
|
|
($mblock)->op_flags |= OPf_PARENS;
|
|
$$ = op_scope($mblock);
|
|
}
|
|
| KW_ELSIF PERLY_PAREN_OPEN mexpr PERLY_PAREN_CLOSE mblock else[else.recurse]
|
|
{ parser->copline = (line_t)$KW_ELSIF;
|
|
$$ = newCONDOP(0,
|
|
newSTATEOP(OPf_SPECIAL,NULL,$mexpr),
|
|
op_scope($mblock), $[else.recurse]);
|
|
PL_hints |= HINT_BLOCK_SCOPE;
|
|
}
|
|
;
|
|
|
|
/* Continue blocks */
|
|
cont
|
|
: empty
|
|
| KW_CONTINUE block
|
|
{ $$ = op_scope($block); }
|
|
;
|
|
|
|
/* Finally blocks */
|
|
finally : %empty
|
|
{ $$ = NULL; }
|
|
| KW_FINALLY block
|
|
{ $$ = op_scope($block); }
|
|
;
|
|
|
|
/* determine whether there are any new my declarations */
|
|
mintro : %empty
|
|
{ $$ = (PL_min_intro_pending &&
|
|
PL_max_intro_pending >= PL_min_intro_pending);
|
|
intro_my(); }
|
|
|
|
/* Normal expression */
|
|
nexpr
|
|
: empty
|
|
| sideff
|
|
;
|
|
|
|
/* Boolean expression */
|
|
texpr : %empty /* NULL means true */
|
|
{ YYSTYPE tmplval;
|
|
(void)scan_num("1", &tmplval);
|
|
$$ = tmplval.opval; }
|
|
| expr
|
|
;
|
|
|
|
/* Inverted boolean expression */
|
|
iexpr : expr
|
|
{ $$ = invert(scalar($expr)); }
|
|
;
|
|
|
|
/* Expression with its own lexical scope */
|
|
mexpr : expr
|
|
{ $$ = $expr; intro_my(); }
|
|
;
|
|
|
|
mnexpr : nexpr
|
|
{ $$ = $nexpr; intro_my(); }
|
|
;
|
|
|
|
formname: BAREWORD { $$ = $BAREWORD; }
|
|
| empty
|
|
;
|
|
|
|
startsub: %empty /* start a regular subroutine scope */
|
|
{ $$ = start_subparse(FALSE, 0);
|
|
SAVEFREESV(PL_compcv); }
|
|
|
|
;
|
|
|
|
startanonsub: %empty /* start an anonymous subroutine scope */
|
|
{ $$ = start_subparse(FALSE, CVf_ANON);
|
|
SAVEFREESV(PL_compcv); }
|
|
;
|
|
|
|
startanonmethod: %empty /* start an anonymous method scope */
|
|
{ $$ = start_subparse(FALSE, CVf_ANON|CVf_IsMETHOD);
|
|
SAVEFREESV(PL_compcv); }
|
|
;
|
|
|
|
startformsub: %empty /* start a format subroutine scope */
|
|
{ $$ = start_subparse(TRUE, 0);
|
|
SAVEFREESV(PL_compcv); }
|
|
;
|
|
|
|
/* Name of a subroutine - must be a bareword, could be special */
|
|
subname : BAREWORD
|
|
| PRIVATEREF
|
|
;
|
|
|
|
/* Subroutine prototype */
|
|
proto
|
|
: empty
|
|
| PROTOTYPE
|
|
;
|
|
|
|
/* Optional list of subroutine attributes */
|
|
subattrlist
|
|
: empty
|
|
| COLONATTR ATTRLIST
|
|
{
|
|
OP *attrlist = $ATTRLIST;
|
|
if(attrlist && !PL_parser->sig_seen)
|
|
attrlist = apply_builtin_cv_attributes(PL_compcv, attrlist);
|
|
$$ = attrlist;
|
|
}
|
|
| COLONATTR
|
|
{ $$ = NULL; }
|
|
;
|
|
|
|
/* List of attributes for some other kind of declaration (variables, classes) */
|
|
attrlist: COLONATTR ATTRLIST
|
|
{ $$ = $ATTRLIST; }
|
|
| COLONATTR
|
|
{ $$ = NULL; }
|
|
;
|
|
|
|
optattrlist
|
|
: empty
|
|
| attrlist
|
|
;
|
|
|
|
|
|
/* --------------------------------------
|
|
* subroutine signature parsing
|
|
*/
|
|
|
|
/* the '' or 'foo' part of a '$' or '@foo' etc signature variable */
|
|
sigvar: %empty
|
|
{ parser->in_my = 0; $$ = 0; }
|
|
| PRIVATEREF
|
|
{ parser->in_my = 0; $$ = $PRIVATEREF->op_targ; op_free($PRIVATEREF); }
|
|
;
|
|
|
|
sigslurpsigil:
|
|
PERLY_SNAIL
|
|
{ $$ = '@'; }
|
|
| PERLY_PERCENT_SIGN
|
|
{ $$ = '%'; }
|
|
|
|
/* @, %, @foo, %foo */
|
|
sigslurpelem: sigslurpsigil sigvar
|
|
{
|
|
subsignature_append_slurpy($sigslurpsigil, $sigvar);
|
|
$$ = NULL;
|
|
}
|
|
| sigslurpsigil sigvar ASSIGNOP
|
|
{
|
|
yyerror("A slurpy parameter may not have a default value");
|
|
}
|
|
| sigslurpsigil sigvar ASSIGNOP term
|
|
{
|
|
yyerror("A slurpy parameter may not have a default value");
|
|
}
|
|
;
|
|
|
|
optcolon: %empty
|
|
{ $$ = 0; }
|
|
| PERLY_COLON
|
|
{ $$ = ':'; }
|
|
;
|
|
|
|
/* subroutine signature scalar element: e.g. '$x', '$=', '$x = $default' */
|
|
sigscalarelem:
|
|
optcolon PERLY_DOLLAR sigvar
|
|
{
|
|
if($optcolon) {
|
|
PADNAME *pn = PadnamelistARRAY(PL_comppad_name)[$sigvar];
|
|
subsignature_append_named(PadnamePV(pn)+1, $sigvar, 0, NULL);
|
|
}
|
|
else
|
|
subsignature_append_positional($sigvar, 0, NULL);
|
|
$$ = NULL;
|
|
}
|
|
| optcolon PERLY_DOLLAR sigvar ASSIGNOP optsigscalardefault
|
|
{
|
|
if($optcolon) {
|
|
PADNAME *pn = PadnamelistARRAY(PL_comppad_name)[$sigvar];
|
|
subsignature_append_named(PadnamePV(pn)+1, $sigvar, $ASSIGNOP, $optsigscalardefault);
|
|
}
|
|
else
|
|
subsignature_append_positional($sigvar, $ASSIGNOP, $optsigscalardefault);
|
|
$$ = NULL;
|
|
}
|
|
;
|
|
|
|
optsigscalardefault:
|
|
%empty
|
|
{ $$ = newOP(OP_NULL, 0); }
|
|
| term
|
|
;
|
|
|
|
|
|
/* subroutine signature element: e.g. '$x = $default' or '%h' */
|
|
sigelem: sigscalarelem
|
|
{ parser->in_my = KEY_sigvar; $$ = $sigscalarelem; }
|
|
| sigslurpelem
|
|
{ parser->in_my = KEY_sigvar; $$ = $sigslurpelem; }
|
|
;
|
|
|
|
/* list of subroutine signature elements
|
|
* These parser tokens no longer emit anything; they are combined just for
|
|
* their side-effect on the parser structures. */
|
|
siglist:
|
|
siglist[list] PERLY_COMMA
|
|
| siglist[list] PERLY_COMMA sigelem[element]
|
|
| sigelem[element] %prec PREC_LOW
|
|
;
|
|
|
|
/* () or (....) */
|
|
optsiglist
|
|
: empty
|
|
| siglist
|
|
;
|
|
|
|
/* optional subroutine signature */
|
|
optsubsignature
|
|
: empty
|
|
| subsignature
|
|
;
|
|
|
|
/* Subroutine signature */
|
|
subsignature: PERLY_PAREN_OPEN subsigguts PERLY_PAREN_CLOSE
|
|
{ $$ = $subsigguts; }
|
|
|
|
subsigguts:
|
|
{
|
|
ENTER;
|
|
subsignature_start();
|
|
parser->in_my = KEY_sigvar;
|
|
}
|
|
optsiglist
|
|
{
|
|
if (!FEATURE_SIGNATURES_IS_ENABLED && !CvIsMETHOD(PL_compcv))
|
|
croak("Experimental "
|
|
"subroutine signatures not enabled");
|
|
|
|
/* We shouldn't get here otherwise */
|
|
$$ = subsignature_finish();
|
|
|
|
CvSIGNATURE_on(PL_compcv);
|
|
|
|
parser->in_my = 0;
|
|
/* tell the toker that attrributes can follow
|
|
* this sig, but only so that the toker
|
|
* can skip through any (illegal) trailing
|
|
* attribute text then give a useful error
|
|
* message about "attributes before sig",
|
|
* rather than falling over ina mess at
|
|
* unrecognised syntax.
|
|
*/
|
|
parser->expect = XATTRBLOCK;
|
|
parser->sig_seen = TRUE;
|
|
LEAVE;
|
|
}
|
|
;
|
|
|
|
/* Optional subroutine body (for named subroutine declaration) */
|
|
optsubbody
|
|
: subbody
|
|
| PERLY_SEMICOLON { $$ = NULL; }
|
|
;
|
|
|
|
|
|
/* Subroutine body (without signature) */
|
|
subbody: remember PERLY_BRACE_OPEN stmtseq PERLY_BRACE_CLOSE
|
|
{
|
|
if (parser->copline > (line_t)$PERLY_BRACE_OPEN)
|
|
parser->copline = (line_t)$PERLY_BRACE_OPEN;
|
|
$$ = block_end($remember, $stmtseq);
|
|
}
|
|
;
|
|
|
|
|
|
/* optional [ Subroutine body with optional signature ] (for named
|
|
* subroutine declaration) */
|
|
optsigsubbody
|
|
: sigsubbody
|
|
| PERLY_SEMICOLON { $$ = NULL; }
|
|
;
|
|
|
|
/* Subroutine body with optional signature */
|
|
sigsubbody: remember optsubsignature PERLY_BRACE_OPEN
|
|
{ PL_parser->sig_seen = FALSE; }
|
|
stmtseq PERLY_BRACE_CLOSE
|
|
{
|
|
if (parser->copline > (line_t)$PERLY_BRACE_OPEN)
|
|
parser->copline = (line_t)$PERLY_BRACE_OPEN;
|
|
$$ = block_end($remember,
|
|
op_append_list(OP_LINESEQ, $optsubsignature, $stmtseq));
|
|
}
|
|
;
|
|
|
|
|
|
/* Ordinary expressions; logical combinations */
|
|
expr : expr[lhs] ANDOP expr[rhs]
|
|
{ $$ = newLOGOP(OP_AND, 0, $lhs, $rhs); }
|
|
| expr[lhs] PLUGIN_LOGICAL_AND_LOW_OP[op] expr[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| expr[lhs] OROP[operator] expr[rhs]
|
|
{ $$ = newLOGOP($operator, 0, $lhs, $rhs); }
|
|
| expr[lhs] PLUGIN_LOGICAL_OR_LOW_OP[op] expr[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| listexpr %prec PREC_LOW
|
|
;
|
|
|
|
/* Expressions are a list of terms joined by commas */
|
|
listexpr: listexpr[list] PERLY_COMMA
|
|
{ $$ = $list; }
|
|
| listexpr[list] PERLY_COMMA term
|
|
{
|
|
OP* term = $term;
|
|
$$ = op_append_elem(OP_LIST, $list, term);
|
|
}
|
|
| term %prec PREC_LOW
|
|
;
|
|
|
|
/* List operators */
|
|
listop : LSTOP indirob listexpr /* map {...} @args or print $fh @args */
|
|
{ $$ = op_convert_list($LSTOP, OPf_STACKED,
|
|
op_prepend_elem(OP_LIST, newGVREF($LSTOP,$indirob), $listexpr) );
|
|
}
|
|
| BLKLSTOP block listexpr /* all/any { ... } @args */
|
|
{ $$ = op_convert_list($BLKLSTOP, OPf_STACKED,
|
|
op_prepend_elem(OP_LIST, newUNOP(OP_NULL, 0, op_scope($block)), $listexpr) );
|
|
}
|
|
| FUNC PERLY_PAREN_OPEN indirob expr PERLY_PAREN_CLOSE /* print ($fh @args */
|
|
{ $$ = op_convert_list($FUNC, OPf_STACKED,
|
|
op_prepend_elem(OP_LIST, newGVREF($FUNC,$indirob), $expr) );
|
|
}
|
|
| term ARROW methodname PERLY_PAREN_OPEN optexpr PERLY_PAREN_CLOSE /* $foo->bar(list) */
|
|
{ $$ = op_convert_list(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST,
|
|
op_prepend_elem(OP_LIST, scalar($term), $optexpr),
|
|
newMETHOP(OP_METHOD, 0, $methodname)));
|
|
}
|
|
| term ARROW methodname /* $foo->bar */
|
|
{ $$ = op_convert_list(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST, scalar($term),
|
|
newMETHOP(OP_METHOD, 0, $methodname)));
|
|
}
|
|
| term ARROW PERLY_AMPERSAND subname[method] PERLY_PAREN_OPEN optexpr PERLY_PAREN_CLOSE /* $foo->&bar(list) */
|
|
{ $$ = op_convert_list(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST,
|
|
op_prepend_elem(OP_LIST, scalar($term), $optexpr),
|
|
newCVREF(0, $method)));
|
|
}
|
|
| term ARROW PERLY_AMPERSAND subname[method] /* $foo->&bar */
|
|
{ $$ = op_convert_list(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST,
|
|
scalar($term),
|
|
newCVREF(0, $method)));
|
|
}
|
|
| METHCALL0 indirob optlistexpr /* new Class @args */
|
|
{ $$ = op_convert_list(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST,
|
|
op_prepend_elem(OP_LIST, $indirob, $optlistexpr),
|
|
newMETHOP(OP_METHOD, 0, $METHCALL0)));
|
|
}
|
|
| METHCALL indirob PERLY_PAREN_OPEN optexpr PERLY_PAREN_CLOSE /* method $object (@args) */
|
|
{ $$ = op_convert_list(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST,
|
|
op_prepend_elem(OP_LIST, $indirob, $optexpr),
|
|
newMETHOP(OP_METHOD, 0, $METHCALL)));
|
|
}
|
|
| LSTOP optlistexpr /* print @args */
|
|
{ $$ = op_convert_list($LSTOP, 0, $optlistexpr); }
|
|
| FUNC PERLY_PAREN_OPEN optexpr PERLY_PAREN_CLOSE /* print (@args) */
|
|
{ $$ = op_convert_list($FUNC, 0, $optexpr); }
|
|
| FUNC SUBLEXSTART optexpr SUBLEXEND /* uc($arg) from "\U..." */
|
|
{ $$ = op_convert_list($FUNC, 0, $optexpr); }
|
|
| LSTOPSUB startanonsub block /* sub f(&@); f { foo } ... */
|
|
{ SvREFCNT_inc_simple_void(PL_compcv);
|
|
$<opval>$ = newANONATTRSUB($startanonsub, 0, NULL, $block);
|
|
/* prevent double op_free() if the following fails to parse */
|
|
$block = NULL;
|
|
}[anonattrsub]
|
|
optlistexpr %prec LSTOP /* ... @bar */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST,
|
|
op_prepend_elem(OP_LIST, $<opval>anonattrsub, $optlistexpr), $LSTOPSUB));
|
|
}
|
|
;
|
|
|
|
/* Names of methods. May use $object->$methodname */
|
|
methodname: METHCALL0
|
|
| scalar
|
|
;
|
|
|
|
/* Some kind of subscripted expression */
|
|
subscripted: gelem PERLY_BRACE_OPEN expr PERLY_SEMICOLON PERLY_BRACE_CLOSE /* *main::{something} */
|
|
/* In this and all the hash accessors, PERLY_SEMICOLON is
|
|
* provided by the tokeniser */
|
|
{ $$ = newBINOP(OP_GELEM, 0, $gelem, scalar($expr)); }
|
|
| scalar[array] PERLY_BRACKET_OPEN expr PERLY_BRACKET_CLOSE /* $array[$element] */
|
|
{ $$ = newBINOP(OP_AELEM, 0, oopsAV($array), scalar($expr));
|
|
}
|
|
| term[array_reference] ARROW PERLY_BRACKET_OPEN expr PERLY_BRACKET_CLOSE /* somearef->[$element] */
|
|
{ $$ = newBINOP(OP_AELEM, 0,
|
|
ref(newAVREF($array_reference),OP_RV2AV),
|
|
scalar($expr));
|
|
}
|
|
| subscripted[array_reference] PERLY_BRACKET_OPEN expr PERLY_BRACKET_CLOSE /* $foo->[$bar]->[$baz] */
|
|
{ $$ = newBINOP(OP_AELEM, 0,
|
|
ref(newAVREF($array_reference),OP_RV2AV),
|
|
scalar($expr));
|
|
}
|
|
| scalar[hash] PERLY_BRACE_OPEN expr PERLY_SEMICOLON PERLY_BRACE_CLOSE /* $foo{bar();} */
|
|
{ $$ = newBINOP(OP_HELEM, 0, oopsHV($hash), jmaybe($expr));
|
|
}
|
|
| term[hash_reference] ARROW PERLY_BRACE_OPEN expr PERLY_SEMICOLON PERLY_BRACE_CLOSE /* somehref->{bar();} */
|
|
{ $$ = newBINOP(OP_HELEM, 0,
|
|
ref(newHVREF($hash_reference),OP_RV2HV),
|
|
jmaybe($expr)); }
|
|
| subscripted[hash_reference] PERLY_BRACE_OPEN expr PERLY_SEMICOLON PERLY_BRACE_CLOSE /* $foo->[bar]->{baz;} */
|
|
{ $$ = newBINOP(OP_HELEM, 0,
|
|
ref(newHVREF($hash_reference),OP_RV2HV),
|
|
jmaybe($expr)); }
|
|
| term[code_reference] ARROW PERLY_PAREN_OPEN PERLY_PAREN_CLOSE /* $subref->() */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
newCVREF(0, scalar($code_reference)));
|
|
if (parser->expect == XBLOCK)
|
|
parser->expect = XOPERATOR;
|
|
}
|
|
| term[code_reference] ARROW PERLY_PAREN_OPEN expr PERLY_PAREN_CLOSE /* $subref->(@args) */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST, $expr,
|
|
newCVREF(0, scalar($code_reference))));
|
|
if (parser->expect == XBLOCK)
|
|
parser->expect = XOPERATOR;
|
|
}
|
|
|
|
| subscripted[code_reference] PERLY_PAREN_OPEN expr PERLY_PAREN_CLOSE /* $foo->{bar}->(@args) */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST, $expr,
|
|
newCVREF(0, scalar($code_reference))));
|
|
if (parser->expect == XBLOCK)
|
|
parser->expect = XOPERATOR;
|
|
}
|
|
| subscripted[code_reference] PERLY_PAREN_OPEN PERLY_PAREN_CLOSE /* $foo->{bar}->() */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
newCVREF(0, scalar($code_reference)));
|
|
if (parser->expect == XBLOCK)
|
|
parser->expect = XOPERATOR;
|
|
}
|
|
| PERLY_PAREN_OPEN expr[list] PERLY_PAREN_CLOSE PERLY_BRACKET_OPEN expr[slice] PERLY_BRACKET_CLOSE /* list slice */
|
|
{ $$ = newSLICEOP(0, $slice, $list); }
|
|
| QWLIST PERLY_BRACKET_OPEN expr PERLY_BRACKET_CLOSE /* list literal slice */
|
|
{ $$ = newSLICEOP(0, $expr, $QWLIST); }
|
|
| PERLY_PAREN_OPEN PERLY_PAREN_CLOSE PERLY_BRACKET_OPEN expr PERLY_BRACKET_CLOSE /* empty list slice! */
|
|
{ $$ = newSLICEOP(0, $expr, NULL); }
|
|
;
|
|
|
|
/* Binary operators between terms */
|
|
termbinop: term[lhs] PLUGIN_HIGH_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| term[lhs] ASSIGNOP term[rhs] /* $x = $y, $x += $y */
|
|
{ $$ = newASSIGNOP(OPf_STACKED, $lhs, $ASSIGNOP, $rhs); }
|
|
| term[lhs] PLUGIN_ASSIGN_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| term[lhs] POWOP term[rhs] /* $x ** $y */
|
|
{ $$ = newBINOP($POWOP, 0, scalar($lhs), scalar($rhs)); }
|
|
| term[lhs] PLUGIN_POW_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| term[lhs] MULOP term[rhs] /* $x * $y, $x x $y */
|
|
{ if ($MULOP != OP_REPEAT)
|
|
scalar($lhs);
|
|
$$ = newBINOP($MULOP, 0, $lhs, scalar($rhs));
|
|
}
|
|
| term[lhs] PLUGIN_MUL_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| term[lhs] ADDOP term[rhs] /* $x + $y */
|
|
{ $$ = newBINOP($ADDOP, 0, scalar($lhs), scalar($rhs)); }
|
|
| term[lhs] PLUGIN_ADD_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| term[lhs] SHIFTOP term[rhs] /* $x >> $y, $x << $y */
|
|
{ $$ = newBINOP($SHIFTOP, 0, scalar($lhs), scalar($rhs)); }
|
|
| termrelop %prec PREC_LOW /* $x > $y, etc. */
|
|
{ $$ = $termrelop; }
|
|
| termeqop %prec PREC_LOW /* $x == $y, $x cmp $y */
|
|
{ $$ = $termeqop; }
|
|
| term[lhs] BITANDOP term[rhs] /* $x & $y */
|
|
{ $$ = newBINOP($BITANDOP, 0, scalar($lhs), scalar($rhs)); }
|
|
| term[lhs] BITOROP term[rhs] /* $x | $y */
|
|
{ $$ = newBINOP($BITOROP, 0, scalar($lhs), scalar($rhs)); }
|
|
| term[lhs] DOTDOT term[rhs] /* $x..$y, $x...$y */
|
|
{ $$ = newRANGE($DOTDOT, scalar($lhs), scalar($rhs)); }
|
|
| term[lhs] ANDAND term[rhs] /* $x && $y */
|
|
{ $$ = newLOGOP(OP_AND, 0, $lhs, $rhs); }
|
|
| term[lhs] PLUGIN_LOGICAL_AND_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| term[lhs] OROR term[rhs] /* $x || $y */
|
|
{ $$ = newLOGOP($OROR, 0, $lhs, $rhs); }
|
|
| term[lhs] PLUGIN_LOGICAL_OR_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
| term[lhs] DORDOR term[rhs] /* $x // $y */
|
|
{ $$ = newLOGOP(OP_DOR, 0, $lhs, $rhs); }
|
|
| term[lhs] MATCHOP term[rhs] /* $x =~ /$y/ */
|
|
{ $$ = bind_match($MATCHOP, $lhs, $rhs); }
|
|
| term[lhs] PLUGIN_LOW_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
;
|
|
|
|
termrelop: relopchain %prec PREC_LOW
|
|
{ $$ = cmpchain_finish($relopchain); }
|
|
| term[lhs] NCRELOP term[rhs]
|
|
{ $$ = newBINOP($NCRELOP, 0, scalar($lhs), scalar($rhs)); }
|
|
| termrelop NCRELOP
|
|
{ yyerror("syntax error"); YYERROR; }
|
|
| termrelop CHRELOP
|
|
{ yyerror("syntax error"); YYERROR; }
|
|
| term[lhs] PLUGIN_REL_OP[op] term[rhs]
|
|
{ $$ = build_infix_plugin($lhs, $rhs, $op); }
|
|
;
|
|
|
|
relopchain: term[lhs] CHRELOP term[rhs]
|
|
{ $$ = cmpchain_start($CHRELOP, $lhs, $rhs); }
|
|
| relopchain[lhs] CHRELOP term[rhs]
|
|
{ $$ = cmpchain_extend($CHRELOP, $lhs, $rhs); }
|
|
;
|
|
|
|
termeqop: eqopchain %prec PREC_LOW
|
|
{ $$ = cmpchain_finish($eqopchain); }
|
|
| term[lhs] NCEQOP term[rhs]
|
|
{ $$ = newBINOP($NCEQOP, 0, scalar($lhs), scalar($rhs)); }
|
|
| termeqop NCEQOP
|
|
{ yyerror("syntax error"); YYERROR; }
|
|
| termeqop CHEQOP
|
|
{ yyerror("syntax error"); YYERROR; }
|
|
;
|
|
|
|
eqopchain: term[lhs] CHEQOP term[rhs]
|
|
{ $$ = cmpchain_start($CHEQOP, $lhs, $rhs); }
|
|
| eqopchain[lhs] CHEQOP term[rhs]
|
|
{ $$ = cmpchain_extend($CHEQOP, $lhs, $rhs); }
|
|
;
|
|
|
|
/* Unary operators and terms */
|
|
termunop : PERLY_MINUS term %prec UMINUS /* -$x */
|
|
{ $$ = newUNOP(OP_NEGATE, 0, scalar($term)); }
|
|
| PERLY_PLUS term %prec UMINUS /* +$x */
|
|
{ $$ = $term; }
|
|
|
|
| PERLY_EXCLAMATION_MARK term /* !$x */
|
|
{ $$ = newUNOP(OP_NOT, 0, scalar($term)); }
|
|
| PERLY_TILDE term /* ~$x */
|
|
{ $$ = newUNOP($PERLY_TILDE, 0, scalar($term)); }
|
|
| term POSTINC /* $x++ */
|
|
{ $$ = newUNOP(OP_POSTINC, 0,
|
|
op_lvalue(scalar($term), OP_POSTINC)); }
|
|
| term POSTDEC /* $x-- */
|
|
{ $$ = newUNOP(OP_POSTDEC, 0,
|
|
op_lvalue(scalar($term), OP_POSTDEC));}
|
|
| term POSTJOIN /* implicit join after interpolated ->@ */
|
|
{ $$ = op_convert_list(OP_JOIN, 0,
|
|
op_append_elem(
|
|
OP_LIST,
|
|
newSVREF(scalar(
|
|
newSVOP(OP_CONST,0,
|
|
newSVpvs("\""))
|
|
)),
|
|
$term
|
|
));
|
|
}
|
|
| PREINC term /* ++$x */
|
|
{ $$ = newUNOP(OP_PREINC, 0,
|
|
op_lvalue(scalar($term), OP_PREINC)); }
|
|
| PREDEC term /* --$x */
|
|
{ $$ = newUNOP(OP_PREDEC, 0,
|
|
op_lvalue(scalar($term), OP_PREDEC)); }
|
|
|
|
;
|
|
|
|
/* Constructors for anonymous data */
|
|
anonymous
|
|
: PERLY_BRACKET_OPEN optexpr PERLY_BRACKET_CLOSE
|
|
{ $$ = newANONLIST($optexpr); }
|
|
| HASHBRACK optexpr PERLY_SEMICOLON PERLY_BRACE_CLOSE %prec PERLY_PAREN_OPEN /* { foo => "Bar" } */
|
|
{ $$ = newANONHASH($optexpr); }
|
|
| KW_SUB_anon startanonsub proto subattrlist subbody %prec PERLY_PAREN_OPEN
|
|
{ SvREFCNT_inc_simple_void(PL_compcv);
|
|
$$ = newANONATTRSUB($startanonsub, $proto, $subattrlist, $subbody); }
|
|
| KW_SUB_anon startanonsub proto subattrlist %prec PERLY_PAREN_OPEN
|
|
{ yyerror("Illegal declaration of anonymous subroutine"); YYERROR; }
|
|
| KW_SUB_anon_sig startanonsub subattrlist sigsubbody %prec PERLY_PAREN_OPEN
|
|
{ SvREFCNT_inc_simple_void(PL_compcv);
|
|
$$ = newANONATTRSUB($startanonsub, NULL, $subattrlist, $sigsubbody); }
|
|
| KW_SUB_anon_sig startanonsub subattrlist %prec PERLY_PAREN_OPEN
|
|
{ yyerror("Illegal declaration of anonymous subroutine"); YYERROR; }
|
|
| KW_METHOD_anon startanonmethod subattrlist sigsubbody %prec PERLY_PAREN_OPEN
|
|
{
|
|
SvREFCNT_inc_simple_void(PL_compcv);
|
|
$$ = newANONATTRSUB($startanonmethod, NULL, $subattrlist, $sigsubbody);
|
|
}
|
|
| KW_METHOD_anon startanonmethod subattrlist %prec PERLY_PAREN_OPEN
|
|
{ yyerror("Illegal declaration of anonymous subroutine"); YYERROR; }
|
|
;
|
|
|
|
/* Things called with "do" */
|
|
termdo : KW_DO term %prec UNIOP /* do $filename */
|
|
{ $$ = dofile($term, $KW_DO);}
|
|
| KW_DO block %prec PERLY_PAREN_OPEN /* do { code */
|
|
{ $$ = newUNOP(OP_NULL, OPf_SPECIAL, op_scope($block));}
|
|
;
|
|
|
|
term[product] : termbinop
|
|
| termunop
|
|
| anonymous
|
|
| termdo
|
|
| term[condition] PERLY_QUESTION_MARK term[then] PERLY_COLON term[else]
|
|
{ $$ = newCONDOP(0, $condition, $then, $else); }
|
|
| REFGEN term[operand] /* \$x, \@y, \%z */
|
|
{ $$ = newUNOP(OP_REFGEN, 0, $operand); }
|
|
| myattrterm %prec UNIOP
|
|
{ $$ = $myattrterm; }
|
|
| KW_LOCAL term[operand] %prec UNIOP
|
|
{ $$ = localize($operand,0); }
|
|
| PERLY_PAREN_OPEN expr PERLY_PAREN_CLOSE
|
|
{ $$ = sawparens($expr); }
|
|
| QWLIST
|
|
{ $$ = $QWLIST; }
|
|
| PERLY_PAREN_OPEN PERLY_PAREN_CLOSE
|
|
{ $$ = sawparens(newNULLLIST()); }
|
|
| scalar %prec PERLY_PAREN_OPEN
|
|
{ $$ = $scalar; }
|
|
| star %prec PERLY_PAREN_OPEN
|
|
{ $$ = $star; }
|
|
| hsh %prec PERLY_PAREN_OPEN
|
|
{ $$ = $hsh; }
|
|
| ary %prec PERLY_PAREN_OPEN
|
|
{ $$ = $ary; }
|
|
| arylen %prec PERLY_PAREN_OPEN /* $#x, $#{ something } */
|
|
{ $$ = newUNOP(OP_AV2ARYLEN, 0, ref($arylen, OP_AV2ARYLEN));}
|
|
| subscripted
|
|
{ $$ = $subscripted; }
|
|
| sliceme PERLY_BRACKET_OPEN expr PERLY_BRACKET_CLOSE /* array slice */
|
|
{ $$ = op_prepend_elem(OP_ASLICE,
|
|
newOP(OP_PUSHMARK, 0),
|
|
newLISTOP(OP_ASLICE, 0,
|
|
list($expr),
|
|
ref($sliceme, OP_ASLICE)));
|
|
if ($$ && $sliceme)
|
|
$$->op_private |=
|
|
$sliceme->op_private & OPpSLICEWARNING;
|
|
}
|
|
| kvslice PERLY_BRACKET_OPEN expr PERLY_BRACKET_CLOSE /* array key/value slice */
|
|
{ $$ = op_prepend_elem(OP_KVASLICE,
|
|
newOP(OP_PUSHMARK, 0),
|
|
newLISTOP(OP_KVASLICE, 0,
|
|
list($expr),
|
|
ref(oopsAV($kvslice), OP_KVASLICE)));
|
|
if ($$ && $kvslice)
|
|
$$->op_private |=
|
|
$kvslice->op_private & OPpSLICEWARNING;
|
|
}
|
|
| sliceme PERLY_BRACE_OPEN expr PERLY_SEMICOLON PERLY_BRACE_CLOSE /* @hash{@keys} */
|
|
{ $$ = op_prepend_elem(OP_HSLICE,
|
|
newOP(OP_PUSHMARK, 0),
|
|
newLISTOP(OP_HSLICE, 0,
|
|
list($expr),
|
|
ref(oopsHV($sliceme), OP_HSLICE)));
|
|
if ($$ && $sliceme)
|
|
$$->op_private |=
|
|
$sliceme->op_private & OPpSLICEWARNING;
|
|
}
|
|
| kvslice PERLY_BRACE_OPEN expr PERLY_SEMICOLON PERLY_BRACE_CLOSE /* %hash{@keys} */
|
|
{ $$ = op_prepend_elem(OP_KVHSLICE,
|
|
newOP(OP_PUSHMARK, 0),
|
|
newLISTOP(OP_KVHSLICE, 0,
|
|
list($expr),
|
|
ref($kvslice, OP_KVHSLICE)));
|
|
if ($$ && $kvslice)
|
|
$$->op_private |=
|
|
$kvslice->op_private & OPpSLICEWARNING;
|
|
}
|
|
| THING %prec PERLY_PAREN_OPEN
|
|
{ $$ = $THING; }
|
|
| amper /* &foo; */
|
|
{ $$ = newUNOP(OP_ENTERSUB, 0, scalar($amper)); }
|
|
| amper PERLY_PAREN_OPEN PERLY_PAREN_CLOSE /* &foo() or foo() */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED, scalar($amper));
|
|
}
|
|
| amper PERLY_PAREN_OPEN expr PERLY_PAREN_CLOSE /* &foo(@args) or foo(@args) */
|
|
{
|
|
$$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST, $expr, scalar($amper)));
|
|
}
|
|
| NOAMP subname optlistexpr /* foo @args (no parens) */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST, $optlistexpr, scalar($subname)));
|
|
}
|
|
| term[operand] ARROW PERLY_DOLLAR PERLY_STAR
|
|
{ $$ = newSVREF($operand); }
|
|
| term[operand] ARROW PERLY_SNAIL PERLY_STAR
|
|
{ $$ = newAVREF($operand); }
|
|
| term[operand] ARROW PERLY_PERCENT_SIGN PERLY_STAR
|
|
{ $$ = newHVREF($operand); }
|
|
| term[operand] ARROW PERLY_AMPERSAND PERLY_STAR
|
|
{ $$ = newUNOP(OP_ENTERSUB, 0,
|
|
scalar(newCVREF($PERLY_AMPERSAND,$operand))); }
|
|
| term[operand] ARROW PERLY_STAR PERLY_STAR %prec PERLY_PAREN_OPEN
|
|
{ $$ = newGVREF(0,$operand); }
|
|
| LOOPEX /* loop exiting command (goto, last, dump, etc) */
|
|
{ $$ = newOP($LOOPEX, OPf_SPECIAL);
|
|
PL_hints |= HINT_BLOCK_SCOPE; }
|
|
| LOOPEX term[operand]
|
|
{ $$ = newLOOPEX($LOOPEX,$operand); }
|
|
| NOTOP listexpr /* not $foo */
|
|
{ $$ = newUNOP(OP_NOT, 0, scalar($listexpr)); }
|
|
| UNIOP /* Unary op, $_ implied */
|
|
{ $$ = newOP($UNIOP, 0); }
|
|
| UNIOP block /* eval { foo }* */
|
|
{ $$ = newUNOP($UNIOP, 0, $block); }
|
|
| UNIOP term[operand] /* Unary op */
|
|
{ $$ = newUNOP($UNIOP, 0, $operand); }
|
|
| KW_REQUIRE /* require, $_ implied */
|
|
{ $$ = newOP(OP_REQUIRE, $KW_REQUIRE ? OPf_SPECIAL : 0); }
|
|
| KW_REQUIRE term[operand] /* require Foo */
|
|
{ $$ = newUNOP(OP_REQUIRE, $KW_REQUIRE ? OPf_SPECIAL : 0, $operand); }
|
|
| UNIOPSUB
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED, scalar($UNIOPSUB)); }
|
|
| UNIOPSUB term[operand] /* Sub treated as unop */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
|
|
op_append_elem(OP_LIST, $operand, scalar($UNIOPSUB))); }
|
|
| FUNC0 /* Nullary operator */
|
|
{ $$ = newOP($FUNC0, 0); }
|
|
| FUNC0 PERLY_PAREN_OPEN PERLY_PAREN_CLOSE
|
|
{ $$ = newOP($FUNC0, 0);}
|
|
| FUNC0OP /* Same as above, but op created in toke.c */
|
|
{ $$ = $FUNC0OP; }
|
|
| FUNC0OP PERLY_PAREN_OPEN PERLY_PAREN_CLOSE
|
|
{ $$ = $FUNC0OP; }
|
|
| FUNC0SUB /* Sub treated as nullop */
|
|
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED, scalar($FUNC0SUB)); }
|
|
| FUNC1 PERLY_PAREN_OPEN PERLY_PAREN_CLOSE /* not () */
|
|
{ $$ = ($FUNC1 == OP_NOT)
|
|
? newUNOP($FUNC1, 0, newSVOP(OP_CONST, 0, newSViv(0)))
|
|
: newOP($FUNC1, OPf_SPECIAL); }
|
|
| FUNC1 PERLY_PAREN_OPEN expr PERLY_PAREN_CLOSE /* not($foo) */
|
|
{ $$ = newUNOP($FUNC1, 0, $expr); }
|
|
| PMFUNC /* m//, s///, qr//, tr/// */
|
|
{
|
|
if ( $PMFUNC->op_type != OP_TRANS
|
|
&& $PMFUNC->op_type != OP_TRANSR
|
|
&& (((PMOP*)$PMFUNC)->op_pmflags & PMf_HAS_CV))
|
|
{
|
|
$<ival>$ = start_subparse(FALSE, CVf_ANON);
|
|
SAVEFREESV(PL_compcv);
|
|
} else
|
|
$<ival>$ = 0;
|
|
}
|
|
SUBLEXSTART listexpr optrepl SUBLEXEND
|
|
{ $$ = pmruntime($PMFUNC, $listexpr, $optrepl, 1, $<ival>2); }
|
|
| BAREWORD
|
|
| listop
|
|
| PLUGEXPR
|
|
;
|
|
|
|
/* "my" declarations, with optional attributes */
|
|
myattrterm
|
|
: KW_MY myterm attrlist
|
|
{ $$ = my_attrs($myterm,$attrlist); }
|
|
| KW_MY myterm
|
|
{ $$ = localize($myterm,1); }
|
|
| KW_MY REFGEN myterm attrlist
|
|
{ $$ = newUNOP(OP_REFGEN, 0, my_attrs($myterm,$attrlist)); }
|
|
| KW_MY REFGEN term[operand]
|
|
{ $$ = newUNOP(OP_REFGEN, 0, localize($operand,1)); }
|
|
;
|
|
|
|
/* Things that can be "my"'d */
|
|
myterm : PERLY_PAREN_OPEN expr PERLY_PAREN_CLOSE
|
|
{ $$ = sawparens($expr); }
|
|
| PERLY_PAREN_OPEN PERLY_PAREN_CLOSE
|
|
{ $$ = sawparens(newNULLLIST()); }
|
|
|
|
| scalar %prec PERLY_PAREN_OPEN
|
|
{ $$ = $scalar; }
|
|
| hsh %prec PERLY_PAREN_OPEN
|
|
{ $$ = $hsh; }
|
|
| ary %prec PERLY_PAREN_OPEN
|
|
{ $$ = $ary; }
|
|
;
|
|
|
|
/* "field" declarations */
|
|
fieldvar: scalar %prec PERLY_PAREN_OPEN
|
|
{
|
|
$$ = PadnamelistARRAY(PL_comppad_name)[$scalar->op_targ];
|
|
op_free($scalar);
|
|
}
|
|
| hsh %prec PERLY_PAREN_OPEN
|
|
{
|
|
$$ = PadnamelistARRAY(PL_comppad_name)[$hsh->op_targ];
|
|
op_free($hsh);
|
|
}
|
|
| ary %prec PERLY_PAREN_OPEN
|
|
{
|
|
$$ = PadnamelistARRAY(PL_comppad_name)[$ary->op_targ];
|
|
op_free($ary);
|
|
}
|
|
;
|
|
|
|
fielddecl
|
|
: KW_FIELD fieldvar optattrlist
|
|
{
|
|
parser->in_my = 0;
|
|
if($optattrlist)
|
|
class_apply_field_attributes((PADNAME *)$fieldvar, $optattrlist);
|
|
$$ = newOP(OP_NULL, 0);
|
|
}
|
|
| KW_FIELD fieldvar optattrlist ASSIGNOP
|
|
{
|
|
parser->in_my = 0;
|
|
if($optattrlist)
|
|
class_apply_field_attributes((PADNAME *)$fieldvar, $optattrlist);
|
|
ENTER;
|
|
class_prepare_initfield_parse();
|
|
}
|
|
term
|
|
{
|
|
class_set_field_defop((PADNAME *)$fieldvar, $ASSIGNOP, $term);
|
|
LEAVE;
|
|
$$ = newOP(OP_NULL, 0);
|
|
}
|
|
;
|
|
|
|
/* Basic list expressions */
|
|
optlistexpr
|
|
: empty %prec PREC_LOW
|
|
| listexpr %prec PREC_LOW
|
|
;
|
|
|
|
optexpr
|
|
: empty
|
|
| expr
|
|
;
|
|
|
|
optrepl
|
|
: empty
|
|
| PERLY_SLASH expr { $$ = $expr; }
|
|
;
|
|
|
|
/* A little bit of trickery to make "for my $foo (@bar)" actually be
|
|
lexical */
|
|
my_scalar: scalar
|
|
{ parser->in_my = 0; $$ = my($scalar); }
|
|
;
|
|
|
|
/* A list of scalars for "for my ($foo, $bar) (@baz)" */
|
|
list_of_itervars: list_of_itervars[list] PERLY_COMMA
|
|
{ $$ = $list; }
|
|
| list_of_itervars[list] PERLY_COMMA scalar
|
|
{
|
|
$$ = op_append_elem(OP_LIST, $list, $scalar);
|
|
}
|
|
| list_of_itervars[list] PERLY_COMMA REFGEN my_var
|
|
{
|
|
$$ = op_append_elem(OP_LIST, $list, newUNOP(OP_REFGEN, 0, $my_var));
|
|
}
|
|
| scalar %prec PREC_LOW
|
|
| REFGEN my_var %prec PREC_LOW
|
|
{
|
|
$$ = newUNOP(OP_REFGEN, 0, $my_var);
|
|
}
|
|
;
|
|
|
|
my_list_of_itervars: list_of_itervars
|
|
{ parser->in_my = 0; $$ = $list_of_itervars; }
|
|
;
|
|
|
|
my_var : scalar
|
|
| ary
|
|
| hsh
|
|
;
|
|
|
|
refgen_topic: my_var
|
|
| amper
|
|
;
|
|
|
|
my_refgen: KW_MY REFGEN
|
|
| REFGEN KW_MY
|
|
;
|
|
|
|
amper : PERLY_AMPERSAND indirob
|
|
{ $$ = newCVREF($PERLY_AMPERSAND,$indirob); }
|
|
;
|
|
|
|
scalar : PERLY_DOLLAR indirob
|
|
{ $$ = newSVREF($indirob); }
|
|
;
|
|
|
|
ary : PERLY_SNAIL indirob
|
|
{ $$ = newAVREF($indirob);
|
|
if ($$) $$->op_private |= $PERLY_SNAIL;
|
|
}
|
|
;
|
|
|
|
hsh : PERLY_PERCENT_SIGN indirob
|
|
{ $$ = newHVREF($indirob);
|
|
if ($$) $$->op_private |= $PERLY_PERCENT_SIGN;
|
|
}
|
|
;
|
|
|
|
arylen : DOLSHARP indirob
|
|
{ $$ = newAVREF($indirob); }
|
|
| term ARROW DOLSHARP PERLY_STAR
|
|
{ $$ = newAVREF($term); }
|
|
;
|
|
|
|
star : PERLY_STAR indirob
|
|
{ $$ = newGVREF(0,$indirob); }
|
|
;
|
|
|
|
sliceme : ary
|
|
| term ARROW PERLY_SNAIL
|
|
{ $$ = newAVREF($term); }
|
|
;
|
|
|
|
kvslice : hsh
|
|
| term ARROW PERLY_PERCENT_SIGN
|
|
{ $$ = newHVREF($term); }
|
|
;
|
|
|
|
gelem : star
|
|
| term ARROW PERLY_STAR
|
|
{ $$ = newGVREF(0,$term); }
|
|
;
|
|
|
|
/* Indirect objects */
|
|
indirob : BAREWORD
|
|
{ $$ = scalar($BAREWORD); }
|
|
| scalar %prec PREC_LOW
|
|
{ $$ = scalar($scalar); }
|
|
| block
|
|
{ $$ = op_scope($block); }
|
|
|
|
| PRIVATEREF
|
|
{ $$ = $PRIVATEREF; }
|
|
;
|