/* perly.y
*
- * Copyright (c) 1991-2002, Larry Wall
+ * Copyright (c) 1991-2002, 2003, 2004 Larry Wall
*
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the README file.
* All that is gold does not glitter, not all those who wander are lost.'
*/
-%{
-#include "EXTERN.h"
-#define PERL_IN_PERLY_C
-#include "perl.h"
-#ifdef EBCDIC
-#undef YYDEBUG
-#endif
-#define dep() deprecate("\"do\" to call subroutines")
-
-/* stuff included here to make perly_c.diff apply better */
-
-#define yydebug PL_yydebug
-#define yynerrs PL_yynerrs
-#define yyerrflag PL_yyerrflag
-#define yychar PL_yychar
-#define yyval PL_yyval
-#define yylval PL_yylval
-
-struct ysv {
- short* yyss;
- YYSTYPE* yyvs;
- int oldyydebug;
- int oldyynerrs;
- int oldyyerrflag;
- int oldyychar;
- YYSTYPE oldyyval;
- YYSTYPE oldyylval;
-};
+/* 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 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.
+ */
-static void yydestruct(pTHX_ void *ptr);
+/* Make the parser re-entrant. */
-%}
+%pure_parser
%start prog
-%{
-#if 0 /* get this from perly.h instead */
-%}
-
%union {
I32 ival;
char *pval;
+#ifdef PERL_MAD
+ TOKEN* tkval;
+#endif
OP *opval;
GV *gvval;
}
-%{
-#endif /* 0 */
-
-#ifdef USE_PURE_BISON
-#define YYLEX_PARAM (&yychar)
-#define yylex yylex_r
-#endif
-
-%}
-
%token <ival> '{'
%token <opval> WORD METHOD FUNCMETH THING PMFUNC PRIVATEREF
%token <pval> LABEL
%token <ival> FORMAT SUB ANONSUB PACKAGE USE
%token <ival> WHILE UNTIL IF UNLESS ELSE ELSIF CONTINUE FOR
+%token <ival> GIVEN WHEN DEFAULT
%token <ival> LOOPEX DOTDOT
%token <ival> FUNC0 FUNC1 FUNC UNIOP LSTOP
%token <ival> RELOP EQOP MULOP ADDOP
%token <ival> DOLSHARP DO HASHBRACK NOAMP
-%token <ival> LOCAL MY MYSUB
+%token <ival> LOCAL MY MYSUB REQUIRE
%token COLONATTR
-%type <ival> prog decl format startsub startanonsub startformsub
-%type <ival> progstart remember mremember '&'
+%type <ival> prog decl format startsub startanonsub startformsub mintro
+%type <ival> progstart remember mremember '&' savescope mydefsv
%type <opval> block mblock lineseq line loop cond else
%type <opval> expr term subscripted scalar ary hsh arylen star amper sideff
-%type <opval> argexpr nexpr texpr iexpr mexpr mnexpr mtexpr miexpr
+%type <opval> argexpr nexpr texpr iexpr mexpr mnexpr miexpr
%type <opval> listexpr listexprcom indirob listop method
%type <opval> formname subname proto subbody cont my_scalar
%type <opval> subattrlist myattrlist mysubrout myattrterm myterm
%type <opval> termbinop termunop anonymous termdo
+%type <opval> switch case
%type <pval> label
%nonassoc PREC_LOW
%nonassoc EQOP
%nonassoc RELOP
%nonassoc UNIOP UNIOPSUB
+%nonassoc REQUIRE
%left <ival> SHIFTOP
%left ADDOP
%left MULOP
%left '('
%left '[' '{'
+%token PEG
+
%% /* RULES */
/* The whole program */
{ $$ = block_start(TRUE); }
;
+mydefsv: /* NULL */ /* lexicalize $_ */
+ { $$ = (I32) allocmy("$_"); }
+ ;
+
progstart:
{
-#if defined(YYDEBUG) && defined(DEBUGGING)
- yydebug = (DEBUG_p_TEST);
-#endif
PL_expect = XSTATE; $$ = block_start(TRUE);
}
;
{ $$ = block_start(FALSE); }
;
+savescope: /* NULL */ /* remember stack pos in case of error */
+ { $$ = PL_savestack_ix; }
+
/* A collection of "lines" in the program */
lineseq : /* NULL */
{ $$ = Nullop; }
| lineseq decl
{ $$ = $1; }
- | lineseq line
- { $$ = append_list(OP_LINESEQ,
- (LISTOP*)$1, (LISTOP*)$2);
+ | lineseq savescope line
+ { LEAVE_SCOPE($2);
+ $$ = append_list(OP_LINESEQ,
+ (LISTOP*)$1, (LISTOP*)$3);
PL_pad_reset_pending = TRUE;
- if ($1 && $2) PL_hints |= HINT_BLOCK_SCOPE; }
+ if ($1 && $3) PL_hints |= HINT_BLOCK_SCOPE; }
;
/* A "line" in the program */
line : label cond
{ $$ = newSTATEOP(0, $1, $2); }
| loop /* loops add their own labels */
+ | switch /* ... and so do switches */
+ { $$ = $1; }
+ | label case
+ { $$ = newSTATEOP(0, $1, $2); }
| label ';'
{ if ($1 != Nullch) {
$$ = newSTATEOP(0, $1, newOP(OP_NULL, 0));
newCONDOP(0, $4, scope($6), $7)); }
;
+/* Cases for a switch statement */
+case : WHEN '(' remember mexpr ')' mblock
+ { $$ = block_end($3,
+ newWHENOP($4, scope($6))); }
+ | DEFAULT block
+ { $$ = newWHENOP(0, scope($2)); }
+ ;
+
/* Continue blocks */
cont : /* NULL */
{ $$ = Nullop; }
;
/* Loops: while, until, for, and a bare block */
-loop : label WHILE '(' remember mtexpr ')' mblock cont
+loop : label WHILE '(' remember texpr ')' mintro mblock cont
{ PL_copline = (line_t)$2;
$$ = block_end($4,
newSTATEOP(0, $1,
newWHILEOP(0, 1, (LOOP*)Nullop,
- $2, $5, $7, $8))); }
- | label UNTIL '(' remember miexpr ')' mblock cont
+ $2, $5, $8, $9, $7))); }
+ | label UNTIL '(' remember iexpr ')' mintro mblock cont
{ PL_copline = (line_t)$2;
$$ = block_end($4,
newSTATEOP(0, $1,
newWHILEOP(0, 1, (LOOP*)Nullop,
- $2, $5, $7, $8))); }
+ $2, $5, $8, $9, $7))); }
| label FOR MY remember my_scalar '(' mexpr ')' mblock cont
{ $$ = block_end($4,
newFOROP(0, $1, (line_t)$2, $5, $7, $9, $10)); }
| label FOR '(' remember mexpr ')' mblock cont
{ $$ = block_end($4,
newFOROP(0, $1, (line_t)$2, Nullop, $5, $7, $8)); }
- | label FOR '(' remember mnexpr ';' mtexpr ';' mnexpr ')' mblock
+ | label FOR '(' remember mnexpr ';' texpr ';' mintro mnexpr ')'
+ mblock
/* basically fake up an initialize-while lineseq */
{ OP *forop;
PL_copline = (line_t)$2;
forop = newSTATEOP(0, $1,
newWHILEOP(0, 1, (LOOP*)Nullop,
$2, scalar($7),
- $11, $9));
+ $12, $10, $9));
if ($5) {
forop = append_elem(OP_LINESEQ,
newSTATEOP(0, ($1?savepv($1):Nullch),
| label block cont /* a block is a loop that happens once */
{ $$ = newSTATEOP(0, $1,
newWHILEOP(0, 1, (LOOP*)Nullop,
- NOLINE, Nullop, $2, $3)); }
+ NOLINE, Nullop, $2, $3, 0)); }
;
+/* Switch blocks */
+switch : label GIVEN '(' remember mydefsv mexpr ')' mblock
+ { PL_copline = (line_t) $2;
+ $$ = block_end($4,
+ newSTATEOP(0, $1,
+ newGIVENOP($6, scope($8),
+ (PADOFFSET) $5) )); }
+ ;
+
+/* determine whether there are any new my declarations */
+mintro : /* NULL */
+ { $$ = (PL_min_intro_pending &&
+ PL_max_intro_pending >= PL_min_intro_pending);
+ intro_my(); }
+
/* Normal expression */
nexpr : /* NULL */
{ $$ = Nullop; }
{ $$ = $1; intro_my(); }
;
-mtexpr : texpr
- { $$ = $1; intro_my(); }
- ;
-
miexpr : iexpr
{ $$ = $1; intro_my(); }
;
;
/* Name of a subroutine - must be a bareword, could be special */
-subname : WORD { STRLEN n_a; char *name = SvPV(((SVOP*)$1)->op_sv,n_a);
+subname : WORD { const char *const name = SvPV_nolen_const(((SVOP*)$1)->op_sv);
if (strEQ(name, "BEGIN") || strEQ(name, "END")
|| strEQ(name, "INIT") || strEQ(name, "CHECK"))
CvSPECIAL_on(PL_compcv);
;
/* List operators */
-listop : LSTOP indirob argexpr /* print $fh @args */
+listop : LSTOP indirob argexpr /* map {...} @args or print $fh @args */
{ $$ = convert($1, OPf_STACKED,
prepend_elem(OP_LIST, newGVREF($1,$2), $3) ); }
| FUNC '(' indirob expr ')' /* print ($fh @args */
{ $$ = convert($1, 0, $2); }
| FUNC '(' listexprcom ')' /* print (@args) */
{ $$ = convert($1, 0, $3); }
- | LSTOPSUB startanonsub block /* map { foo } ... */
+ | LSTOPSUB startanonsub block /* sub f(&@); f { foo } ... */
{ $3 = newANONATTRSUB($2, 0, Nullop, $3); }
listexpr %prec LSTOP /* ... @bar */
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
| subscripted '(' ')' /* $foo->{bar}->() */
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
newCVREF(0, scalar($1))); }
+ | '(' expr ')' '[' expr ']' /* list slice */
+ { $$ = newSLICEOP(0, $5, $2); }
+ | '(' ')' '[' expr ']' /* empty list slice! */
+ { $$ = newSLICEOP(0, $4, Nullop); }
;
/* Binary operators between terms */
/* Things called with "do" */
termdo : DO term %prec UNIOP /* do $filename */
- { $$ = dofile($2); }
+ { $$ = dofile($2, $1); }
| DO block %prec '(' /* do { code */
{ $$ = newUNOP(OP_NULL, OPf_SPECIAL, scope($2)); }
| DO WORD '(' ')' /* do somesub() */
{ $$ = newUNOP(OP_AV2ARYLEN, 0, ref($1, OP_AV2ARYLEN));}
| subscripted
{ $$ = $1; }
- | '(' expr ')' '[' expr ']' /* list slice */
- { $$ = newSLICEOP(0, $5, $2); }
- | '(' ')' '[' expr ']' /* empty list slice! */
- { $$ = newSLICEOP(0, $4, Nullop); }
| ary '[' expr ']' /* array slice */
{ $$ = prepend_elem(OP_ASLICE,
newOP(OP_PUSHMARK, 0),
{ $$ = newUNOP(OP_NOT, 0, scalar($2)); }
| UNIOP /* Unary op, $_ implied */
{ $$ = newOP($1, 0); }
- | UNIOP block /* eval { foo }, I *think* */
+ | UNIOP block /* eval { foo } */
{ $$ = newUNOP($1, 0, $2); }
| UNIOP term /* Unary op */
{ $$ = newUNOP($1, 0, $2); }
+ | REQUIRE /* require, $_ implied */
+ { $$ = newOP(OP_REQUIRE, $1 ? OPf_SPECIAL : 0); }
+ | REQUIRE term /* require Foo */
+ { $$ = newUNOP(OP_REQUIRE, $1 ? OPf_SPECIAL : 0, $2); }
| UNIOPSUB term /* Sub treated as unop */
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
append_elem(OP_LIST, $2, scalar($1))); }
{ $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
scalar($1)); }
| FUNC1 '(' ')' /* not () */
- { $$ = newOP($1, OPf_SPECIAL); }
+ { $$ = $1 == OP_NOT ? newUNOP($1, 0, newSVOP(OP_CONST, 0, newSViv(0)))
+ : newOP($1, OPf_SPECIAL); }
| FUNC1 '(' expr ')' /* not($foo) */
{ $$ = newUNOP($1, 0, $3); }
- | PMFUNC '(' term ')' /* split (/foo/) */
- { $$ = pmruntime($1, $3, Nullop); }
- | PMFUNC '(' term ',' term ')' /* split (/foo/,$bar) */
- { $$ = pmruntime($1, $3, $5); }
+ | PMFUNC '(' argexpr ')' /* m//, s///, tr/// */
+ { $$ = pmruntime($1, $3, 1); }
| WORD
| listop
;
| PRIVATEREF
{ $$ = $1; }
;
-
-%% /* PROGRAM */
-
-/* more stuff added to make perly_c.diff easier to apply */
-
-#ifdef yyparse
-#undef yyparse
-#endif
-#define yyparse() Perl_yyparse(pTHX)
-