/**********************************************************************
parse.y -
$Author$
$Date$
created at: Fri May 28 18:02:42 JST 1993
Copyright (C) 1993-2003 Yukihiro Matsumoto
**********************************************************************/
%{
#define YYDEBUG 1
#include "ruby.h"
#include "env.h"
#include "intern.h"
#include "node.h"
#include "st.h"
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#define yyparse ruby_yyparse
#define yylex ruby_yylex
#define yyerror ruby_yyerror
#define yylval ruby_yylval
#define yychar ruby_yychar
#define yydebug ruby_yydebug
#define ID_SCOPE_SHIFT 3
#define ID_SCOPE_MASK 0x07
#define ID_LOCAL 0x01
#define ID_INSTANCE 0x02
#define ID_GLOBAL 0x03
#define ID_ATTRSET 0x04
#define ID_CONST 0x05
#define ID_CLASS 0x06
#define ID_JUNK 0x07
#define ID_INTERNAL ID_JUNK
#define is_notop_id(id) ((id)>tLAST_TOKEN)
#define is_local_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
#define is_global_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL)
#define is_instance_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE)
#define is_attrset_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET)
#define is_const_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CONST)
#define is_class_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_CLASS)
#define is_junk_id(id) (is_notop_id(id)&&((id)&ID_SCOPE_MASK)==ID_JUNK)
#define is_asgn_or_id(id) ((is_notop_id(id)) && \
(((id)&ID_SCOPE_MASK) == ID_GLOBAL || \
((id)&ID_SCOPE_MASK) == ID_INSTANCE || \
((id)&ID_SCOPE_MASK) == ID_CLASS))
NODE *ruby_eval_tree_begin = 0;
NODE *ruby_eval_tree = 0;
char *ruby_sourcefile; /* current source file */
int ruby_sourceline; /* current line no. */
static int yylex();
static int yyerror();
static enum lex_state {
EXPR_BEG, /* ignore newline, +/- is a sign. */
EXPR_END, /* newline significant, +/- is a operator. */
EXPR_ARG, /* newline significant, +/- is a operator. */
EXPR_CMDARG, /* newline significant, +/- is a operator. */
EXPR_ENDARG, /* newline significant, +/- is a operator. */
EXPR_MID, /* newline significant, +/- is a operator. */
EXPR_FNAME, /* ignore newline, no reserved words. */
EXPR_DOT, /* right after `.' or `::', no reserved words. */
EXPR_CLASS, /* immediate after `class', no here document. */
} lex_state;
static NODE *lex_strterm;
#ifdef HAVE_LONG_LONG
typedef unsigned LONG_LONG stack_type;
#else
typedef unsigned long stack_type;
#endif
#define BITSTACK_PUSH(stack, n) (stack = (stack<<1)|((n)&1))
#define BITSTACK_POP(stack) (stack >>= 1)
#define BITSTACK_LEXPOP(stack) (stack = (stack >> 1) | (stack & 1))
#define BITSTACK_SET_P(stack) (stack&1)
static stack_type cond_stack = 0;
#define COND_PUSH(n) BITSTACK_PUSH(cond_stack, n)
#define COND_POP() BITSTACK_POP(cond_stack)
#define COND_LEXPOP() BITSTACK_LEXPOP(cond_stack)
#define COND_P() BITSTACK_SET_P(cond_stack)
static stack_type cmdarg_stack = 0;
#define CMDARG_PUSH(n) BITSTACK_PUSH(cmdarg_stack, n)
#define CMDARG_POP() BITSTACK_POP(cmdarg_stack)
#define CMDARG_LEXPOP() BITSTACK_LEXPOP(cmdarg_stack)
#define CMDARG_P() BITSTACK_SET_P(cmdarg_stack)
static int class_nest = 0;
static int in_single = 0;
static int in_def = 0;
static int compile_for_eval = 0;
static ID cur_mid = 0;
static NODE *cond();
static NODE *logop();
static int cond_negative();
static NODE *newline_node();
static void fixpos();
static int value_expr0();
static void void_expr0();
static void void_stmts();
static NODE *remove_begin();
#define value_expr(node) value_expr0((node) = remove_begin(node))
#define void_expr(node) void_expr0((node) = remove_begin(node))
static NODE *block_append();
static NODE *list_append();
static NODE *list_concat();
static NODE *arg_concat();
static NODE *arg_prepend();
static NODE *literal_concat();
static NODE *new_evstr();
static NODE *evstr2dstr();
static NODE *call_op();
static int in_defined = 0;
static NODE *negate_lit();
static NODE *ret_args();
static NODE *arg_blk_pass();
static NODE *new_call();
static NODE *new_fcall();
static NODE *new_super();
static NODE *new_yield();
static NODE *gettable();
static NODE *assignable();
static NODE *aryset();
static NODE *attrset();
static void rb_backref_error();
static NODE *node_assign();
static NODE *match_gen();
static void local_push();
static void local_pop();
static int local_append();
static int local_cnt();
static int local_id();
static ID *local_tbl();
static ID internal_id();
static struct RVarmap *dyna_push();
static void dyna_pop();
static int dyna_in_block();
static NODE *dyna_init();
static void top_local_init();
static void top_local_setup();
#define RE_OPTION_ONCE 0x80
#define NODE_STRTERM NODE_ZARRAY /* nothing to gc */
#define NODE_HEREDOC NODE_ARRAY /* 1, 3 to gc */
#define SIGN_EXTEND(x,n) (((1<<(n))-1-((x)&~(~0<<(n))))^~(~0<<(n)))
#define nd_func u1.id
#if SIZEOF_SHORT == 2
#define nd_term(node) ((signed short)(node)->u2.id)
#else
#define nd_term(node) SIGN_EXTEND((node)->u2.id, CHAR_BIT*2)
#endif
#define nd_paren(node) (char)((node)->u2.id >> CHAR_BIT*2)
#define nd_nest u3.id
%}
%union {
NODE *node;
ID id;
int num;
struct RVarmap *vars;
}
%token kCLASS
kMODULE
kDEF
kUNDEF
kBEGIN
kRESCUE
kENSURE
kEND
kIF
kUNLESS
kTHEN
kELSIF
kELSE
kCASE
kWHEN
kWHILE
kUNTIL
kFOR
kBREAK
kNEXT
kREDO
kRETRY
kIN
kDO
kDO_COND
kDO_BLOCK
kRETURN
kYIELD
kSUPER
kSELF
kNIL
kTRUE
kFALSE
kAND
kOR
kNOT
kIF_MOD
kUNLESS_MOD
kWHILE_MOD
kUNTIL_MOD
kRESCUE_MOD
kALIAS
kDEFINED
klBEGIN
klEND
k__LINE__
k__FILE__
%token <id> tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR
%token <node> tINTEGER tFLOAT tSTRING_CONTENT
%token <node> tNTH_REF tBACK_REF
%token <num> tREGEXP_END
%type <node> singleton strings string string1 xstring regexp
%type <node> string_contents xstring_contents string_content
%type <node> words qwords word_list qword_list word
%type <node> literal numeric dsym cpath
%type <node> bodystmt compstmt stmts stmt expr arg primary command command_call method_call
%type <node> expr_value arg_value primary_value
%type <node> if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure
%type <node> args when_args call_args call_args2 open_args paren_args opt_paren_args
%type <node> command_args aref_args opt_block_arg block_arg var_ref var_lhs
%type <node> mrhs superclass block_call block_command
%type <node> f_arglist f_args f_optarg f_opt f_block_arg opt_f_block_arg
%type <node> assoc_list assocs assoc undef_list backref string_dvar
%type <node> block_var opt_block_var brace_block cmd_brace_block do_block lhs none
%type <node> mlhs mlhs_head mlhs_basic mlhs_entry mlhs_item mlhs_node
%type <id> fitem variable sym symbol operation operation2 operation3
%type <id> cname fname op f_rest_arg
%type <num> f_norm_arg f_arg
%token tUPLUS /* unary+ */
%token tUMINUS /* unary- */
%token tPOW /* ** */
%token tCMP /* <=> */
%token tEQ /* == */
%token tEQQ /* === */
%token tNEQ /* != */
%token tGEQ /* >= */
%token tLEQ /* <= */
%token tANDOP tOROP /* && and || */
%token tMATCH tNMATCH /* =~ and !~ */
%token tDOT2 tDOT3 /* .. and ... */
%token tAREF tASET /* [] and []= */
%token tLSHFT tRSHFT /* << and >> */
%token tCOLON2 /* :: */
%token tCOLON3 /* :: at EXPR_BEG */
%token <id> tOP_ASGN /* +=, -= etc. */
%token tASSOC /* => */
%token tLPAREN /* ( */
%token tLPAREN_ARG /* ( */
%token tRPAREN /* ) */
%token tLBRACK /* [ */
%token tLBRACE /* { */
%token tLBRACE_ARG /* { */
%token tSTAR /* * */
%token tAMPER /* & */
%token tSYMBEG tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tWORDS_BEG tQWORDS_BEG
%token tSTRING_DBEG tSTRING_DVAR tSTRING_END
/*
* precedence table
*/
%nonassoc tLOWEST
%nonassoc tLBRACE_ARG
%nonassoc kIF_MOD kUNLESS_MOD kWHILE_MOD kUNTIL_MOD
%left kOR kAND
%right kNOT
%nonassoc kDEFINED
%right '=' tOP_ASGN
%left kRESCUE_MOD
%right '?' ':'
%nonassoc tDOT2 tDOT3
%left tOROP
%left tANDOP
%nonassoc tCMP tEQ tEQQ tNEQ tMATCH tNMATCH
%left '>' tGEQ '<' tLEQ
%left '|' '^'
%left '&'
%left tLSHFT tRSHFT
%left '+' '-'
%left '*' '/' '%'
%right tUMINUS_NUM tUMINUS
%right tPOW
%right '!' '~' tUPLUS
%token tLAST_TOKEN
%%
program : {
lex_state = EXPR_BEG;
top_local_init();
if (ruby_class == rb_cObject) class_nest = 0;
else class_nest = 1;
}
compstmt
{
if ($2 && !compile_for_eval) {
/* last expression should not be void */
if (nd_type($2) != NODE_BLOCK) void_expr($2);
else {
NODE *node = $2;
while (node->nd_next) {
node = node->nd_next;
}
void_expr(node->nd_head);
}
}
ruby_eval_tree = block_append(ruby_eval_tree, $2);
top_local_setup();
class_nest = 0;
}
;
bodystmt : compstmt
opt_rescue
opt_else
opt_ensure
{
$$ = $1;
if ($2) {
$$ = NEW_RESCUE($1, $2, $3);
}
else if ($3) {
rb_warn("else without rescue is useless");
$$ = block_append($$, $3);
}
if ($4) {
$$ = NEW_ENSURE($$, $4);
}
fixpos($$, $1);
}
;
compstmt : stmts opt_terms
{
void_stmts($1);
$$ = $1;
}
;
stmts : none
| stmt
{
$$ = newline_node($1);
}
| stmts terms stmt
{
$$ = block_append($1, newline_node($3));
}
| error stmt
{
$$ = $2;
}
;
stmt : kALIAS fitem {lex_state = EXPR_FNAME;} fitem
{
$$ = NEW_ALIAS($2, $4);
}
| kALIAS tGVAR tGVAR
{
$$ = NEW_VALIAS($2, $3);
}
| kALIAS tGVAR tBACK_REF
{
char buf[3];
sprintf(buf, "$%c", (char)$3->nd_nth);
$$ = NEW_VALIAS($2, rb_intern(buf));
}
| kALIAS tGVAR tNTH_REF
{
yyerror("can't make alias for the number variables");
$$ = 0;
}
| kUNDEF undef_list
{
$$ = $2;
}
| stmt kIF_MOD expr_value
{
$$ = NEW_IF(cond($3), $1, 0);
fixpos($$, $3);
if (cond_negative(&$$->nd_cond)) {
$$->nd_else = $$->nd_body;
$$->nd_body = 0;
}
}
| stmt kUNLESS_MOD expr_value
{
$$ = NEW_UNLESS(cond($3), $1, 0);
fixpos($$, $3);
if (cond_negative(&$$->nd_cond)) {
$$->nd_body = $$->nd_else;
$$->nd_else = 0;
}
}
| stmt kWHILE_MOD expr_value
{
if ($1 && nd_type($1) == NODE_BEGIN) {
$$ = NEW_WHILE(cond($3), $1->nd_body, 0);
}
else {
$$ = NEW_WHILE(cond($3), $1, 1);
}
if (cond_negative(&$$->nd_cond)) {
nd_set_type($$, NODE_UNTIL);
}
}
| stmt kUNTIL_MOD expr_value
{
if ($1 && nd_type($1) == NODE_BEGIN) {
$$ = NEW_UNTIL(cond($3), $1->nd_body, 0);
}
else {
$$ = NEW_UNTIL(cond($3), $1, 1);
}
if (cond_negative(&$$->nd_cond)) {
nd_set_type($$, NODE_WHILE);
}
}
| stmt kRESCUE_MOD stmt
{
$$ = NEW_RESCUE($1, NEW_RESBODY(0,$3,0), 0);
}
| klBEGIN
{
if (in_def || in_single) {
yyerror("BEGIN in method");
}
local_push(0);
}
'{' compstmt '}'
{
ruby_eval_tree_begin = block_append(ruby_eval_tree_begin,
NEW_PREEXE($4));
local_pop();
$$ = 0;
}
| klEND '{' compstmt '}'
{
if (in_def || in_single) {
rb_warn("END in method; use at_exit");
}
$$ = NEW_ITER(0, NEW_POSTEXE(), $3);
}
| lhs '=' command_call
{
value_expr($3);
$$ = node_assign($1, $3);
}
| mlhs '=' command_call
{
value_expr($3);
$1->nd_value = ($1->nd_head) ? NEW_TO_ARY($3) : NEW_ARRAY($3);
$$ = $1;
}
| var_lhs tOP_ASGN command_call
{
value_expr($3);
if ($1) {
ID vid = $1->nd_vid;
if ($2 == tOROP) {
$1->nd_value = $3;
$$ = NEW_OP_ASGN_OR(gettable(vid), $1);
if (is_asgn_or_id(vid)) {
$$->nd_aid = vid;
}
}
else if ($2 == tANDOP) {
$1->nd_value = $3;
$$ = NEW_OP_ASGN_AND(gettable(vid), $1);
}
else {
$$ = $1;
$$->nd_value = call_op(gettable(vid),$2,1,$3);
}
}
else {
$$ = 0;
}
}
| primary_value '[' aref_args ']' tOP_ASGN command_call
{
NODE *args;
value_expr($6);
args = NEW_LIST($6);
$3 = list_append($3, NEW_NIL());
list_concat(args, $3);
if ($5 == tOROP) {
$5 = 0;
}
else if ($5 == tANDOP) {
$5 = 1;
}
$$ = NEW_OP_ASGN1($1, $5, args);
fixpos($$, $1);
}
| primary_value '.' tIDENTIFIER tOP_ASGN command_call
{
value_expr($5);
if ($4 == tOROP) {
$4 = 0;
}
else if ($4 == tANDOP) {
$4 = 1;
}
$$ = NEW_OP_ASGN2($1, $3, $4, $5);
fixpos($$, $1);
}
| primary_value '.' tCONSTANT tOP_ASGN command_call
{
value_expr($5);
if ($4 == tOROP) {
$4 = 0;
}
else if ($4 == tANDOP) {
$4 = 1;
}
$$ = NEW_OP_ASGN2($1, $3, $4, $5);
fixpos($$, $1);
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call
{
value_expr($5);
if ($4 == tOROP) {
$4 = 0;
}
else if ($4 == tANDOP) {
$4 = 1;
}
$$ = NEW_OP_ASGN2($1, $3, $4, $5);
fixpos($$, $1);
}
| backref tOP_ASGN command_call
{
rb_backref_error($1);
$$ = 0;
}
| lhs '=' mrhs
{
$$ = node_assign($1, NEW_SVALUE($3));
}
| mlhs '=' arg_value
{
$1->nd_value = ($1->nd_head) ? NEW_TO_ARY($3) : NEW_ARRAY($3);
$$ = $1;
}
| mlhs '=' mrhs
{
$1->nd_value = $3;
$$ = $1;
}
| expr
;
expr : command_call
| expr kAND expr
{
$$ = logop(NODE_AND, $1, $3);
}
| expr kOR expr
{
$$ = logop(NODE_OR, $1, $3);
}
| kNOT expr
{
$$ = NEW_NOT(cond($2));
}
| '!' command_call
{
$$ = NEW_NOT(cond($2));
}
| arg
;
expr_value : expr
{
value_expr($$);
$$ = $1;
}
;
command_call : command
| block_command
| kRETURN call_args
{
$$ = NEW_RETURN(ret_args($2));
}
| kBREAK call_args
{
$$ = NEW_BREAK(ret_args($2));
}
| kNEXT call_args
{
$$ = NEW_NEXT(ret_args($2));
}
;
block_command : block_call
| block_call '.' operation2 command_args
{
$$ = new_call($1, $3, $4);
}
| block_call tCOLON2 operation2 command_args
{
$$ = new_call($1, $3, $4);
}
;
cmd_brace_block : tLBRACE_ARG
{
$<vars>$ = dyna_push();
$<num>1 = ruby_sourceline;
}
opt_block_var {$<vars>$ = ruby_dyna_vars;}
compstmt
'}'
{
$$ = NEW_ITER($3, 0, dyna_init($5, $<vars>4));
nd_set_line($$, $<num>1);
dyna_pop($<vars>2);
}
;
command : operation command_args %prec tLOWEST
{
$$ = new_fcall($1, $2);
fixpos($$, $2);
}
| operation command_args cmd_brace_block
{
$$ = new_fcall($1, $2);
if ($3) {
if (nd_type($$) == NODE_BLOCK_PASS) {
rb_compile_error("both block arg and actual block given");
}
$3->nd_iter = $$;
$$ = $3;
}
fixpos($$, $2);
}
| primary_value '.' operation2 command_args %prec tLOWEST
{
$$ = new_call($1, $3, $4);
fixpos($$, $1);
}
| primary_value '.' operation2 command_args cmd_brace_block
{
$$ = new_call($1, $3, $4);
if ($5) {
if (nd_type($$) == NODE_BLOCK_PASS) {
rb_compile_error("both block arg and actual block given");
}
$5->nd_iter = $$;
$$ = $5;
}
fixpos($$, $1);
}
| primary_value tCOLON2 operation2 command_args %prec tLOWEST
{
$$ = new_call($1, $3, $4);
fixpos($$, $1);
}
| primary_value tCOLON2 operation2 command_args cmd_brace_block
{
$$ = new_call($1, $3, $4);
if ($5) {
if (nd_type($$) == NODE_BLOCK_PASS) {
rb_compile_error("both block arg and actual block given");
}
$5->nd_iter = $$;
$$ = $5;
}
fixpos($$, $1);
}
| kSUPER command_args
{
$$ = new_super($2);
fixpos($$, $2);
}
| kYIELD command_args
{
$$ = new_yield($2);
fixpos($$, $2);
}
;
mlhs : mlhs_basic
| tLPAREN mlhs_entry ')'
{
$$ = $2;
}
;
mlhs_entry : mlhs_basic
| tLPAREN mlhs_entry ')'
{
$$ = NEW_MASGN(NEW_LIST($2), 0);
}
;
mlhs_basic : mlhs_head
{
$$ = NEW_MASGN($1, 0);
}
| mlhs_head mlhs_item
{
$$ = NEW_MASGN(list_append($1,$2), 0);
}
| mlhs_head tSTAR mlhs_node
{
$$ = NEW_MASGN($1, $3);
}
| mlhs_head tSTAR
{
$$ = NEW_MASGN($1, -1);
}
| tSTAR mlhs_node
{
$$ = NEW_MASGN(0, $2);
}
| tSTAR
{
$$ = NEW_MASGN(0, -1);
}
;
mlhs_item : mlhs_node
| tLPAREN mlhs_entry ')'
{
$$ = $2;
}
;
mlhs_head : mlhs_item ','
{
$$ = NEW_LIST($1);
}
| mlhs_head mlhs_item ','
{
$$ = list_append($1, $2);
}
;
mlhs_node : variable
{
$$ = assignable($1, 0);
}
| primary_value '[' aref_args ']'
{
$$ = aryset($1, $3);
}
| primary_value '.' tIDENTIFIER
{
$$ = attrset($1, $3);
}
| primary_value tCOLON2 tIDENTIFIER
{
$$ = attrset($1, $3);
}
| primary_value '.' tCONSTANT
{
$$ = attrset($1, $3);
}
| primary_value tCOLON2 tCONSTANT
{
if (in_def || in_single)
yyerror("dynamic constant assignment");
$$ = NEW_CDECL(0, 0, NEW_COLON2($1, $3));
}
| tCOLON3 tCONSTANT
{
if (in_def || in_single)
yyerror("dynamic constant assignment");
$$ = NEW_CDECL(0, 0, NEW_COLON3($2));
}
| backref
{
rb_backref_error($1);
$$ = 0;
}
;
lhs : variable
{
$$ = assignable($1, 0);
}
| primary_value '[' aref_args ']'
{
$$ = aryset($1, $3);
}
| primary_value '.' tIDENTIFIER
{
$$ = attrset($1, $3);
}
| primary_value tCOLON2 tIDENTIFIER
{
$$ = attrset($1, $3);
}
| primary_value '.' tCONSTANT
{
$$ = attrset($1, $3);
}
| primary_value tCOLON2 tCONSTANT
{
if (in_def || in_single)
yyerror("dynamic constant assignment");
$$ = NEW_CDECL(0, 0, NEW_COLON2($1, $3));
}
| tCOLON3 tCONSTANT
{
if (in_def || in_single)
yyerror("dynamic constant assignment");
$$ = NEW_CDECL(0, 0, NEW_COLON3($2));
}
| backref
{
rb_backref_error($1);
$$ = 0;
}
;
cname : tIDENTIFIER
{
yyerror("class/module name must be CONSTANT");
}
| tCONSTANT
;
cpath : tCOLON3 cname
{
$$ = NEW_COLON3($2);
}
| cname
{
$$ = NEW_COLON2(0, $$);
}
| primary_value tCOLON2 cname
{
$$ = NEW_COLON2($1, $3);
}
;
fname : tIDENTIFIER
| tCONSTANT
| tFID
| op
{
lex_state = EXPR_END;
$$ = $1;
}
| reswords
{
lex_state = EXPR_END;
$$ = $<id>1;
}
;
fitem : fname
| symbol
;
undef_list : fitem
{
$$ = NEW_UNDEF($1);
}
| undef_list ',' {lex_state = EXPR_FNAME;} fitem
{
$$ = block_append($1, NEW_UNDEF($4));
}
;
op : '|' { $$ = '|'; }
| '^' { $$ = '^'; }
| '&' { $$ = '&'; }
| tCMP { $$ = tCMP; }
| tEQ { $$ = tEQ; }
| tEQQ { $$ = tEQQ; }
| tMATCH { $$ = tMATCH; }
| '>' { $$ = '>'; }
| tGEQ { $$ = tGEQ; }
| '<' { $$ = '<'; }
| tLEQ { $$ = tLEQ; }
| tLSHFT { $$ = tLSHFT; }
| tRSHFT { $$ = tRSHFT; }
| '+' { $$ = '+'; }
| '-' { $$ = '-'; }
| '*' { $$ = '*'; }
| tSTAR { $$ = '*'; }
| '/' { $$ = '/'; }
| '%' { $$ = '%'; }
| tPOW { $$ = tPOW; }
| '~' { $$ = '~'; }
| tUPLUS { $$ = tUPLUS; }
| tUMINUS { $$ = tUMINUS; }
| tAREF { $$ = tAREF; }
| tASET { $$ = tASET; }
| '`' { $$ = '`'; }
;
reswords : k__LINE__ | k__FILE__ | klBEGIN | klEND
| kALIAS | kAND | kBEGIN | kBREAK | kCASE | kCLASS | kDEF
| kDEFINED | kDO | kELSE | kELSIF | kEND | kENSURE | kFALSE
| kFOR | kIN | kMODULE | kNEXT | kNIL | kNOT
| kOR | kREDO | kRESCUE | kRETRY | kRETURN | kSELF | kSUPER
| kTHEN | kTRUE | kUNDEF | kWHEN | kYIELD
| kIF_MOD | kUNLESS_MOD | kWHILE_MOD | kUNTIL_MOD | kRESCUE_MOD
;
arg : lhs '=' arg
{
$$ = node_assign($1, $3);
}
| lhs '=' arg kRESCUE_MOD arg
{
$$ = node_assign($1, NEW_RESCUE($3, NEW_RESBODY(0,$5,0), 0));
}
| var_lhs tOP_ASGN arg
{
value_expr($3);
if ($1) {
ID vid = $1->nd_vid;
if ($2 == tOROP) {
$1->nd_value = $3;
$$ = NEW_OP_ASGN_OR(gettable(vid), $1);
if (is_asgn_or_id(vid)) {
$$->nd_aid = vid;
}
}
else if ($2 == tANDOP) {
$1->nd_value = $3;
$$ = NEW_OP_ASGN_AND(gettable(vid), $1);
}
else {
$$ = $1;
$$->nd_value = call_op(gettable(vid),$2,1,$3);
}
}
else {
$$ = 0;
}
}
| primary_value '[' aref_args ']' tOP_ASGN arg
{
NODE *args;
value_expr($6);
args = NEW_LIST($6);
$3 = list_append($3, NEW_NIL());
list_concat(args, $3);
if ($5 == tOROP) {
$5 = 0;
}
else if ($5 == tANDOP) {
$5 = 1;
}
$$ = NEW_OP_ASGN1($1, $5, args);
fixpos($$, $1);
}
| primary_value '.' tIDENTIFIER tOP_ASGN arg
{
value_expr($5);
if ($4 == tOROP) {
$4 = 0;
}
else if ($4 == tANDOP) {
$4 = 1;
}
$$ = NEW_OP_ASGN2($1, $3, $4, $5);
fixpos($$, $1);
}
| primary_value '.' tCONSTANT tOP_ASGN arg
{
value_expr($5);
if ($4 == tOROP) {
$4 = 0;
}
else if ($4 == tANDOP) {
$4 = 1;
}
$$ = NEW_OP_ASGN2($1, $3, $4, $5);
fixpos($$, $1);
}
| primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg
{
value_expr($5);
if ($4 == tOROP) {
$4 = 0;
}
else if ($4 == tANDOP) {
$4 = 1;
}
$$ = NEW_OP_ASGN2($1, $3, $4, $5);
fixpos($$, $1);
}
| primary_value tCOLON2 tCONSTANT tOP_ASGN arg
{
yyerror("constant re-assignment");
}
| tCOLON3 tCONSTANT tOP_ASGN arg
{
yyerror("constant re-assignment");
}
| backref tOP_ASGN arg
{
rb_backref_error($1);
$$ = 0;
}
| arg tDOT2 arg
{
value_expr($1);
value_expr($3);
$$ = NEW_DOT2($1, $3);
}
| arg tDOT3 arg
{
value_expr($1);
value_expr($3);
$$ = NEW_DOT3($1, $3);
}
| arg '+' arg
{
$$ = call_op($1, '+', 1, $3);
}
| arg '-' arg
{
$$ = call_op($1, '-', 1, $3);
}
| arg '*' arg
{
$$ = call_op($1, '*', 1, $3);
}
| arg '/' arg
{
$$ = call_op($1, '/', 1, $3);
}
| arg '%' arg
{
$$ = call_op($1, '%', 1, $3);
}
| arg tPOW arg
{
$$ = call_op($1, tPOW, 1, $3);
}
| tUMINUS_NUM tINTEGER tPOW arg
{
$$ = call_op(call_op($2, tPOW, 1, $4), tUMINUS, 0, 0);
}
| tUMINUS_NUM tFLOAT tPOW arg
{
$$ = call_op(call_op($2, tPOW, 1, $4), tUMINUS, 0, 0);
}
| tUPLUS arg
{
if ($2 && nd_type($2) == NODE_LIT) {
$$ = $2;
}
else {
$$ = call_op($2, tUPLUS, 0, 0);
}
}
| tUMINUS arg
{
$$ = call_op($2, tUMINUS, 0, 0);
}
| arg '|' arg
{
$$ = call_op($1, '|', 1, $3);
}
| arg '^' arg
{
$$ = call_op($1, '^', 1, $3);
}
| arg '&' arg
{
$$ = call_op($1, '&', 1, $3);
}
| arg tCMP arg
{
$$ = call_op($1, tCMP, 1, $3);
}
| arg '>' arg
{
$$ = call_op($1, '>', 1, $3);
}
| arg tGEQ arg
{
$$ = call_op($1, tGEQ, 1, $3);
}
| arg '<' arg
{
$$ = call_op($1, '<', 1, $3);
}
| arg tLEQ arg
{
$$ = call_op($1, tLEQ, 1, $3);
}
| arg tEQ arg
{
$$ = call_op($1, tEQ, 1, $3);
}
| arg tEQQ arg
{
$$ = call_op($1, tEQQ, 1, $3);
}
| arg tNEQ arg
{
$$ = NEW_NOT(call_op($1, tEQ, 1, $3));
}
| arg tMATCH arg
{
$$ = match_gen($1, $3);
}
| arg tNMATCH arg
{
$$ = NEW_NOT(match_gen($1, $3));
}
| '!' arg
{
$$ = NEW_NOT(cond($2));
}
| '~' arg
{
$$ = call_op($2, '~', 0, 0);
}
| arg tLSHFT arg
{
$$ = call_op($1, tLSHFT, 1, $3);
}
| arg tRSHFT arg
{
$$ = call_op($1, tRSHFT, 1, $3);
}
| arg tANDOP arg
{
$$ = logop(NODE_AND, $1, $3);
}
| arg tOROP arg
{
$$ = logop(NODE_OR, $1, $3);
}
| kDEFINED opt_nl {in_defined = 1;} arg
{
in_defined = 0;
$$ = NEW_DEFINED($4);
}
| arg '?' arg ':' arg
{
$$ = NEW_IF(cond($1), $3, $5);
fixpos($$, $1);
}
| primary
{
$$ = $1;
}
;
arg_value : arg
{
value_expr($1);
$$ = $1;
}
;
aref_args : none
| command opt_nl
{
rb_warn("parenthesize argument(s) for future version");
$$ = NEW_LIST($1);
}
| args trailer
{
$$ = $1;
}
| args ',' tSTAR arg opt_nl
{
value_expr($4);
$$ = arg_concat($1, $4);
}
| assocs trailer
{
$$ = NEW_LIST(NEW_HASH($1));
}
| tSTAR arg opt_nl
{
value_expr($2);
$$ = NEW_NEWLINE(NEW_SPLAT($2));
}
;
paren_args : '(' none ')'
{
$$ = $2;
}
| '(' call_args opt_nl ')'
{
$$ = $2;
}
| '(' block_call opt_nl ')'
{
rb_warn("parenthesize argument for future version");
$$ = NEW_LIST($2);
}
| '(' args ',' block_call opt_nl ')'
{
rb_warn("parenthesize argument for future version");
$$ = list_append($2, $4);
}
;
opt_paren_args : none
| paren_args
;
call_args : command
{
rb_warn("parenthesize argument(s) for future version");
$$ = NEW_LIST($1);
}
| args opt_block_arg
{
$$ = arg_blk_pass($1, $2);
}
| args ',' tSTAR arg_value opt_block_arg
{
$$ = arg_concat($1, $4);
$$ = arg_blk_pass($$, $5);
}
| assocs opt_block_arg
{
$$ = NEW_LIST(NEW_HASH($1));
$$ = arg_blk_pass($$, $2);
}
| assocs ',' tSTAR arg_value opt_block_arg
{
$$ = arg_concat(NEW_LIST(NEW_HASH($1)), $4);
$$ = arg_blk_pass($$, $5);
}
| args ',' assocs opt_block_arg
{
$$ = list_append($1, NEW_HASH($3));
$$ = arg_blk_pass($$, $4);
}
| args ',' assocs ',' tSTAR arg opt_block_arg
{
value_expr($6);
$$ = arg_concat(list_append($1, NEW_HASH($3)), $6);
$$ = arg_blk_pass($$, $7);
}
| tSTAR arg_value opt_block_arg
{
$$ = arg_blk_pass(NEW_SPLAT($2), $3);
}
| block_arg
;
call_args2 : arg_value ',' args opt_block_arg
{
$$ = arg_blk_pass(list_concat(NEW_LIST($1),$3), $4);
}
| arg_value ',' block_arg
{
$$ = arg_blk_pass($1, $3);
}
| arg_value ',' tSTAR arg_value opt_block_arg
{
$$ = arg_concat(NEW_LIST($1), $4);
$$ = arg_blk_pass($$, $5);
}
| arg_value ',' args ',' tSTAR arg_value opt_block_arg
{
$$ = arg_concat(list_concat(NEW_LIST($1),$3), $6);
$$ = arg_blk_pass($$, $7);
}
| assocs opt_block_arg
{
$$ = NEW_LIST(NEW_HASH($1));
$$ = arg_blk_pass($$, $2);
}
| assocs ',' tSTAR arg_value opt_block_arg
{
$$ = arg_concat(NEW_LIST(NEW_HASH($1)), $4);
$$ = arg_blk_pass($$, $5);
}
| arg_value ',' assocs opt_block_arg
{
$$ = list_append(NEW_LIST($1), NEW_HASH($3));
$$ = arg_blk_pass($$, $4);
}
| arg_value ',' args ',' assocs opt_block_arg
{
$$ = list_append(list_concat(NEW_LIST($1),$3), NEW_HASH($5));
$$ = arg_blk_pass($$, $6);
}
| arg_value ',' assocs ',' tSTAR arg_value opt_block_arg
{
$$ = arg_concat(list_append(NEW_LIST($1), NEW_HASH($3)), $6);
$$ = arg_blk_pass($$, $7);
}
| arg_value ',' args ',' assocs ',' tSTAR arg_value opt_block_arg
{
$$ = arg_concat(list_append(list_concat(NEW_LIST($1), $3), NEW_HASH($5)), $8);
$$ = arg_blk_pass($$, $9);
}
| tSTAR arg_value opt_block_arg
{
$$ = arg_blk_pass(NEW_SPLAT($2), $3);
}
| block_arg
;
command_args : {
$<num>$ = cmdarg_stack;
CMDARG_PUSH(1);
}
open_args
{
/* CMDARG_POP() */
cmdarg_stack = $<num>1;
$$ = $2;
}
;
open_args : call_args
| tLPAREN_ARG {lex_state = EXPR_ENDARG;} ')'
{
rb_warn("don't put space before argument parentheses");
$$ = 0;
}
| tLPAREN_ARG call_args2 {lex_state = EXPR_ENDARG;} ')'
{
rb_warn("don't put space before argument parentheses");
$$ = $2;
}
;
block_arg : tAMPER arg_value
{
$$ = NEW_BLOCK_PASS($2);
}
;
opt_block_arg : ',' block_arg
{
$$ = $2;
}
| none
;
args : arg_value
{
$$ = NEW_LIST($1);
}
| args ',' arg_value
{
$$ = list_append($1, $3);
}
;
mrhs : args ',' arg_value
{
$$ = list_append($1, $3);
}
| args ',' tSTAR arg_value
{
$$ = arg_concat($1, $4);
}
| tSTAR arg_value
{
$$ = NEW_SPLAT($2);
}
;
primary : literal
| strings
| xstring
| regexp
| words
| qwords
| var_ref
| backref
| tFID
{
$$ = NEW_FCALL($1, 0);
}
| kBEGIN
{
$<num>1 = ruby_sourceline;
}
bodystmt
kEND
{
$$ = NEW_BEGIN($3);
nd_set_line($$, $<num>1);
}
| tLPAREN_ARG expr {lex_state = EXPR_ENDARG;} ')'
{
rb_warning("(...) interpreted as grouped expression");
$$ = $2;
}
| tLPAREN compstmt ')'
{
$$ = $2;
}
| primary_value tCOLON2 tCONSTANT
{
$$ = NEW_COLON2($1, $3);
}
| tCOLON3 tCONSTANT
{
$$ = NEW_COLON3($2);
}
| primary_value '[' aref_args ']'
{
if ($1 && nd_type($1) == NODE_SELF)
$$ = NEW_FCALL(tAREF, $3);
else
$$ = NEW_CALL($1, tAREF, $3);
}
| tLBRACK aref_args ']'
{
if ($2 == 0) {
$$ = NEW_ZARRAY(); /* zero length array*/
}
else {
$$ = $2;
}
}
| tLBRACE assoc_list '}'
{
$$ = NEW_HASH($2);
}
| kRETURN
{
$$ = NEW_RETURN(0);
}
| kYIELD '(' call_args ')'
{
$$ = new_yield($3);
}
| kYIELD '(' ')'
{
$$ = NEW_YIELD(0, Qfalse);
}
| kYIELD
{
$$ = NEW_YIELD(0, Qfalse);
}
| kDEFINED opt_nl '(' {in_defined = 1;} expr ')'
{
in_defined = 0;
$$ = NEW_DEFINED($5);
}
| operation brace_block
{
$2->nd_iter = NEW_FCALL($1, 0);
$$ = $2;
}
| method_call
| method_call brace_block
{
if ($1 && nd_type($1) == NODE_BLOCK_PASS) {
rb_compile_error("both block arg and actual block given");
}
$2->nd_iter = $1;
$$ = $2;
fixpos($$, $1);
}
| kIF expr_value then
compstmt
if_tail
kEND
{
$$ = NEW_IF(cond($2), $4, $5);
fixpos($$, $2);
if (cond_negative(&$$->nd_cond)) {
NODE *tmp = $$->nd_body;
$$->nd_body = $$->nd_else;
$$->nd_else = tmp;
}
}
| kUNLESS expr_value then
compstmt
opt_else
kEND
{
$$ = NEW_UNLESS(cond($2), $4, $5);
fixpos($$, $2);
if (cond_negative(&$$->nd_cond)) {
NODE *tmp = $$->nd_body;
$$->nd_body = $$->nd_else;
$$->nd_else = tmp;
}
}
| kWHILE {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt
kEND
{
$$ = NEW_WHILE(cond($3), $6, 1);
fixpos($$, $3);
if (cond_negative(&$$->nd_cond)) {
nd_set_type($$, NODE_UNTIL);
}
}
| kUNTIL {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt
kEND
{
$$ = NEW_UNTIL(cond($3), $6, 1);
fixpos($$, $3);
if (cond_negative(&$$->nd_cond)) {
nd_set_type($$, NODE_WHILE);
}
}
| kCASE expr_value opt_terms
case_body
kEND
{
$$ = NEW_CASE($2, $4);
fixpos($$, $2);
}
| kCASE opt_terms case_body kEND
{
$$ = $3;
}
| kCASE opt_terms kELSE compstmt kEND
{
$$ = $4;
}
| kFOR block_var kIN {COND_PUSH(1);} expr_value do {COND_POP();}
compstmt
kEND
{
$$ = NEW_FOR($2, $5, $8);
fixpos($$, $2);
}
| kCLASS cpath superclass
{
if (in_def || in_single)
yyerror("class definition in method body");
class_nest++;
local_push(0);
$<num>$ = ruby_sourceline;
}
bodystmt
kEND
{
$$ = NEW_CLASS($2, $5, $3);
nd_set_line($$, $<num>4);
local_pop();
class_nest--;
}
| kCLASS tLSHFT expr
{
$<num>$ = in_def;
in_def = 0;
}
term
{
$<num>$ = in_single;
in_single = 0;
class_nest++;
local_push(0);
}
bodystmt
kEND
{
$$ = NEW_SCLASS($3, $7);
fixpos($$, $3);
local_pop();
class_nest--;
in_def = $<num>4;
in_single = $<num>6;
}
| kMODULE cpath
{
if (in_def || in_single)
yyerror("module definition in method body");
class_nest++;
local_push(0);
$<num>$ = ruby_sourceline;
}
bodystmt
kEND
{
$$ = NEW_MODULE($2, $4);
nd_set_line($$, $<num>3);
local_pop();
class_nest--;
}
| kDEF fname
{
$<id>$ = cur_mid;
cur_mid = $2;
in_def++;
local_push(0);
}
f_arglist
bodystmt
kEND
{
$$ = NEW_DEFN($2, $4, $5, NOEX_PRIVATE);
fixpos($$, $4);
local_pop();
in_def--;
cur_mid = $<id>3;
}
| kDEF singleton dot_or_colon {lex_state = EXPR_FNAME;} fname
{
in_single++;
|