+/* Subroutine body - block with optional signature */
+realsubbody: remember subsignature '{' stmtseq '}'
+ {
+ if (PL_parser->copline > (line_t)IVAL($3))
+ PL_parser->copline = (line_t)IVAL($3);
+ $$ = block_end($1,
+ op_append_list(OP_LINESEQ, $2, $4));
+ TOKEN_GETMAD($3,$$,'{');
+ TOKEN_GETMAD($5,$$,'}');
+ }
+ ;
+
+/* Optional subroutine body, for named subroutine declaration */
+optsubbody: realsubbody { $$ = $1; }
+ | ';' { $$ = IF_MAD(
+ newOP(OP_NULL,0),
+ (OP*)NULL
+ );
+ PL_parser->expect = XSTATE;
+ TOKEN_GETMAD($1,$$,';');
+ }
+ ;
+
+/* Ordinary expressions; logical combinations */
+expr : expr ANDOP expr
+ { $$ = newLOGOP(OP_AND, 0, $1, $3);
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | expr OROP expr
+ { $$ = newLOGOP(IVAL($2), 0, $1, $3);
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | expr DOROP expr
+ { $$ = newLOGOP(OP_DOR, 0, $1, $3);
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | listexpr %prec PREC_LOW
+ ;
+
+/* Expressions are a list of terms joined by commas */
+listexpr: listexpr ','
+ {
+#ifdef MAD
+ OP* op = newNULLLIST();
+ token_getmad($2,op,',');
+ $$ = op_append_elem(OP_LIST, $1, op);
+#else
+ $$ = $1;
+#endif
+ }
+ | listexpr ',' term
+ {
+ OP* term = $3;
+ DO_MAD(
+ term = newUNOP(OP_NULL, 0, term);
+ token_getmad($2,term,',');
+ )
+ $$ = op_append_elem(OP_LIST, $1, term);
+ }
+ | term %prec PREC_LOW
+ ;
+
+/* List operators */
+listop : LSTOP indirob listexpr /* map {...} @args or print $fh @args */
+ { $$ = convert(IVAL($1), OPf_STACKED,
+ op_prepend_elem(OP_LIST, newGVREF(IVAL($1),$2), $3) );
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | FUNC '(' indirob expr ')' /* print ($fh @args */
+ { $$ = convert(IVAL($1), OPf_STACKED,
+ op_prepend_elem(OP_LIST, newGVREF(IVAL($1),$3), $4) );
+ TOKEN_GETMAD($1,$$,'o');
+ TOKEN_GETMAD($2,$$,'(');
+ TOKEN_GETMAD($5,$$,')');
+ }
+ | term ARROW method '(' optexpr ')' /* $foo->bar(list) */
+ { $$ = convert(OP_ENTERSUB, OPf_STACKED,
+ op_append_elem(OP_LIST,
+ op_prepend_elem(OP_LIST, scalar($1), $5),
+ newUNOP(OP_METHOD, 0, $3)));
+ TOKEN_GETMAD($2,$$,'A');
+ TOKEN_GETMAD($4,$$,'(');
+ TOKEN_GETMAD($6,$$,')');
+ }
+ | term ARROW method /* $foo->bar */
+ { $$ = convert(OP_ENTERSUB, OPf_STACKED,
+ op_append_elem(OP_LIST, scalar($1),
+ newUNOP(OP_METHOD, 0, $3)));
+ TOKEN_GETMAD($2,$$,'A');
+ }
+ | METHOD indirob optlistexpr /* new Class @args */
+ { $$ = convert(OP_ENTERSUB, OPf_STACKED,
+ op_append_elem(OP_LIST,
+ op_prepend_elem(OP_LIST, $2, $3),
+ newUNOP(OP_METHOD, 0, $1)));
+ }
+ | FUNCMETH indirob '(' optexpr ')' /* method $object (@args) */
+ { $$ = convert(OP_ENTERSUB, OPf_STACKED,
+ op_append_elem(OP_LIST,
+ op_prepend_elem(OP_LIST, $2, $4),
+ newUNOP(OP_METHOD, 0, $1)));
+ TOKEN_GETMAD($3,$$,'(');
+ TOKEN_GETMAD($5,$$,')');
+ }
+ | LSTOP optlistexpr /* print @args */
+ { $$ = convert(IVAL($1), 0, $2);
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | FUNC '(' optexpr ')' /* print (@args) */
+ { $$ = convert(IVAL($1), 0, $3);
+ TOKEN_GETMAD($1,$$,'o');
+ TOKEN_GETMAD($2,$$,'(');
+ TOKEN_GETMAD($4,$$,')');
+ }
+ | LSTOPSUB startanonsub block /* sub f(&@); f { foo } ... */
+ { SvREFCNT_inc_simple_void(PL_compcv);
+ $<opval>$ = newANONATTRSUB($2, 0, (OP*)NULL, $3); }
+ optlistexpr %prec LSTOP /* ... @bar */
+ { $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
+ op_append_elem(OP_LIST,
+ op_prepend_elem(OP_LIST, $<opval>4, $5), $1));
+ }
+ ;
+
+/* Names of methods. May use $object->$methodname */
+method : METHOD
+ | scalar
+ ;
+
+/* Some kind of subscripted expression */
+subscripted: gelem '{' expr ';' '}' /* *main::{something} */
+ /* In this and all the hash accessors, ';' is
+ * provided by the tokeniser */
+ { $$ = newBINOP(OP_GELEM, 0, $1, scalar($3));
+ PL_parser->expect = XOPERATOR;
+ TOKEN_GETMAD($2,$$,'{');
+ TOKEN_GETMAD($4,$$,';');
+ TOKEN_GETMAD($5,$$,'}');
+ }
+ | scalar '[' expr ']' /* $array[$element] */
+ { $$ = newBINOP(OP_AELEM, 0, oopsAV($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'[');
+ TOKEN_GETMAD($4,$$,']');
+ }
+ | term ARROW '[' expr ']' /* somearef->[$element] */
+ { $$ = newBINOP(OP_AELEM, 0,
+ ref(newAVREF($1),OP_RV2AV),
+ scalar($4));
+ TOKEN_GETMAD($2,$$,'a');
+ TOKEN_GETMAD($3,$$,'[');
+ TOKEN_GETMAD($5,$$,']');
+ }
+ | subscripted '[' expr ']' /* $foo->[$bar]->[$baz] */
+ { $$ = newBINOP(OP_AELEM, 0,
+ ref(newAVREF($1),OP_RV2AV),
+ scalar($3));
+ TOKEN_GETMAD($2,$$,'[');
+ TOKEN_GETMAD($4,$$,']');
+ }
+ | scalar '{' expr ';' '}' /* $foo{bar();} */
+ { $$ = newBINOP(OP_HELEM, 0, oopsHV($1), jmaybe($3));
+ PL_parser->expect = XOPERATOR;
+ TOKEN_GETMAD($2,$$,'{');
+ TOKEN_GETMAD($4,$$,';');
+ TOKEN_GETMAD($5,$$,'}');
+ }
+ | term ARROW '{' expr ';' '}' /* somehref->{bar();} */
+ { $$ = newBINOP(OP_HELEM, 0,
+ ref(newHVREF($1),OP_RV2HV),
+ jmaybe($4));
+ PL_parser->expect = XOPERATOR;
+ TOKEN_GETMAD($2,$$,'a');
+ TOKEN_GETMAD($3,$$,'{');
+ TOKEN_GETMAD($5,$$,';');
+ TOKEN_GETMAD($6,$$,'}');
+ }
+ | subscripted '{' expr ';' '}' /* $foo->[bar]->{baz;} */
+ { $$ = newBINOP(OP_HELEM, 0,
+ ref(newHVREF($1),OP_RV2HV),
+ jmaybe($3));
+ PL_parser->expect = XOPERATOR;
+ TOKEN_GETMAD($2,$$,'{');
+ TOKEN_GETMAD($4,$$,';');
+ TOKEN_GETMAD($5,$$,'}');
+ }
+ | term ARROW '(' ')' /* $subref->() */
+ { $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
+ newCVREF(0, scalar($1)));
+ TOKEN_GETMAD($2,$$,'a');
+ TOKEN_GETMAD($3,$$,'(');
+ TOKEN_GETMAD($4,$$,')');
+ }
+ | term ARROW '(' expr ')' /* $subref->(@args) */
+ { $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
+ op_append_elem(OP_LIST, $4,
+ newCVREF(0, scalar($1))));
+ TOKEN_GETMAD($2,$$,'a');
+ TOKEN_GETMAD($3,$$,'(');
+ TOKEN_GETMAD($5,$$,')');
+ }
+
+ | subscripted '(' expr ')' /* $foo->{bar}->(@args) */
+ { $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
+ op_append_elem(OP_LIST, $3,
+ newCVREF(0, scalar($1))));
+ TOKEN_GETMAD($2,$$,'(');
+ TOKEN_GETMAD($4,$$,')');
+ }
+ | subscripted '(' ')' /* $foo->{bar}->() */
+ { $$ = newUNOP(OP_ENTERSUB, OPf_STACKED,
+ newCVREF(0, scalar($1)));
+ TOKEN_GETMAD($2,$$,'(');
+ TOKEN_GETMAD($3,$$,')');
+ }
+ | '(' expr ')' '[' expr ']' /* list slice */
+ { $$ = newSLICEOP(0, $5, $2);
+ TOKEN_GETMAD($1,$$,'(');
+ TOKEN_GETMAD($3,$$,')');
+ TOKEN_GETMAD($4,$$,'[');
+ TOKEN_GETMAD($6,$$,']');
+ }
+ | QWLIST '[' expr ']' /* list literal slice */
+ { $$ = newSLICEOP(0, $3, $1);
+ TOKEN_GETMAD($2,$$,'[');
+ TOKEN_GETMAD($4,$$,']');
+ }
+ | '(' ')' '[' expr ']' /* empty list slice! */
+ { $$ = newSLICEOP(0, $4, (OP*)NULL);
+ TOKEN_GETMAD($1,$$,'(');
+ TOKEN_GETMAD($2,$$,')');
+ TOKEN_GETMAD($3,$$,'[');
+ TOKEN_GETMAD($5,$$,']');
+ }
+ ;
+
+/* Binary operators between terms */
+termbinop: term ASSIGNOP term /* $x = $y */
+ { $$ = newASSIGNOP(OPf_STACKED, $1, IVAL($2), $3);
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term POWOP term /* $x ** $y */
+ { $$ = newBINOP(IVAL($2), 0, scalar($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term MULOP term /* $x * $y, $x x $y */
+ { if (IVAL($2) != OP_REPEAT)
+ scalar($1);
+ $$ = newBINOP(IVAL($2), 0, $1, scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term ADDOP term /* $x + $y */
+ { $$ = newBINOP(IVAL($2), 0, scalar($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term SHIFTOP term /* $x >> $y, $x << $y */
+ { $$ = newBINOP(IVAL($2), 0, scalar($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term RELOP term /* $x > $y, etc. */
+ { $$ = newBINOP(IVAL($2), 0, scalar($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term EQOP term /* $x == $y, $x eq $y */
+ { $$ = newBINOP(IVAL($2), 0, scalar($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term BITANDOP term /* $x & $y */
+ { $$ = newBINOP(IVAL($2), 0, scalar($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term BITOROP term /* $x | $y */
+ { $$ = newBINOP(IVAL($2), 0, scalar($1), scalar($3));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term DOTDOT term /* $x..$y, $x...$y */
+ {
+ $$ = newRANGE(IVAL($2), scalar($1), scalar($3));
+ DO_MAD({
+ UNOP *op;
+ op = (UNOP*)$$;
+ op = (UNOP*)op->op_first; /* get to flop */
+ op = (UNOP*)op->op_first; /* get to flip */
+ op = (UNOP*)op->op_first; /* get to range */
+ token_getmad($2,(OP*)op,'o');
+ });
+ }
+ | term ANDAND term /* $x && $y */
+ { $$ = newLOGOP(OP_AND, 0, $1, $3);
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term OROR term /* $x || $y */
+ { $$ = newLOGOP(OP_OR, 0, $1, $3);
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term DORDOR term /* $x // $y */
+ { $$ = newLOGOP(OP_DOR, 0, $1, $3);
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term MATCHOP term /* $x =~ /$y/ */
+ { $$ = bind_match(IVAL($2), $1, $3);
+ TOKEN_GETMAD($2,
+ ($$->op_type == OP_NOT
+ ? ((UNOP*)$$)->op_first : $$),
+ '~');
+ }
+ ;
+
+/* Unary operators and terms */
+termunop : '-' term %prec UMINUS /* -$x */
+ { $$ = newUNOP(OP_NEGATE, 0, scalar($2));
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | '+' term %prec UMINUS /* +$x */
+ { $$ = IF_MAD(
+ newUNOP(OP_NULL, 0, $2),
+ $2
+ );
+ TOKEN_GETMAD($1,$$,'+');
+ }
+ | '!' term /* !$x */
+ { $$ = newUNOP(OP_NOT, 0, scalar($2));
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | '~' term /* ~$x */
+ { $$ = newUNOP(OP_COMPLEMENT, 0, scalar($2));
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | term POSTINC /* $x++ */
+ { $$ = newUNOP(OP_POSTINC, 0,
+ op_lvalue(scalar($1), OP_POSTINC));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term POSTDEC /* $x-- */
+ { $$ = newUNOP(OP_POSTDEC, 0,
+ op_lvalue(scalar($1), OP_POSTDEC));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | term POSTJOIN /* implicit join after interpolated ->@ */
+ { $$ = convert(OP_JOIN, 0,
+ op_append_elem(
+ OP_LIST,
+ newSVREF(scalar(
+ newSVOP(OP_CONST,0,
+ newSVpvs("\""))
+ )),
+ $1
+ ));
+ TOKEN_GETMAD($2,$$,'o');
+ }
+ | PREINC term /* ++$x */
+ { $$ = newUNOP(OP_PREINC, 0,
+ op_lvalue(scalar($2), OP_PREINC));
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | PREDEC term /* --$x */
+ { $$ = newUNOP(OP_PREDEC, 0,
+ op_lvalue(scalar($2), OP_PREDEC));
+ TOKEN_GETMAD($1,$$,'o');
+ }
+
+ ;
+
+/* Constructors for anonymous data */
+anonymous: '[' expr ']'
+ { $$ = newANONLIST($2);
+ TOKEN_GETMAD($1,$$,'[');
+ TOKEN_GETMAD($3,$$,']');
+ }
+ | '[' ']'
+ { $$ = newANONLIST((OP*)NULL);
+ TOKEN_GETMAD($1,$$,'[');
+ TOKEN_GETMAD($2,$$,']');
+ }
+ | HASHBRACK expr ';' '}' %prec '(' /* { foo => "Bar" } */
+ { $$ = newANONHASH($2);
+ TOKEN_GETMAD($1,$$,'{');
+ TOKEN_GETMAD($3,$$,';');
+ TOKEN_GETMAD($4,$$,'}');
+ }
+ | HASHBRACK ';' '}' %prec '(' /* { } (';' by tokener) */
+ { $$ = newANONHASH((OP*)NULL);
+ TOKEN_GETMAD($1,$$,'{');
+ TOKEN_GETMAD($2,$$,';');
+ TOKEN_GETMAD($3,$$,'}');
+ }
+ | ANONSUB startanonsub proto subattrlist realsubbody %prec '('
+ { SvREFCNT_inc_simple_void(PL_compcv);
+ $$ = newANONATTRSUB($2, $3, $4, $5);
+ TOKEN_GETMAD($1,$$,'o');
+ OP_GETMAD($3,$$,'s');
+ OP_GETMAD($4,$$,'a');
+ }
+
+ ;
+
+/* Things called with "do" */
+termdo : DO term %prec UNIOP /* do $filename */
+ { $$ = dofile($2, IVAL($1));
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | DO block %prec '(' /* do { code */
+ { $$ = newUNOP(OP_NULL, OPf_SPECIAL, op_scope($2));
+ TOKEN_GETMAD($1,$$,'D');
+ }
+ ;
+
+term : termbinop
+ | termunop
+ | anonymous
+ | termdo
+ | term '?' term ':' term
+ { $$ = newCONDOP(0, $1, $3, $5);
+ TOKEN_GETMAD($2,$$,'?');
+ TOKEN_GETMAD($4,$$,':');
+ }
+ | REFGEN term /* \$x, \@y, \%z */
+ { $$ = newUNOP(OP_REFGEN, 0, op_lvalue($2,OP_REFGEN));
+ TOKEN_GETMAD($1,$$,'o');
+ }
+ | myattrterm %prec UNIOP
+ { $$ = $1; }
+ | LOCAL term %prec UNIOP
+ { $$ = localize($2,IVAL($1));
+ TOKEN_GETMAD($1,$$,'k');
+ }
+ | '(' expr ')'
+ { $$ = sawparens(IF_MAD(newUNOP(OP_NULL,0,$2), $2));
+ TOKEN_GETMAD($1,$$,'(');
+ TOKEN_GETMAD($3,$$,')');
+ }
+ | QWLIST
+ { $$ = IF_MAD(newUNOP(OP_NULL,0,$1), $1); }