From d464704f111d211c1f1ff9ef23ef1d755054be00 Mon Sep 17 00:00:00 2001 From: shyouhei Date: Wed, 15 Aug 2007 19:08:43 +0000 Subject: add tag v1_8_5_54 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_8_5_54@12952 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ruby_1_8_5/parse.y | 6337 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6337 insertions(+) create mode 100644 ruby_1_8_5/parse.y (limited to 'ruby_1_8_5/parse.y') diff --git a/ruby_1_8_5/parse.y b/ruby_1_8_5/parse.y new file mode 100644 index 0000000000..d1466e77ca --- /dev/null +++ b/ruby_1_8_5/parse.y @@ -0,0 +1,6337 @@ +/********************************************************************** + + parse.y - + + $Author: shyouhei $ + $Date: 2007/01/27 15:45:51 $ + created at: Fri May 28 18:02:42 JST 1993 + + Copyright (C) 1993-2003 Yukihiro Matsumoto + +**********************************************************************/ + +%{ + +#define YYDEBUG 1 +#define YYERROR_VERBOSE 1 +#ifndef YYSTACK_USE_ALLOCA +#define YYSTACK_USE_ALLOCA 0 +#endif + +#include "ruby.h" +#include "env.h" +#include "intern.h" +#include "node.h" +#include "st.h" +#include +#include +#include + +#define YYMALLOC rb_parser_malloc +#define YYREALLOC rb_parser_realloc +#define YYCALLOC rb_parser_calloc +#define YYFREE rb_parser_free +#define malloc YYMALLOC +#define realloc YYREALLOC +#define calloc YYCALLOC +#define free YYFREE +static void *rb_parser_malloc _((size_t)); +static void *rb_parser_realloc _((void *, size_t)); +static void *rb_parser_calloc _((size_t, size_t)); +static void rb_parser_free _((void *)); + +#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 int command_start = Qtrue; + +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))))-(1<<(n)-1)) +#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 + +/* Older versions of Yacc set YYMAXDEPTH to a very low value by default (150, + for instance). This is too low for Ruby to parse some files, such as + date/format.rb, therefore bump the value up to at least Bison's default. */ +#ifdef OLD_YACC +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif +#endif + +%} + +%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 tIDENTIFIER tFID tGVAR tIVAR tCONSTANT tCVAR +%token tINTEGER tFLOAT tSTRING_CONTENT +%token tNTH_REF tBACK_REF +%token tREGEXP_END + +%type singleton strings string string1 xstring regexp +%type string_contents xstring_contents string_content +%type words qwords word_list qword_list word +%type literal numeric dsym cpath +%type bodystmt compstmt stmts stmt expr arg primary command command_call method_call +%type expr_value arg_value primary_value +%type if_tail opt_else case_body cases opt_rescue exc_list exc_var opt_ensure +%type args when_args call_args call_args2 open_args paren_args opt_paren_args +%type command_args aref_args opt_block_arg block_arg var_ref var_lhs +%type mrhs superclass block_call block_command +%type f_arglist f_args f_optarg f_opt f_rest_arg f_block_arg opt_f_block_arg +%type assoc_list assocs assoc undef_list backref string_dvar +%type block_var opt_block_var brace_block cmd_brace_block do_block lhs none fitem +%type mlhs mlhs_head mlhs_basic mlhs_entry mlhs_item mlhs_node +%type fsym variable sym symbol operation operation2 operation3 +%type cname fname op +%type 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 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 + { + $$ = 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); + if (!$3) $3 = NEW_ZARRAY(); + args = arg_concat($6, $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 + { + $$ = dyna_push(); + $1 = ruby_sourceline; + } + opt_block_var {$$ = ruby_dyna_vars;} + compstmt + '}' + { + $$ = NEW_ITER($3, 0, dyna_init($5, $4)); + nd_set_line($$, $1); + dyna_pop($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; + $$ = $1; + } + ; + +fsym : fname + | symbol + ; + +fitem : fsym + { + $$ = NEW_LIT(ID2SYM($1)); + } + | dsym + ; + +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 | kUNLESS | kWHILE | kUNTIL + ; + +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); + if (!$3) $3 = NEW_ZARRAY(); + args = arg_concat($6, $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"); + $$ = 0; + } + | tCOLON3 tCONSTANT tOP_ASGN arg + { + yyerror("constant re-assignment"); + $$ = 0; + } + | backref tOP_ASGN arg + { + rb_backref_error($1); + $$ = 0; + } + | arg tDOT2 arg + { + value_expr($1); + value_expr($3); + if (nd_type($1) == NODE_LIT && FIXNUM_P($1->nd_lit) && + nd_type($3) == NODE_LIT && FIXNUM_P($3->nd_lit)) { + $1->nd_lit = rb_range_new($1->nd_lit, $3->nd_lit, Qfalse); + $$ = $1; + } + else { + $$ = NEW_DOT2($1, $3); + } + } + | arg tDOT3 arg + { + value_expr($1); + value_expr($3); + if (nd_type($1) == NODE_LIT && FIXNUM_P($1->nd_lit) && + nd_type($3) == NODE_LIT && FIXNUM_P($3->nd_lit)) { + $1->nd_lit = rb_range_new($1->nd_lit, $3->nd_lit, Qtrue); + $$ = $1; + } + else { + $$ = 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 : { + $$ = cmdarg_stack; + CMDARG_PUSH(1); + } + open_args + { + /* CMDARG_POP() */ + cmdarg_stack = $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 + { + $1 = ruby_sourceline; + } + bodystmt + kEND + { + if ($3 == NULL) + $$ = NEW_NIL(); + else + $$ = NEW_BEGIN($3); + nd_set_line($$, $1); + } + | tLPAREN_ARG expr {lex_state = EXPR_ENDARG;} opt_nl ')' + { + rb_warning("(...) interpreted as grouped expression"); + $$ = $2; + } + | tLPAREN compstmt ')' + { + if (!$2) $$ = NEW_NIL(); + else $$ = $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); + fixpos($$, $1); + } + | 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; + fixpos($2->nd_iter, $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); + $$ = ruby_sourceline; + } + bodystmt + kEND + { + $$ = NEW_CLASS($2, $5, $3); + nd_set_line($$, $4); + local_pop(); + class_nest--; + } + | kCLASS tLSHFT expr + { + $$ = in_def; + in_def = 0; + } + term + { + $$ = in_single; + in_single = 0; + class_nest++; + local_push(0); + } + bodystmt + kEND + { + $$ = NEW_SCLASS($3, $7); + fixpos($$, $3); + local_pop(); + class_nest--; + in_def = $4; + in_single = $6; + } + | kMODULE cpath + { + if (in_def || in_single) + yyerror("module definition in method body"); + class_nest++; + local_push(0); + $$ = ruby_sourceline; + } + bodystmt + kEND + { + $$ = NEW_MODULE($2, $4); + nd_set_line($$, $3); + local_pop(); + class_nest--; + } + | kDEF fname + { + $$ = cur_mid; + cur_mid = $2; + in_def++; + local_push(0); + } + f_arglist + bodystmt + kEND + { + if (!$5) $5 = NEW_NIL(); + $$ = NEW_DEFN($2, $4, $5, NOEX_PRIVATE); + fixpos($$, $4); + local_pop(); + in_def--; + cur_mid = $3; + } + | kDEF singleton dot_or_colon {lex_state = EXPR_FNAME;} fname + { + in_single++; + local_push(0); + lex_state = EXPR_END; /* force for args */ + } + f_arglist + bodystmt + kEND + { + $$ = NEW_DEFS($2, $5, $7, $8); + fixpos($$, $2); + local_pop(); + in_single--; + } + | kBREAK + { + $$ = NEW_BREAK(0); + } + | kNEXT + { + $$ = NEW_NEXT(0); + } + | kREDO + { + $$ = NEW_REDO(); + } + | kRETRY + { + $$ = NEW_RETRY(); + } + ; + +primary_value : primary + { + value_expr($1); + $$ = $1; + } + ; + +then : term + | ':' + | kTHEN + | term kTHEN + ; + +do : term + | ':' + | kDO_COND + ; + +if_tail : opt_else + | kELSIF expr_value then + compstmt + if_tail + { + $$ = NEW_IF(cond($2), $4, $5); + fixpos($$, $2); + } + ; + +opt_else : none + | kELSE compstmt + { + $$ = $2; + } + ; + +block_var : lhs + | mlhs + ; + +opt_block_var : none + | '|' /* none */ '|' + { + $$ = (NODE*)1; + } + | tOROP + { + $$ = (NODE*)1; + } + | '|' block_var '|' + { + $$ = $2; + } + ; + +do_block : kDO_BLOCK + { + $$ = dyna_push(); + $1 = ruby_sourceline; + } + opt_block_var {$$ = ruby_dyna_vars;} + compstmt + kEND + { + $$ = NEW_ITER($3, 0, dyna_init($5, $4)); + nd_set_line($$, $1); + dyna_pop($2); + } + ; + +block_call : command do_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); + } + | block_call '.' operation2 opt_paren_args + { + $$ = new_call($1, $3, $4); + } + | block_call tCOLON2 operation2 opt_paren_args + { + $$ = new_call($1, $3, $4); + } + ; + +method_call : operation paren_args + { + $$ = new_fcall($1, $2); + fixpos($$, $2); + } + | primary_value '.' operation2 opt_paren_args + { + $$ = new_call($1, $3, $4); + fixpos($$, $1); + } + | primary_value tCOLON2 operation2 paren_args + { + $$ = new_call($1, $3, $4); + fixpos($$, $1); + } + | primary_value tCOLON2 operation3 + { + $$ = new_call($1, $3, 0); + } + | kSUPER paren_args + { + $$ = new_super($2); + } + | kSUPER + { + $$ = NEW_ZSUPER(); + } + ; + +brace_block : '{' + { + $$ = dyna_push(); + $1 = ruby_sourceline; + } + opt_block_var {$$ = ruby_dyna_vars;} + compstmt '}' + { + $$ = NEW_ITER($3, 0, dyna_init($5, $4)); + nd_set_line($$, $1); + dyna_pop($2); + } + | kDO + { + $$ = dyna_push(); + $1 = ruby_sourceline; + } + opt_block_var {$$ = ruby_dyna_vars;} + compstmt kEND + { + $$ = NEW_ITER($3, 0, dyna_init($5, $4)); + nd_set_line($$, $1); + dyna_pop($2); + } + ; + +case_body : kWHEN when_args then + compstmt + cases + { + $$ = NEW_WHEN($2, $4, $5); + } + ; +when_args : args + | args ',' tSTAR arg_value + { + $$ = list_append($1, NEW_WHEN($4, 0, 0)); + } + | tSTAR arg_value + { + $$ = NEW_LIST(NEW_WHEN($2, 0, 0)); + } + ; + +cases : opt_else + | case_body + ; + +opt_rescue : kRESCUE exc_list exc_var then + compstmt + opt_rescue + { + if ($3) { + $3 = node_assign($3, NEW_GVAR(rb_intern("$!"))); + $5 = block_append($3, $5); + } + $$ = NEW_RESBODY($2, $5, $6); + fixpos($$, $2?$2:$5); + } + | none + ; + +exc_list : arg_value + { + $$ = NEW_LIST($1); + } + | mrhs + | none + ; + +exc_var : tASSOC lhs + { + $$ = $2; + } + | none + ; + +opt_ensure : kENSURE compstmt + { + if ($2) + $$ = $2; + else + /* place holder */ + $$ = NEW_NIL(); + } + | none + ; + +literal : numeric + | symbol + { + $$ = NEW_LIT(ID2SYM($1)); + } + | dsym + ; + +strings : string + { + NODE *node = $1; + if (!node) { + node = NEW_STR(rb_str_new(0, 0)); + } + else { + node = evstr2dstr(node); + } + $$ = node; + } + ; + +string : string1 + | string string1 + { + $$ = literal_concat($1, $2); + } + ; + +string1 : tSTRING_BEG string_contents tSTRING_END + { + $$ = $2; + } + ; + +xstring : tXSTRING_BEG xstring_contents tSTRING_END + { + NODE *node = $2; + if (!node) { + node = NEW_XSTR(rb_str_new(0, 0)); + } + else { + switch (nd_type(node)) { + case NODE_STR: + nd_set_type(node, NODE_XSTR); + break; + case NODE_DSTR: + nd_set_type(node, NODE_DXSTR); + break; + default: + node = NEW_NODE(NODE_DXSTR, rb_str_new(0, 0), 1, NEW_LIST(node)); + break; + } + } + $$ = node; + } + ; + +regexp : tREGEXP_BEG xstring_contents tREGEXP_END + { + int options = $3; + NODE *node = $2; + if (!node) { + node = NEW_LIT(rb_reg_new("", 0, options & ~RE_OPTION_ONCE)); + } + else switch (nd_type(node)) { + case NODE_STR: + { + VALUE src = node->nd_lit; + nd_set_type(node, NODE_LIT); + node->nd_lit = rb_reg_new(RSTRING(src)->ptr, + RSTRING(src)->len, + options & ~RE_OPTION_ONCE); + } + break; + default: + node = NEW_NODE(NODE_DSTR, rb_str_new(0, 0), 1, NEW_LIST(node)); + case NODE_DSTR: + if (options & RE_OPTION_ONCE) { + nd_set_type(node, NODE_DREGX_ONCE); + } + else { + nd_set_type(node, NODE_DREGX); + } + node->nd_cflag = options & ~RE_OPTION_ONCE; + break; + } + $$ = node; + } + ; + +words : tWORDS_BEG ' ' tSTRING_END + { + $$ = NEW_ZARRAY(); + } + | tWORDS_BEG word_list tSTRING_END + { + $$ = $2; + } + ; + +word_list : /* none */ + { + $$ = 0; + } + | word_list word ' ' + { + $$ = list_append($1, evstr2dstr($2)); + } + ; + +word : string_content + | word string_content + { + $$ = literal_concat($1, $2); + } + ; + +qwords : tQWORDS_BEG ' ' tSTRING_END + { + $$ = NEW_ZARRAY(); + } + | tQWORDS_BEG qword_list tSTRING_END + { + $$ = $2; + } + ; + +qword_list : /* none */ + { + $$ = 0; + } + | qword_list tSTRING_CONTENT ' ' + { + $$ = list_append($1, $2); + } + ; + +string_contents : /* none */ + { + $$ = 0; + } + | string_contents string_content + { + $$ = literal_concat($1, $2); + } + ; + +xstring_contents: /* none */ + { + $$ = 0; + } + | xstring_contents string_content + { + $$ = literal_concat($1, $2); + } + ; + +string_content : tSTRING_CONTENT + | tSTRING_DVAR + { + $$ = lex_strterm; + lex_strterm = 0; + lex_state = EXPR_BEG; + } + string_dvar + { + lex_strterm = $2; + $$ = NEW_EVSTR($3); + } + | tSTRING_DBEG + { + $$ = lex_strterm; + lex_strterm = 0; + lex_state = EXPR_BEG; + COND_PUSH(0); + CMDARG_PUSH(0); + } + compstmt '}' + { + lex_strterm = $2; + COND_LEXPOP(); + CMDARG_LEXPOP(); + if (($$ = $3) && nd_type($$) == NODE_NEWLINE) { + $$ = $$->nd_next; + rb_gc_force_recycle((VALUE)$3); + } + $$ = new_evstr($$); + } + ; + +string_dvar : tGVAR {$$ = NEW_GVAR($1);} + | tIVAR {$$ = NEW_IVAR($1);} + | tCVAR {$$ = NEW_CVAR($1);} + | backref + ; + +symbol : tSYMBEG sym + { + lex_state = EXPR_END; + $$ = $2; + } + ; + +sym : fname + | tIVAR + | tGVAR + | tCVAR + ; + +dsym : tSYMBEG xstring_contents tSTRING_END + { + lex_state = EXPR_END; + if (!($$ = $2)) { + yyerror("empty symbol literal"); + } + else { + VALUE lit; + + switch (nd_type($$)) { + case NODE_DSTR: + nd_set_type($$, NODE_DSYM); + break; + case NODE_STR: + lit = $$->nd_lit; + if (RSTRING(lit)->len == 0) { + yyerror("empty symbol literal"); + break; + } + if (strlen(RSTRING(lit)->ptr) == RSTRING(lit)->len) { + $$->nd_lit = ID2SYM(rb_intern(RSTRING($$->nd_lit)->ptr)); + nd_set_type($$, NODE_LIT); + break; + } + /* fall through */ + default: + $$ = NEW_NODE(NODE_DSYM, rb_str_new(0, 0), 1, NEW_LIST($$)); + break; + } + } + } + ; + +numeric : tINTEGER + | tFLOAT + | tUMINUS_NUM tINTEGER %prec tLOWEST + { + $$ = negate_lit($2); + } + | tUMINUS_NUM tFLOAT %prec tLOWEST + { + $$ = negate_lit($2); + } + ; + +variable : tIDENTIFIER + | tIVAR + | tGVAR + | tCONSTANT + | tCVAR + | kNIL {$$ = kNIL;} + | kSELF {$$ = kSELF;} + | kTRUE {$$ = kTRUE;} + | kFALSE {$$ = kFALSE;} + | k__FILE__ {$$ = k__FILE__;} + | k__LINE__ {$$ = k__LINE__;} + ; + +var_ref : variable + { + $$ = gettable($1); + } + ; + +var_lhs : variable + { + $$ = assignable($1, 0); + } + ; + +backref : tNTH_REF + | tBACK_REF + ; + +superclass : term + { + $$ = 0; + } + | '<' + { + lex_state = EXPR_BEG; + } + expr_value term + { + $$ = $3; + } + | error term {yyerrok; $$ = 0;} + ; + +f_arglist : '(' f_args opt_nl ')' + { + $$ = $2; + lex_state = EXPR_BEG; + command_start = Qtrue; + } + | f_args term + { + $$ = $1; + } + ; + +f_args : f_arg ',' f_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = block_append(NEW_ARGS($1, $3, $5), $6); + } + | f_arg ',' f_optarg opt_f_block_arg + { + $$ = block_append(NEW_ARGS($1, $3, 0), $4); + } + | f_arg ',' f_rest_arg opt_f_block_arg + { + $$ = block_append(NEW_ARGS($1, 0, $3), $4); + } + | f_arg opt_f_block_arg + { + $$ = block_append(NEW_ARGS($1, 0, 0), $2); + } + | f_optarg ',' f_rest_arg opt_f_block_arg + { + $$ = block_append(NEW_ARGS(0, $1, $3), $4); + } + | f_optarg opt_f_block_arg + { + $$ = block_append(NEW_ARGS(0, $1, 0), $2); + } + | f_rest_arg opt_f_block_arg + { + $$ = block_append(NEW_ARGS(0, 0, $1), $2); + } + | f_block_arg + { + $$ = block_append(NEW_ARGS(0, 0, 0), $1); + } + | /* none */ + { + $$ = NEW_ARGS(0, 0, 0); + } + ; + +f_norm_arg : tCONSTANT + { + yyerror("formal argument cannot be a constant"); + } + | tIVAR + { + yyerror("formal argument cannot be an instance variable"); + } + | tGVAR + { + yyerror("formal argument cannot be a global variable"); + } + | tCVAR + { + yyerror("formal argument cannot be a class variable"); + } + | tIDENTIFIER + { + if (!is_local_id($1)) + yyerror("formal argument must be local variable"); + else if (local_id($1)) + yyerror("duplicate argument name"); + local_cnt($1); + $$ = 1; + } + ; + +f_arg : f_norm_arg + | f_arg ',' f_norm_arg + { + $$ += 1; + } + ; + +f_opt : tIDENTIFIER '=' arg_value + { + if (!is_local_id($1)) + yyerror("formal argument must be local variable"); + else if (local_id($1)) + yyerror("duplicate optional argument name"); + $$ = assignable($1, $3); + } + ; + +f_optarg : f_opt + { + $$ = NEW_BLOCK($1); + $$->nd_end = $$; + } + | f_optarg ',' f_opt + { + $$ = block_append($1, $3); + } + ; + +restarg_mark : '*' + | tSTAR + ; + +f_rest_arg : restarg_mark tIDENTIFIER + { + if (!is_local_id($2)) + yyerror("rest argument must be local variable"); + if (dyna_in_block()) { + rb_dvar_push($2, Qnil); + } + $$ = assignable($2, 0); + } + | restarg_mark + { + if (dyna_in_block()) { + $$ = NEW_DASGN_CURR(internal_id(), 0); + } + else { + $$ = NEW_NODE(NODE_LASGN,0,0,local_append(0)); + } + } + ; + +blkarg_mark : '&' + | tAMPER + ; + +f_block_arg : blkarg_mark tIDENTIFIER + { + if (!is_local_id($2)) + yyerror("block argument must be local variable"); + else if (local_id($2)) + yyerror("duplicate block argument name"); + $$ = NEW_BLOCK_ARG($2); + } + ; + +opt_f_block_arg : ',' f_block_arg + { + $$ = $2; + } + | none + ; + +singleton : var_ref + { + if ($1 && nd_type($1) == NODE_SELF) { + $$ = NEW_SELF(); + } + else { + $$ = $1; + value_expr($$); + } + } + | '(' {lex_state = EXPR_BEG;} expr opt_nl ')' + { + if ($3 == 0) { + yyerror("can't define singleton method for ()."); + } + else { + switch (nd_type($3)) { + case NODE_STR: + case NODE_DSTR: + case NODE_XSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_LIT: + case NODE_ARRAY: + case NODE_ZARRAY: + yyerror("can't define singleton method for literals"); + default: + value_expr($3); + break; + } + } + $$ = $3; + } + ; + +assoc_list : none + | assocs trailer + { + $$ = $1; + } + | args trailer + { + if ($1->nd_alen%2 != 0) { + yyerror("odd number list for Hash"); + } + $$ = $1; + } + ; + +assocs : assoc + | assocs ',' assoc + { + $$ = list_concat($1, $3); + } + ; + +assoc : arg_value tASSOC arg_value + { + $$ = list_append(NEW_LIST($1), $3); + } + ; + +operation : tIDENTIFIER + | tCONSTANT + | tFID + ; + +operation2 : tIDENTIFIER + | tCONSTANT + | tFID + | op + ; + +operation3 : tIDENTIFIER + | tFID + | op + ; + +dot_or_colon : '.' + | tCOLON2 + ; + +opt_terms : /* none */ + | terms + ; + +opt_nl : /* none */ + | '\n' + ; + +trailer : /* none */ + | '\n' + | ',' + ; + +term : ';' {yyerrok;} + | '\n' + ; + +terms : term + | terms ';' {yyerrok;} + ; + +none : /* none */ {$$ = 0;} + ; +%% +#ifdef yystacksize +#undef YYMALLOC +#endif + +#include "regex.h" +#include "util.h" + +/* We remove any previous definition of `SIGN_EXTEND_CHAR', + since ours (we hope) works properly with all combinations of + machines, compilers, `char' and `unsigned char' argument types. + (Per Bothner suggested the basic approach.) */ +#undef SIGN_EXTEND_CHAR +#if __STDC__ +# define SIGN_EXTEND_CHAR(c) ((signed char)(c)) +#else /* not __STDC__ */ +/* As in Harbison and Steele. */ +# define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128) +#endif +#define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_' || ismbchar(c))) + +static char *tokenbuf = NULL; +static int tokidx, toksiz = 0; + +#define LEAVE_BS 1 + +static VALUE (*lex_gets)(); /* gets function */ +static VALUE lex_input; /* non-nil if File */ +static VALUE lex_lastline; /* gc protect */ +static char *lex_pbeg; +static char *lex_p; +static char *lex_pend; + +static int +yyerror(msg) + const char *msg; +{ + char *p, *pe, *buf; + int len, i; + + rb_compile_error("%s", msg); + p = lex_p; + while (lex_pbeg <= p) { + if (*p == '\n') break; + p--; + } + p++; + + pe = lex_p; + while (pe < lex_pend) { + if (*pe == '\n') break; + pe++; + } + + len = pe - p; + if (len > 4) { + buf = ALLOCA_N(char, len+2); + MEMCPY(buf, p, char, len); + buf[len] = '\0'; + rb_compile_error_append("%s", buf); + + i = lex_p - p; + p = buf; pe = p + len; + + while (p < pe) { + if (*p != '\t') *p = ' '; + p++; + } + buf[i] = '^'; + buf[i+1] = '\0'; + rb_compile_error_append("%s", buf); + } + + return 0; +} + +static int heredoc_end; + +int ruby_in_compile = 0; +int ruby__end__seen; + +static VALUE ruby_debug_lines; +#ifdef YYMALLOC +static NODE *parser_heap; +#endif + +static NODE* +yycompile(f, line) + char *f; + int line; +{ + int n; + NODE *node = 0; + struct RVarmap *vp, *vars = ruby_dyna_vars; + + ruby_in_compile = 1; + if (!compile_for_eval && rb_safe_level() == 0 && + rb_const_defined(rb_cObject, rb_intern("SCRIPT_LINES__"))) { + VALUE hash, fname; + + hash = rb_const_get(rb_cObject, rb_intern("SCRIPT_LINES__")); + if (TYPE(hash) == T_HASH) { + fname = rb_str_new2(f); + ruby_debug_lines = rb_hash_aref(hash, fname); + if (NIL_P(ruby_debug_lines)) { + ruby_debug_lines = rb_ary_new(); + rb_hash_aset(hash, fname, ruby_debug_lines); + } + } + if (line > 1) { + VALUE str = rb_str_new(0,0); + while (line > 1) { + rb_ary_push(ruby_debug_lines, str); + line--; + } + } + } + + ruby__end__seen = 0; + ruby_eval_tree = 0; + heredoc_end = 0; + lex_strterm = 0; + ruby_current_node = 0; + ruby_sourcefile = rb_source_filename(f); + n = yyparse(); + ruby_debug_lines = 0; + compile_for_eval = 0; + ruby_in_compile = 0; + cond_stack = 0; + cmdarg_stack = 0; + command_start = 1; + class_nest = 0; + in_single = 0; + in_def = 0; + cur_mid = 0; + + vp = ruby_dyna_vars; + ruby_dyna_vars = vars; + lex_strterm = 0; + while (vp && vp != vars) { + struct RVarmap *tmp = vp; + vp = vp->next; + rb_gc_force_recycle((VALUE)tmp); + } + if (n == 0) node = ruby_eval_tree; + else ruby_eval_tree_begin = 0; + return node; +} + +static int lex_gets_ptr; + +static VALUE +lex_get_str(s) + VALUE s; +{ + char *beg, *end, *pend; + + beg = RSTRING(s)->ptr; + if (lex_gets_ptr) { + if (RSTRING(s)->len == lex_gets_ptr) return Qnil; + beg += lex_gets_ptr; + } + pend = RSTRING(s)->ptr + RSTRING(s)->len; + end = beg; + while (end < pend) { + if (*end++ == '\n') break; + } + lex_gets_ptr = end - RSTRING(s)->ptr; + return rb_str_new(beg, end - beg); +} + +static VALUE +lex_getline() +{ + VALUE line = (*lex_gets)(lex_input); + if (ruby_debug_lines && !NIL_P(line)) { + rb_ary_push(ruby_debug_lines, line); + } + return line; +} + +NODE* +rb_compile_string(f, s, line) + const char *f; + VALUE s; + int line; +{ + lex_gets = lex_get_str; + lex_gets_ptr = 0; + lex_input = s; + lex_pbeg = lex_p = lex_pend = 0; + ruby_sourceline = line - 1; + compile_for_eval = ruby_in_eval; + + return yycompile(f, line); +} + +NODE* +rb_compile_cstr(f, s, len, line) + const char *f, *s; + int len, line; +{ + return rb_compile_string(f, rb_str_new(s, len), line); +} + +NODE* +rb_compile_file(f, file, start) + const char *f; + VALUE file; + int start; +{ + lex_gets = rb_io_gets; + lex_input = file; + lex_pbeg = lex_p = lex_pend = 0; + ruby_sourceline = start - 1; + + return yycompile(f, start); +} + +static inline int +nextc() +{ + int c; + + if (lex_p == lex_pend) { + if (lex_input) { + VALUE v = lex_getline(); + + if (NIL_P(v)) return -1; + if (heredoc_end > 0) { + ruby_sourceline = heredoc_end; + heredoc_end = 0; + } + ruby_sourceline++; + lex_pbeg = lex_p = RSTRING(v)->ptr; + lex_pend = lex_p + RSTRING(v)->len; + lex_lastline = v; + } + else { + lex_lastline = 0; + return -1; + } + } + c = (unsigned char)*lex_p++; + if (c == '\r' && lex_p < lex_pend && *lex_p == '\n') { + lex_p++; + c = '\n'; + } + + return c; +} + +static void +pushback(c) + int c; +{ + if (c == -1) return; + lex_p--; +} + +#define was_bol() (lex_p == lex_pbeg + 1) +#define peek(c) (lex_p != lex_pend && (c) == *lex_p) + +#define tokfix() (tokenbuf[tokidx]='\0') +#define tok() tokenbuf +#define toklen() tokidx +#define toklast() (tokidx>0?tokenbuf[tokidx-1]:0) + +static char* +newtok() +{ + tokidx = 0; + if (!tokenbuf) { + toksiz = 60; + tokenbuf = ALLOC_N(char, 60); + } + if (toksiz > 4096) { + toksiz = 60; + REALLOC_N(tokenbuf, char, 60); + } + return tokenbuf; +} + +static void +tokadd(c) + char c; +{ + tokenbuf[tokidx++] = c; + if (tokidx >= toksiz) { + toksiz *= 2; + REALLOC_N(tokenbuf, char, toksiz); + } +} + +static int +read_escape() +{ + int c; + + switch (c = nextc()) { + case '\\': /* Backslash */ + return c; + + case 'n': /* newline */ + return '\n'; + + case 't': /* horizontal tab */ + return '\t'; + + case 'r': /* carriage-return */ + return '\r'; + + case 'f': /* form-feed */ + return '\f'; + + case 'v': /* vertical tab */ + return '\13'; + + case 'a': /* alarm(bell) */ + return '\007'; + + case 'e': /* escape */ + return 033; + + case '0': case '1': case '2': case '3': /* octal constant */ + case '4': case '5': case '6': case '7': + { + int numlen; + + pushback(c); + c = scan_oct(lex_p, 3, &numlen); + lex_p += numlen; + } + return c; + + case 'x': /* hex constant */ + { + int numlen; + + c = scan_hex(lex_p, 2, &numlen); + if (numlen == 0) { + yyerror("Invalid escape character syntax"); + return 0; + } + lex_p += numlen; + } + return c; + + case 'b': /* backspace */ + return '\010'; + + case 's': /* space */ + return ' '; + + case 'M': + if ((c = nextc()) != '-') { + yyerror("Invalid escape character syntax"); + pushback(c); + return '\0'; + } + if ((c = nextc()) == '\\') { + return read_escape() | 0x80; + } + else if (c == -1) goto eof; + else { + return ((c & 0xff) | 0x80); + } + + case 'C': + if ((c = nextc()) != '-') { + yyerror("Invalid escape character syntax"); + pushback(c); + return '\0'; + } + case 'c': + if ((c = nextc())== '\\') { + c = read_escape(); + } + else if (c == '?') + return 0177; + else if (c == -1) goto eof; + return c & 0x9f; + + eof: + case -1: + yyerror("Invalid escape character syntax"); + return '\0'; + + default: + return c; + } +} + +static int +tokadd_escape(term) + int term; +{ + int c; + + switch (c = nextc()) { + case '\n': + return 0; /* just ignore */ + + case '0': case '1': case '2': case '3': /* octal constant */ + case '4': case '5': case '6': case '7': + { + int i; + + tokadd('\\'); + tokadd(c); + for (i=0; i<2; i++) { + c = nextc(); + if (c == -1) goto eof; + if (c < '0' || '7' < c) { + pushback(c); + break; + } + tokadd(c); + } + } + return 0; + + case 'x': /* hex constant */ + { + int numlen; + + tokadd('\\'); + tokadd(c); + scan_hex(lex_p, 2, &numlen); + if (numlen == 0) { + yyerror("Invalid escape character syntax"); + return -1; + } + while (numlen--) + tokadd(nextc()); + } + return 0; + + case 'M': + if ((c = nextc()) != '-') { + yyerror("Invalid escape character syntax"); + pushback(c); + return 0; + } + tokadd('\\'); tokadd('M'); tokadd('-'); + goto escaped; + + case 'C': + if ((c = nextc()) != '-') { + yyerror("Invalid escape character syntax"); + pushback(c); + return 0; + } + tokadd('\\'); tokadd('C'); tokadd('-'); + goto escaped; + + case 'c': + tokadd('\\'); tokadd('c'); + escaped: + if ((c = nextc()) == '\\') { + return tokadd_escape(term); + } + else if (c == -1) goto eof; + tokadd(c); + return 0; + + eof: + case -1: + yyerror("Invalid escape character syntax"); + return -1; + + default: + if (c != '\\' || c != term) + tokadd('\\'); + tokadd(c); + } + return 0; +} + +static int +regx_options() +{ + char kcode = 0; + int options = 0; + int c; + + newtok(); + while (c = nextc(), ISALPHA(c)) { + switch (c) { + case 'i': + options |= RE_OPTION_IGNORECASE; + break; + case 'x': + options |= RE_OPTION_EXTENDED; + break; + case 'm': + options |= RE_OPTION_MULTILINE; + break; + case 'o': + options |= RE_OPTION_ONCE; + break; + case 'n': + kcode = 16; + break; + case 'e': + kcode = 32; + break; + case 's': + kcode = 48; + break; + case 'u': + kcode = 64; + break; + default: + tokadd(c); + break; + } + } + pushback(c); + if (toklen()) { + tokfix(); + rb_compile_error("unknown regexp option%s - %s", + toklen() > 1 ? "s" : "", tok()); + } + return options | kcode; +} + +#define STR_FUNC_ESCAPE 0x01 +#define STR_FUNC_EXPAND 0x02 +#define STR_FUNC_REGEXP 0x04 +#define STR_FUNC_QWORDS 0x08 +#define STR_FUNC_SYMBOL 0x10 +#define STR_FUNC_INDENT 0x20 + +enum string_type { + str_squote = (0), + str_dquote = (STR_FUNC_EXPAND), + str_xquote = (STR_FUNC_EXPAND), + str_regexp = (STR_FUNC_REGEXP|STR_FUNC_ESCAPE|STR_FUNC_EXPAND), + str_sword = (STR_FUNC_QWORDS), + str_dword = (STR_FUNC_QWORDS|STR_FUNC_EXPAND), + str_ssym = (STR_FUNC_SYMBOL), + str_dsym = (STR_FUNC_SYMBOL|STR_FUNC_EXPAND), +}; + +static void +dispose_string(str) + VALUE str; +{ + xfree(RSTRING(str)->ptr); + rb_gc_force_recycle(str); +} + +static int +tokadd_string(func, term, paren, nest) + int func, term, paren, *nest; +{ + int c; + + while ((c = nextc()) != -1) { + if (paren && c == paren) { + ++*nest; + } + else if (c == term) { + if (!nest || !*nest) { + pushback(c); + break; + } + --*nest; + } + else if ((func & STR_FUNC_EXPAND) && c == '#' && lex_p < lex_pend) { + int c2 = *lex_p; + if (c2 == '$' || c2 == '@' || c2 == '{') { + pushback(c); + break; + } + } + else if (c == '\\') { + c = nextc(); + switch (c) { + case '\n': + if (func & STR_FUNC_QWORDS) break; + if (func & STR_FUNC_EXPAND) continue; + tokadd('\\'); + break; + + case '\\': + if (func & STR_FUNC_ESCAPE) tokadd(c); + break; + + default: + if (func & STR_FUNC_REGEXP) { + pushback(c); + if (tokadd_escape(term) < 0) + return -1; + continue; + } + else if (func & STR_FUNC_EXPAND) { + pushback(c); + if (func & STR_FUNC_ESCAPE) tokadd('\\'); + c = read_escape(); + } + else if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) { + /* ignore backslashed spaces in %w */ + } + else if (c != term && !(paren && c == paren)) { + tokadd('\\'); + } + } + } + else if (ismbchar(c)) { + int i, len = mbclen(c)-1; + + for (i = 0; i < len; i++) { + tokadd(c); + c = nextc(); + } + } + else if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) { + pushback(c); + break; + } + if (!c && (func & STR_FUNC_SYMBOL)) { + func &= ~STR_FUNC_SYMBOL; + rb_compile_error("symbol cannot contain '\\0'"); + continue; + } + tokadd(c); + } + return c; +} + +#define NEW_STRTERM(func, term, paren) \ + rb_node_newnode(NODE_STRTERM, (func), (term) | ((paren) << (CHAR_BIT * 2)), 0) + +static int +parse_string(quote) + NODE *quote; +{ + int func = quote->nd_func; + int term = nd_term(quote); + int paren = nd_paren(quote); + int c, space = 0; + + if (func == -1) return tSTRING_END; + c = nextc(); + if ((func & STR_FUNC_QWORDS) && ISSPACE(c)) { + do {c = nextc();} while (ISSPACE(c)); + space = 1; + } + if (c == term && !quote->nd_nest) { + if (func & STR_FUNC_QWORDS) { + quote->nd_func = -1; + return ' '; + } + if (!(func & STR_FUNC_REGEXP)) return tSTRING_END; + yylval.num = regx_options(); + return tREGEXP_END; + } + if (space) { + pushback(c); + return ' '; + } + newtok(); + if ((func & STR_FUNC_EXPAND) && c == '#') { + switch (c = nextc()) { + case '$': + case '@': + pushback(c); + return tSTRING_DVAR; + case '{': + return tSTRING_DBEG; + } + tokadd('#'); + } + pushback(c); + if (tokadd_string(func, term, paren, "e->nd_nest) == -1) { + ruby_sourceline = nd_line(quote); + rb_compile_error("unterminated string meets end of file"); + return tSTRING_END; + } + + tokfix(); + yylval.node = NEW_STR(rb_str_new(tok(), toklen())); + return tSTRING_CONTENT; +} + +static int +heredoc_identifier() +{ + int c = nextc(), term, func = 0, len; + + if (c == '-') { + c = nextc(); + func = STR_FUNC_INDENT; + } + switch (c) { + case '\'': + func |= str_squote; goto quoted; + case '"': + func |= str_dquote; goto quoted; + case '`': + func |= str_xquote; + quoted: + newtok(); + tokadd(func); + term = c; + while ((c = nextc()) != -1 && c != term) { + len = mbclen(c); + do {tokadd(c);} while (--len > 0 && (c = nextc()) != -1); + } + if (c == -1) { + rb_compile_error("unterminated here document identifier"); + return 0; + } + break; + + default: + if (!is_identchar(c)) { + pushback(c); + if (func & STR_FUNC_INDENT) { + pushback('-'); + } + return 0; + } + newtok(); + term = '"'; + tokadd(func |= str_dquote); + do { + len = mbclen(c); + do {tokadd(c);} while (--len > 0 && (c = nextc()) != -1); + } while ((c = nextc()) != -1 && is_identchar(c)); + pushback(c); + break; + } + + tokfix(); + len = lex_p - lex_pbeg; + lex_p = lex_pend; + lex_strterm = rb_node_newnode(NODE_HEREDOC, + rb_str_new(tok(), toklen()), /* nd_lit */ + len, /* nd_nth */ + lex_lastline); /* nd_orig */ + return term == '`' ? tXSTRING_BEG : tSTRING_BEG; +} + +static void +heredoc_restore(here) + NODE *here; +{ + VALUE line = here->nd_orig; + lex_lastline = line; + lex_pbeg = RSTRING(line)->ptr; + lex_pend = lex_pbeg + RSTRING(line)->len; + lex_p = lex_pbeg + here->nd_nth; + heredoc_end = ruby_sourceline; + ruby_sourceline = nd_line(here); + dispose_string(here->nd_lit); + rb_gc_force_recycle((VALUE)here); +} + +static int +whole_match_p(eos, len, indent) + char *eos; + int len, indent; +{ + char *p = lex_pbeg; + int n; + + if (indent) { + while (*p && ISSPACE(*p)) p++; + } + n= lex_pend - (p + len); + if (n < 0 || (n > 0 && p[len] != '\n' && p[len] != '\r')) return Qfalse; + if (strncmp(eos, p, len) == 0) return Qtrue; + return Qfalse; +} + +static int +here_document(here) + NODE *here; +{ + int c, func, indent = 0; + char *eos, *p, *pend; + long len; + VALUE str = 0; + + eos = RSTRING(here->nd_lit)->ptr; + len = RSTRING(here->nd_lit)->len - 1; + indent = (func = *eos++) & STR_FUNC_INDENT; + + if ((c = nextc()) == -1) { + error: + rb_compile_error("can't find string \"%s\" anywhere before EOF", eos); + heredoc_restore(lex_strterm); + lex_strterm = 0; + return 0; + } + if (was_bol() && whole_match_p(eos, len, indent)) { + heredoc_restore(lex_strterm); + return tSTRING_END; + } + + if (!(func & STR_FUNC_EXPAND)) { + do { + p = RSTRING(lex_lastline)->ptr; + pend = lex_pend; + if (pend > p) { + switch (pend[-1]) { + case '\n': + if (--pend == p || pend[-1] != '\r') { + pend++; + break; + } + case '\r': + --pend; + } + } + if (str) + rb_str_cat(str, p, pend - p); + else + str = rb_str_new(p, pend - p); + if (pend < lex_pend) rb_str_cat(str, "\n", 1); + lex_p = lex_pend; + if (nextc() == -1) { + if (str) dispose_string(str); + goto error; + } + } while (!whole_match_p(eos, len, indent)); + } + else { + newtok(); + if (c == '#') { + switch (c = nextc()) { + case '$': + case '@': + pushback(c); + return tSTRING_DVAR; + case '{': + return tSTRING_DBEG; + } + tokadd('#'); + } + do { + pushback(c); + if ((c = tokadd_string(func, '\n', 0, NULL)) == -1) goto error; + if (c != '\n') { + yylval.node = NEW_STR(rb_str_new(tok(), toklen())); + return tSTRING_CONTENT; + } + tokadd(nextc()); + if ((c = nextc()) == -1) goto error; + } while (!whole_match_p(eos, len, indent)); + str = rb_str_new(tok(), toklen()); + } + heredoc_restore(lex_strterm); + lex_strterm = NEW_STRTERM(-1, 0, 0); + yylval.node = NEW_STR(str); + return tSTRING_CONTENT; +} + +#include "lex.c" + +static void +arg_ambiguous() +{ + rb_warning("ambiguous first argument; put parentheses or even spaces"); +} + +#define IS_ARG() (lex_state == EXPR_ARG || lex_state == EXPR_CMDARG) + +static int +yylex() +{ + register int c; + int space_seen = 0; + int cmd_state; + enum lex_state last_state; + + if (lex_strterm) { + int token; + if (nd_type(lex_strterm) == NODE_HEREDOC) { + token = here_document(lex_strterm); + if (token == tSTRING_END) { + lex_strterm = 0; + lex_state = EXPR_END; + } + } + else { + token = parse_string(lex_strterm); + if (token == tSTRING_END || token == tREGEXP_END) { + rb_gc_force_recycle((VALUE)lex_strterm); + lex_strterm = 0; + lex_state = EXPR_END; + } + } + return token; + } + cmd_state = command_start; + command_start = Qfalse; + retry: + switch (c = nextc()) { + case '\0': /* NUL */ + case '\004': /* ^D */ + case '\032': /* ^Z */ + case -1: /* end of script. */ + return 0; + + /* white spaces */ + case ' ': case '\t': case '\f': case '\r': + case '\13': /* '\v' */ + space_seen++; + goto retry; + + case '#': /* it's a comment */ + while ((c = nextc()) != '\n') { + if (c == -1) + return 0; + } + /* fall through */ + case '\n': + switch (lex_state) { + case EXPR_BEG: + case EXPR_FNAME: + case EXPR_DOT: + case EXPR_CLASS: + goto retry; + default: + break; + } + command_start = Qtrue; + lex_state = EXPR_BEG; + return '\n'; + + case '*': + if ((c = nextc()) == '*') { + if ((c = nextc()) == '=') { + yylval.id = tPOW; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + c = tPOW; + } + else { + if (c == '=') { + yylval.id = '*'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + if (IS_ARG() && space_seen && !ISSPACE(c)){ + rb_warning("`*' interpreted as argument prefix"); + c = tSTAR; + } + else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = tSTAR; + } + else { + c = '*'; + } + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + return c; + + case '!': + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + return tNEQ; + } + if (c == '~') { + return tNMATCH; + } + pushback(c); + return '!'; + + case '=': + if (was_bol()) { + /* skip embedded rd document */ + if (strncmp(lex_p, "begin", 5) == 0 && ISSPACE(lex_p[5])) { + for (;;) { + lex_p = lex_pend; + c = nextc(); + if (c == -1) { + rb_compile_error("embedded document meets end of file"); + return 0; + } + if (c != '=') continue; + if (strncmp(lex_p, "end", 3) == 0 && + (lex_p + 3 == lex_pend || ISSPACE(lex_p[3]))) { + break; + } + } + lex_p = lex_pend; + goto retry; + } + } + + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + if ((c = nextc()) == '=') { + if ((c = nextc()) == '=') { + return tEQQ; + } + pushback(c); + return tEQ; + } + if (c == '~') { + return tMATCH; + } + else if (c == '>') { + return tASSOC; + } + pushback(c); + return '='; + + case '<': + c = nextc(); + if (c == '<' && + lex_state != EXPR_END && + lex_state != EXPR_DOT && + lex_state != EXPR_ENDARG && + lex_state != EXPR_CLASS && + (!IS_ARG() || space_seen)) { + int token = heredoc_identifier(); + if (token) return token; + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + if (c == '=') { + if ((c = nextc()) == '>') { + return tCMP; + } + pushback(c); + return tLEQ; + } + if (c == '<') { + if ((c = nextc()) == '=') { + yylval.id = tLSHFT; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + return tLSHFT; + } + pushback(c); + return '<'; + + case '>': + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + if ((c = nextc()) == '=') { + return tGEQ; + } + if (c == '>') { + if ((c = nextc()) == '=') { + yylval.id = tRSHFT; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + return tRSHFT; + } + pushback(c); + return '>'; + + case '"': + lex_strterm = NEW_STRTERM(str_dquote, '"', 0); + return tSTRING_BEG; + + case '`': + if (lex_state == EXPR_FNAME) { + lex_state = EXPR_END; + return c; + } + if (lex_state == EXPR_DOT) { + if (cmd_state) + lex_state = EXPR_CMDARG; + else + lex_state = EXPR_ARG; + return c; + } + lex_strterm = NEW_STRTERM(str_xquote, '`', 0); + return tXSTRING_BEG; + + case '\'': + lex_strterm = NEW_STRTERM(str_squote, '\'', 0); + return tSTRING_BEG; + + case '?': + if (lex_state == EXPR_END || lex_state == EXPR_ENDARG) { + lex_state = EXPR_BEG; + return '?'; + } + c = nextc(); + if (c == -1) { + rb_compile_error("incomplete character syntax"); + return 0; + } + if (ISSPACE(c)){ + if (!IS_ARG()){ + int c2 = 0; + switch (c) { + case ' ': + c2 = 's'; + break; + case '\n': + c2 = 'n'; + break; + case '\t': + c2 = 't'; + break; + case '\v': + c2 = 'v'; + break; + case '\r': + c2 = 'r'; + break; + case '\f': + c2 = 'f'; + break; + } + if (c2) { + rb_warn("invalid character syntax; use ?\\%c", c2); + } + } + ternary: + pushback(c); + lex_state = EXPR_BEG; + return '?'; + } + else if (ismbchar(c)) { + rb_warn("multibyte character literal not supported yet; use ?\\%.3o", c); + goto ternary; + } + else if ((ISALNUM(c) || c == '_') && lex_p < lex_pend && is_identchar(*lex_p)) { + goto ternary; + } + else if (c == '\\') { + c = read_escape(); + } + c &= 0xff; + lex_state = EXPR_END; + yylval.node = NEW_LIT(INT2FIX(c)); + return tINTEGER; + + case '&': + if ((c = nextc()) == '&') { + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + yylval.id = tANDOP; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + return tANDOP; + } + else if (c == '=') { + yylval.id = '&'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + if (IS_ARG() && space_seen && !ISSPACE(c)){ + rb_warning("`&' interpreted as argument prefix"); + c = tAMPER; + } + else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = tAMPER; + } + else { + c = '&'; + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; + } + return c; + + case '|': + if ((c = nextc()) == '|') { + lex_state = EXPR_BEG; + if ((c = nextc()) == '=') { + yylval.id = tOROP; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + return tOROP; + } + if (c == '=') { + yylval.id = '|'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + lex_state = EXPR_ARG; + } + else { + lex_state = EXPR_BEG; + } + pushback(c); + return '|'; + + case '+': + c = nextc(); + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + lex_state = EXPR_ARG; + if (c == '@') { + return tUPLUS; + } + pushback(c); + return '+'; + } + if (c == '=') { + yylval.id = '+'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (lex_state == EXPR_BEG || lex_state == EXPR_MID || + (IS_ARG() && space_seen && !ISSPACE(c))) { + if (IS_ARG()) arg_ambiguous(); + lex_state = EXPR_BEG; + pushback(c); + if (ISDIGIT(c)) { + c = '+'; + goto start_num; + } + return tUPLUS; + } + lex_state = EXPR_BEG; + pushback(c); + return '+'; + + case '-': + c = nextc(); + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + lex_state = EXPR_ARG; + if (c == '@') { + return tUMINUS; + } + pushback(c); + return '-'; + } + if (c == '=') { + yylval.id = '-'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (lex_state == EXPR_BEG || lex_state == EXPR_MID || + (IS_ARG() && space_seen && !ISSPACE(c))) { + if (IS_ARG()) arg_ambiguous(); + lex_state = EXPR_BEG; + pushback(c); + if (ISDIGIT(c)) { + return tUMINUS_NUM; + } + return tUMINUS; + } + lex_state = EXPR_BEG; + pushback(c); + return '-'; + + case '.': + lex_state = EXPR_BEG; + if ((c = nextc()) == '.') { + if ((c = nextc()) == '.') { + return tDOT3; + } + pushback(c); + return tDOT2; + } + pushback(c); + if (ISDIGIT(c)) { + yyerror("no . floating literal anymore; put 0 before dot"); + } + lex_state = EXPR_DOT; + return '.'; + + start_num: + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int is_float, seen_point, seen_e, nondigit; + + is_float = seen_point = seen_e = nondigit = 0; + lex_state = EXPR_END; + newtok(); + if (c == '-' || c == '+') { + tokadd(c); + c = nextc(); + } + if (c == '0') { + int start = toklen(); + c = nextc(); + if (c == 'x' || c == 'X') { + /* hexadecimal */ + c = nextc(); + if (ISXDIGIT(c)) { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (!ISXDIGIT(c)) break; + nondigit = 0; + tokadd(c); + } while ((c = nextc()) != -1); + } + pushback(c); + tokfix(); + if (toklen() == start) { + yyerror("numeric literal without digits"); + } + else if (nondigit) goto trailing_uc; + yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 16, Qfalse)); + return tINTEGER; + } + if (c == 'b' || c == 'B') { + /* binary */ + c = nextc(); + if (c == '0' || c == '1') { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (c != '0' && c != '1') break; + nondigit = 0; + tokadd(c); + } while ((c = nextc()) != -1); + } + pushback(c); + tokfix(); + if (toklen() == start) { + yyerror("numeric literal without digits"); + } + else if (nondigit) goto trailing_uc; + yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 2, Qfalse)); + return tINTEGER; + } + if (c == 'd' || c == 'D') { + /* decimal */ + c = nextc(); + if (ISDIGIT(c)) { + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (!ISDIGIT(c)) break; + nondigit = 0; + tokadd(c); + } while ((c = nextc()) != -1); + } + pushback(c); + tokfix(); + if (toklen() == start) { + yyerror("numeric literal without digits"); + } + else if (nondigit) goto trailing_uc; + yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 10, Qfalse)); + return tINTEGER; + } + if (c == '_') { + /* 0_0 */ + goto octal_number; + } + if (c == 'o' || c == 'O') { + /* prefixed octal */ + c = nextc(); + if (c == '_') { + yyerror("numeric literal without digits"); + } + } + if (c >= '0' && c <= '7') { + /* octal */ + octal_number: + do { + if (c == '_') { + if (nondigit) break; + nondigit = c; + continue; + } + if (c < '0' || c > '7') break; + nondigit = 0; + tokadd(c); + } while ((c = nextc()) != -1); + if (toklen() > start) { + pushback(c); + tokfix(); + if (nondigit) goto trailing_uc; + yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 8, Qfalse)); + return tINTEGER; + } + if (nondigit) { + pushback(c); + goto trailing_uc; + } + } + if (c > '7' && c <= '9') { + yyerror("Illegal octal digit"); + } + else if (c == '.' || c == 'e' || c == 'E') { + tokadd('0'); + } + else { + pushback(c); + yylval.node = NEW_LIT(INT2FIX(0)); + return tINTEGER; + } + } + + for (;;) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + nondigit = 0; + tokadd(c); + break; + + case '.': + if (nondigit) goto trailing_uc; + if (seen_point || seen_e) { + goto decode_num; + } + else { + int c0 = nextc(); + if (!ISDIGIT(c0)) { + pushback(c0); + goto decode_num; + } + c = c0; + } + tokadd('.'); + tokadd(c); + is_float++; + seen_point++; + nondigit = 0; + break; + + case 'e': + case 'E': + if (nondigit) { + pushback(c); + c = nondigit; + goto decode_num; + } + if (seen_e) { + goto decode_num; + } + tokadd(c); + seen_e++; + is_float++; + nondigit = c; + c = nextc(); + if (c != '-' && c != '+') continue; + tokadd(c); + nondigit = c; + break; + + case '_': /* `_' in number just ignored */ + if (nondigit) goto decode_num; + nondigit = c; + break; + + default: + goto decode_num; + } + c = nextc(); + } + + decode_num: + pushback(c); + tokfix(); + if (nondigit) { + char tmp[30]; + trailing_uc: + sprintf(tmp, "trailing `%c' in number", nondigit); + yyerror(tmp); + } + if (is_float) { + double d = strtod(tok(), 0); + if (errno == ERANGE) { + rb_warn("Float %s out of range", tok()); + errno = 0; + } + yylval.node = NEW_LIT(rb_float_new(d)); + return tFLOAT; + } + yylval.node = NEW_LIT(rb_cstr_to_inum(tok(), 10, Qfalse)); + return tINTEGER; + } + + case ']': + case '}': + case ')': + COND_LEXPOP(); + CMDARG_LEXPOP(); + lex_state = EXPR_END; + return c; + + case ':': + c = nextc(); + if (c == ':') { + if (lex_state == EXPR_BEG || lex_state == EXPR_MID || + lex_state == EXPR_CLASS || (IS_ARG() && space_seen)) { + lex_state = EXPR_BEG; + return tCOLON3; + } + lex_state = EXPR_DOT; + return tCOLON2; + } + if (lex_state == EXPR_END || lex_state == EXPR_ENDARG || ISSPACE(c)) { + pushback(c); + lex_state = EXPR_BEG; + return ':'; + } + switch (c) { + case '\'': + lex_strterm = NEW_STRTERM(str_ssym, c, 0); + break; + case '"': + lex_strterm = NEW_STRTERM(str_dsym, c, 0); + break; + default: + pushback(c); + break; + } + lex_state = EXPR_FNAME; + return tSYMBEG; + + case '/': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + lex_strterm = NEW_STRTERM(str_regexp, '/', 0); + return tREGEXP_BEG; + } + if ((c = nextc()) == '=') { + yylval.id = '/'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + pushback(c); + if (IS_ARG() && space_seen) { + if (!ISSPACE(c)) { + arg_ambiguous(); + lex_strterm = NEW_STRTERM(str_regexp, '/', 0); + return tREGEXP_BEG; + } + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + return '/'; + + case '^': + if ((c = nextc()) == '=') { + yylval.id = '^'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + pushback(c); + return '^'; + + case ';': + command_start = Qtrue; + case ',': + lex_state = EXPR_BEG; + return c; + + case '~': + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + if ((c = nextc()) != '@') { + pushback(c); + } + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + return '~'; + + case '(': + command_start = Qtrue; + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = tLPAREN; + } + else if (space_seen) { + if (lex_state == EXPR_CMDARG) { + c = tLPAREN_ARG; + } + else if (lex_state == EXPR_ARG) { + rb_warn("don't put space before argument parentheses"); + c = '('; + } + } + COND_PUSH(0); + CMDARG_PUSH(0); + lex_state = EXPR_BEG; + return c; + + case '[': + if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { + lex_state = EXPR_ARG; + if ((c = nextc()) == ']') { + if ((c = nextc()) == '=') { + return tASET; + } + pushback(c); + return tAREF; + } + pushback(c); + return '['; + } + else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + c = tLBRACK; + } + else if (IS_ARG() && space_seen) { + c = tLBRACK; + } + lex_state = EXPR_BEG; + COND_PUSH(0); + CMDARG_PUSH(0); + return c; + + case '{': + if (IS_ARG() || lex_state == EXPR_END) + c = '{'; /* block (primary) */ + else if (lex_state == EXPR_ENDARG) + c = tLBRACE_ARG; /* block (expr) */ + else + c = tLBRACE; /* hash */ + COND_PUSH(0); + CMDARG_PUSH(0); + lex_state = EXPR_BEG; + return c; + + case '\\': + c = nextc(); + if (c == '\n') { + space_seen = 1; + goto retry; /* skip \\n */ + } + pushback(c); + return '\\'; + + case '%': + if (lex_state == EXPR_BEG || lex_state == EXPR_MID) { + int term; + int paren; + + c = nextc(); + quotation: + if (!ISALNUM(c)) { + term = c; + c = 'Q'; + } + else { + term = nextc(); + if (ISALNUM(term) || ismbchar(term)) { + yyerror("unknown type of %string"); + return 0; + } + } + if (c == -1 || term == -1) { + rb_compile_error("unterminated quoted string meets end of file"); + return 0; + } + paren = term; + if (term == '(') term = ')'; + else if (term == '[') term = ']'; + else if (term == '{') term = '}'; + else if (term == '<') term = '>'; + else paren = 0; + + switch (c) { + case 'Q': + lex_strterm = NEW_STRTERM(str_dquote, term, paren); + return tSTRING_BEG; + + case 'q': + lex_strterm = NEW_STRTERM(str_squote, term, paren); + return tSTRING_BEG; + + case 'W': + lex_strterm = NEW_STRTERM(str_dquote | STR_FUNC_QWORDS, term, paren); + do {c = nextc();} while (ISSPACE(c)); + pushback(c); + return tWORDS_BEG; + + case 'w': + lex_strterm = NEW_STRTERM(str_squote | STR_FUNC_QWORDS, term, paren); + do {c = nextc();} while (ISSPACE(c)); + pushback(c); + return tQWORDS_BEG; + + case 'x': + lex_strterm = NEW_STRTERM(str_xquote, term, paren); + return tXSTRING_BEG; + + case 'r': + lex_strterm = NEW_STRTERM(str_regexp, term, paren); + return tREGEXP_BEG; + + case 's': + lex_strterm = NEW_STRTERM(str_ssym, term, paren); + lex_state = EXPR_FNAME; + return tSYMBEG; + + default: + yyerror("unknown type of %string"); + return 0; + } + } + if ((c = nextc()) == '=') { + yylval.id = '%'; + lex_state = EXPR_BEG; + return tOP_ASGN; + } + if (IS_ARG() && space_seen && !ISSPACE(c)) { + goto quotation; + } + switch (lex_state) { + case EXPR_FNAME: case EXPR_DOT: + lex_state = EXPR_ARG; break; + default: + lex_state = EXPR_BEG; break; + } + pushback(c); + return '%'; + + case '$': + last_state = lex_state; + lex_state = EXPR_END; + newtok(); + c = nextc(); + switch (c) { + case '_': /* $_: last read line string */ + c = nextc(); + if (is_identchar(c)) { + tokadd('$'); + tokadd('_'); + break; + } + pushback(c); + c = '_'; + /* fall through */ + case '~': /* $~: match-data */ + local_cnt(c); + /* fall through */ + case '*': /* $*: argv */ + case '$': /* $$: pid */ + case '?': /* $?: last status */ + case '!': /* $!: error string */ + case '@': /* $@: error position */ + case '/': /* $/: input record separator */ + case '\\': /* $\: output record separator */ + case ';': /* $;: field separator */ + case ',': /* $,: output field separator */ + case '.': /* $.: last read line number */ + case '=': /* $=: ignorecase */ + case ':': /* $:: load path */ + case '<': /* $<: reading filename */ + case '>': /* $>: default output handle */ + case '\"': /* $": already loaded files */ + tokadd('$'); + tokadd(c); + tokfix(); + yylval.id = rb_intern(tok()); + return tGVAR; + + case '-': + tokadd('$'); + tokadd(c); + c = nextc(); + if (is_identchar(c)) { + tokadd(c); + } + else { + pushback(c); + } + gvar: + tokfix(); + yylval.id = rb_intern(tok()); + /* xxx shouldn't check if valid option variable */ + return tGVAR; + + case '&': /* $&: last match */ + case '`': /* $`: string before last match */ + case '\'': /* $': string after last match */ + case '+': /* $+: string matches last paren. */ + if (last_state == EXPR_FNAME) { + tokadd('$'); + tokadd(c); + goto gvar; + } + yylval.node = NEW_BACK_REF(c); + return tBACK_REF; + + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + tokadd('$'); + do { + tokadd(c); + c = nextc(); + } while (ISDIGIT(c)); + pushback(c); + if (last_state == EXPR_FNAME) goto gvar; + tokfix(); + yylval.node = NEW_NTH_REF(atoi(tok()+1)); + return tNTH_REF; + + default: + if (!is_identchar(c)) { + pushback(c); + return '$'; + } + case '0': + tokadd('$'); + } + break; + + case '@': + c = nextc(); + newtok(); + tokadd('@'); + if (c == '@') { + tokadd('@'); + c = nextc(); + } + if (ISDIGIT(c)) { + if (tokidx == 1) { + rb_compile_error("`@%c' is not allowed as an instance variable name", c); + } + else { + rb_compile_error("`@@%c' is not allowed as a class variable name", c); + } + } + if (!is_identchar(c)) { + pushback(c); + return '@'; + } + break; + + case '_': + if (was_bol() && whole_match_p("__END__", 7, 0)) { + ruby__end__seen = 1; + lex_lastline = 0; + return -1; + } + newtok(); + break; + + default: + if (!is_identchar(c)) { + rb_compile_error("Invalid char `\\%03o' in expression", c); + goto retry; + } + + newtok(); + break; + } + + do { + tokadd(c); + if (ismbchar(c)) { + int i, len = mbclen(c)-1; + + for (i = 0; i < len; i++) { + c = nextc(); + tokadd(c); + } + } + c = nextc(); + } while (is_identchar(c)); + if ((c == '!' || c == '?') && is_identchar(tok()[0]) && !peek('=')) { + tokadd(c); + } + else { + pushback(c); + } + tokfix(); + + { + int result = 0; + + last_state = lex_state; + switch (tok()[0]) { + case '$': + lex_state = EXPR_END; + result = tGVAR; + break; + case '@': + lex_state = EXPR_END; + if (tok()[1] == '@') + result = tCVAR; + else + result = tIVAR; + break; + + default: + if (toklast() == '!' || toklast() == '?') { + result = tFID; + } + else { + if (lex_state == EXPR_FNAME) { + if ((c = nextc()) == '=' && !peek('~') && !peek('>') && + (!peek('=') || (lex_p + 1 < lex_pend && lex_p[1] == '>'))) { + result = tIDENTIFIER; + tokadd(c); + tokfix(); + } + else { + pushback(c); + } + } + if (result == 0 && ISUPPER(tok()[0])) { + result = tCONSTANT; + } + else { + result = tIDENTIFIER; + } + } + + if (lex_state != EXPR_DOT) { + struct kwtable *kw; + + /* See if it is a reserved word. */ + kw = rb_reserved_word(tok(), toklen()); + if (kw) { + enum lex_state state = lex_state; + lex_state = kw->state; + if (state == EXPR_FNAME) { + yylval.id = rb_intern(kw->name); + return kw->id[0]; + } + if (kw->id[0] == kDO) { + if (COND_P()) return kDO_COND; + if (CMDARG_P() && state != EXPR_CMDARG) + return kDO_BLOCK; + if (state == EXPR_ENDARG) + return kDO_BLOCK; + return kDO; + } + if (state == EXPR_BEG) + return kw->id[0]; + else { + if (kw->id[0] != kw->id[1]) + lex_state = EXPR_BEG; + return kw->id[1]; + } + } + } + + if (lex_state == EXPR_BEG || + lex_state == EXPR_MID || + lex_state == EXPR_DOT || + lex_state == EXPR_ARG || + lex_state == EXPR_CMDARG) { + if (cmd_state) { + lex_state = EXPR_CMDARG; + } + else { + lex_state = EXPR_ARG; + } + } + else { + lex_state = EXPR_END; + } + } + yylval.id = rb_intern(tok()); + if (is_local_id(yylval.id) && + last_state != EXPR_DOT && + ((dyna_in_block() && rb_dvar_defined(yylval.id)) || local_id(yylval.id))) { + lex_state = EXPR_END; + } + return result; + } +} + +NODE* +rb_node_newnode(type, a0, a1, a2) + enum node_type type; + VALUE a0, a1, a2; +{ + NODE *n = (NODE*)rb_newobj(); + + n->flags |= T_NODE; + nd_set_type(n, type); + nd_set_line(n, ruby_sourceline); + n->nd_file = ruby_sourcefile; + + n->u1.value = a0; + n->u2.value = a1; + n->u3.value = a2; + + return n; +} + +static enum node_type +nodetype(node) /* for debug */ + NODE *node; +{ + return (enum node_type)nd_type(node); +} + +static int +nodeline(node) + NODE *node; +{ + return nd_line(node); +} + +static NODE* +newline_node(node) + NODE *node; +{ + NODE *nl = 0; + if (node) { + if (nd_type(node) == NODE_NEWLINE) return node; + nl = NEW_NEWLINE(node); + fixpos(nl, node); + nl->nd_nth = nd_line(node); + } + return nl; +} + +static void +fixpos(node, orig) + NODE *node, *orig; +{ + if (!node) return; + if (!orig) return; + if (orig == (NODE*)1) return; + node->nd_file = orig->nd_file; + nd_set_line(node, nd_line(orig)); +} + +static void +parser_warning(node, mesg) + NODE *node; + const char *mesg; +{ + int line = ruby_sourceline; + ruby_sourceline = nd_line(node); + rb_warning(mesg); + ruby_sourceline = line; +} + +static void +parser_warn(node, mesg) + NODE *node; + const char *mesg; +{ + int line = ruby_sourceline; + ruby_sourceline = nd_line(node); + rb_warn(mesg); + ruby_sourceline = line; +} + +static NODE* +block_append(head, tail) + NODE *head, *tail; +{ + NODE *end, *h = head; + + if (tail == 0) return head; + + again: + if (h == 0) return tail; + switch (nd_type(h)) { + case NODE_NEWLINE: + h = h->nd_next; + goto again; + case NODE_LIT: + case NODE_STR: + parser_warning(h, "unused literal ignored"); + return tail; + default: + h = end = NEW_BLOCK(head); + end->nd_end = end; + fixpos(end, head); + head = end; + break; + case NODE_BLOCK: + end = h->nd_end; + break; + } + + if (RTEST(ruby_verbose)) { + NODE *nd = end->nd_head; + newline: + switch (nd_type(nd)) { + case NODE_RETURN: + case NODE_BREAK: + case NODE_NEXT: + case NODE_REDO: + case NODE_RETRY: + parser_warning(nd, "statement not reached"); + break; + + case NODE_NEWLINE: + nd = nd->nd_next; + goto newline; + + default: + break; + } + } + + if (nd_type(tail) != NODE_BLOCK) { + tail = NEW_BLOCK(tail); + tail->nd_end = tail; + } + end->nd_next = tail; + h->nd_end = tail->nd_end; + return head; +} + +/* append item to the list */ +static NODE* +list_append(list, item) + NODE *list, *item; +{ + NODE *last; + + if (list == 0) return NEW_LIST(item); + if (list->nd_next) { + last = list->nd_next->nd_end; + } + else { + last = list; + } + + list->nd_alen += 1; + last->nd_next = NEW_LIST(item); + list->nd_next->nd_end = last->nd_next; + return list; +} + +/* concat two lists */ +static NODE* +list_concat(head, tail) + NODE *head, *tail; +{ + NODE *last; + + if (head->nd_next) { + last = head->nd_next->nd_end; + } + else { + last = head; + } + + head->nd_alen += tail->nd_alen; + last->nd_next = tail; + if (tail->nd_next) { + head->nd_next->nd_end = tail->nd_next->nd_end; + } + else { + head->nd_next->nd_end = tail; + } + + return head; +} + +/* concat two string literals */ +static NODE * +literal_concat(head, tail) + NODE *head, *tail; +{ + enum node_type htype; + + if (!head) return tail; + if (!tail) return head; + + htype = nd_type(head); + if (htype == NODE_EVSTR) { + NODE *node = NEW_DSTR(rb_str_new(0, 0)); + head = list_append(node, head); + } + switch (nd_type(tail)) { + case NODE_STR: + if (htype == NODE_STR) { + rb_str_concat(head->nd_lit, tail->nd_lit); + rb_gc_force_recycle((VALUE)tail); + } + else { + list_append(head, tail); + } + break; + + case NODE_DSTR: + if (htype == NODE_STR) { + rb_str_concat(head->nd_lit, tail->nd_lit); + tail->nd_lit = head->nd_lit; + rb_gc_force_recycle((VALUE)head); + head = tail; + } + else { + nd_set_type(tail, NODE_ARRAY); + tail->nd_head = NEW_STR(tail->nd_lit); + list_concat(head, tail); + } + break; + + case NODE_EVSTR: + if (htype == NODE_STR) { + nd_set_type(head, NODE_DSTR); + head->nd_alen = 1; + } + list_append(head, tail); + break; + } + return head; +} + +static NODE * +evstr2dstr(node) + NODE *node; +{ + if (nd_type(node) == NODE_EVSTR) { + node = list_append(NEW_DSTR(rb_str_new(0, 0)), node); + } + return node; +} + +static NODE * +new_evstr(node) + NODE *node; +{ + NODE *head = node; + + again: + if (node) { + switch (nd_type(node)) { + case NODE_STR: case NODE_DSTR: case NODE_EVSTR: + return node; + case NODE_NEWLINE: + node = node->nd_next; + goto again; + } + } + return NEW_EVSTR(head); +} + +static NODE * +call_op(recv, id, narg, arg1) + NODE *recv; + ID id; + int narg; + NODE *arg1; +{ + value_expr(recv); + if (narg == 1) { + value_expr(arg1); + arg1 = NEW_LIST(arg1); + } + else { + arg1 = 0; + } + return NEW_CALL(recv, id, arg1); +} + +static NODE* +match_gen(node1, node2) + NODE *node1; + NODE *node2; +{ + local_cnt('~'); + + value_expr(node1); + value_expr(node2); + if (node1) { + switch (nd_type(node1)) { + case NODE_DREGX: + case NODE_DREGX_ONCE: + return NEW_MATCH2(node1, node2); + + case NODE_LIT: + if (TYPE(node1->nd_lit) == T_REGEXP) { + return NEW_MATCH2(node1, node2); + } + } + } + + if (node2) { + switch (nd_type(node2)) { + case NODE_DREGX: + case NODE_DREGX_ONCE: + return NEW_MATCH3(node2, node1); + + case NODE_LIT: + if (TYPE(node2->nd_lit) == T_REGEXP) { + return NEW_MATCH3(node2, node1); + } + } + } + + return NEW_CALL(node1, tMATCH, NEW_LIST(node2)); +} + +static NODE* +gettable(id) + ID id; +{ + if (id == kSELF) { + return NEW_SELF(); + } + else if (id == kNIL) { + return NEW_NIL(); + } + else if (id == kTRUE) { + return NEW_TRUE(); + } + else if (id == kFALSE) { + return NEW_FALSE(); + } + else if (id == k__FILE__) { + return NEW_STR(rb_str_new2(ruby_sourcefile)); + } + else if (id == k__LINE__) { + return NEW_LIT(INT2FIX(ruby_sourceline)); + } + else if (is_local_id(id)) { + if (dyna_in_block() && rb_dvar_defined(id)) return NEW_DVAR(id); + if (local_id(id)) return NEW_LVAR(id); + /* method call without arguments */ +#if 0 + /* Rite will warn this */ + rb_warn("ambiguous identifier; %s() or self.%s is better for method call", + rb_id2name(id), rb_id2name(id)); +#endif + return NEW_VCALL(id); + } + else if (is_global_id(id)) { + return NEW_GVAR(id); + } + else if (is_instance_id(id)) { + return NEW_IVAR(id); + } + else if (is_const_id(id)) { + return NEW_CONST(id); + } + else if (is_class_id(id)) { + return NEW_CVAR(id); + } + rb_compile_error("identifier %s is not valid", rb_id2name(id)); + return 0; +} + +static VALUE dyna_var_lookup _((ID id)); + +static NODE* +assignable(id, val) + ID id; + NODE *val; +{ + value_expr(val); + if (id == kSELF) { + yyerror("Can't change the value of self"); + } + else if (id == kNIL) { + yyerror("Can't assign to nil"); + } + else if (id == kTRUE) { + yyerror("Can't assign to true"); + } + else if (id == kFALSE) { + yyerror("Can't assign to false"); + } + else if (id == k__FILE__) { + yyerror("Can't assign to __FILE__"); + } + else if (id == k__LINE__) { + yyerror("Can't assign to __LINE__"); + } + else if (is_local_id(id)) { + if (rb_dvar_curr(id)) { + return NEW_DASGN_CURR(id, val); + } + else if (dyna_var_lookup(id)) { + return NEW_DASGN(id, val); + } + else if (local_id(id) || !dyna_in_block()) { + return NEW_LASGN(id, val); + } + else{ + rb_dvar_push(id, Qnil); + return NEW_DASGN_CURR(id, val); + } + } + else if (is_global_id(id)) { + return NEW_GASGN(id, val); + } + else if (is_instance_id(id)) { + return NEW_IASGN(id, val); + } + else if (is_const_id(id)) { + if (in_def || in_single) + yyerror("dynamic constant assignment"); + return NEW_CDECL(id, val, 0); + } + else if (is_class_id(id)) { + if (in_def || in_single) return NEW_CVASGN(id, val); + return NEW_CVDECL(id, val); + } + else { + rb_compile_error("identifier %s is not valid", rb_id2name(id)); + } + return 0; +} + +static NODE * +aryset(recv, idx) + NODE *recv, *idx; +{ + if (recv && nd_type(recv) == NODE_SELF) + recv = (NODE *)1; + else + value_expr(recv); + return NEW_ATTRASGN(recv, tASET, idx); +} + +ID +rb_id_attrset(id) + ID id; +{ + id &= ~ID_SCOPE_MASK; + id |= ID_ATTRSET; + return id; +} + +static NODE * +attrset(recv, id) + NODE *recv; + ID id; +{ + if (recv && nd_type(recv) == NODE_SELF) + recv = (NODE *)1; + else + value_expr(recv); + return NEW_ATTRASGN(recv, rb_id_attrset(id), 0); +} + +static void +rb_backref_error(node) + NODE *node; +{ + switch (nd_type(node)) { + case NODE_NTH_REF: + rb_compile_error("Can't set variable $%d", node->nd_nth); + break; + case NODE_BACK_REF: + rb_compile_error("Can't set variable $%c", (int)node->nd_nth); + break; + } +} + +static NODE * +arg_concat(node1, node2) + NODE *node1; + NODE *node2; +{ + if (!node2) return node1; + return NEW_ARGSCAT(node1, node2); +} + +static NODE * +arg_add(node1, node2) + NODE *node1; + NODE *node2; +{ + if (!node1) return NEW_LIST(node2); + if (nd_type(node1) == NODE_ARRAY) { + return list_append(node1, node2); + } + else { + return NEW_ARGSPUSH(node1, node2); + } +} + +static NODE* +node_assign(lhs, rhs) + NODE *lhs, *rhs; +{ + if (!lhs) return 0; + + value_expr(rhs); + switch (nd_type(lhs)) { + case NODE_GASGN: + case NODE_IASGN: + case NODE_LASGN: + case NODE_DASGN: + case NODE_DASGN_CURR: + case NODE_MASGN: + case NODE_CDECL: + case NODE_CVDECL: + case NODE_CVASGN: + lhs->nd_value = rhs; + break; + + case NODE_ATTRASGN: + case NODE_CALL: + lhs->nd_args = arg_add(lhs->nd_args, rhs); + break; + + default: + /* should not happen */ + break; + } + + return lhs; +} + +static int +value_expr0(node) + NODE *node; +{ + int cond = 0; + + while (node) { + switch (nd_type(node)) { + case NODE_DEFN: + case NODE_DEFS: + parser_warning(node, "void value expression"); + return Qfalse; + + case NODE_RETURN: + case NODE_BREAK: + case NODE_NEXT: + case NODE_REDO: + case NODE_RETRY: + if (!cond) yyerror("void value expression"); + /* or "control never reach"? */ + return Qfalse; + + case NODE_BLOCK: + while (node->nd_next) { + node = node->nd_next; + } + node = node->nd_head; + break; + + case NODE_BEGIN: + node = node->nd_body; + break; + + case NODE_IF: + if (!value_expr(node->nd_body)) return Qfalse; + node = node->nd_else; + break; + + case NODE_AND: + case NODE_OR: + cond = 1; + node = node->nd_2nd; + break; + + case NODE_NEWLINE: + node = node->nd_next; + break; + + default: + return Qtrue; + } + } + + return Qtrue; +} + +static void +void_expr0(node) + NODE *node; +{ + char *useless = 0; + + if (!RTEST(ruby_verbose)) return; + + again: + if (!node) return; + switch (nd_type(node)) { + case NODE_NEWLINE: + node = node->nd_next; + goto again; + + case NODE_CALL: + switch (node->nd_mid) { + case '+': + case '-': + case '*': + case '/': + case '%': + case tPOW: + case tUPLUS: + case tUMINUS: + case '|': + case '^': + case '&': + case tCMP: + case '>': + case tGEQ: + case '<': + case tLEQ: + case tEQ: + case tNEQ: + useless = rb_id2name(node->nd_mid); + break; + } + break; + + case NODE_LVAR: + case NODE_DVAR: + case NODE_GVAR: + case NODE_IVAR: + case NODE_CVAR: + case NODE_NTH_REF: + case NODE_BACK_REF: + useless = "a variable"; + break; + case NODE_CONST: + case NODE_CREF: + useless = "a constant"; + break; + case NODE_LIT: + case NODE_STR: + case NODE_DSTR: + case NODE_DREGX: + case NODE_DREGX_ONCE: + useless = "a literal"; + break; + case NODE_COLON2: + case NODE_COLON3: + useless = "::"; + break; + case NODE_DOT2: + useless = ".."; + break; + case NODE_DOT3: + useless = "..."; + break; + case NODE_SELF: + useless = "self"; + break; + case NODE_NIL: + useless = "nil"; + break; + case NODE_TRUE: + useless = "true"; + break; + case NODE_FALSE: + useless = "false"; + break; + case NODE_DEFINED: + useless = "defined?"; + break; + } + + if (useless) { + int line = ruby_sourceline; + + ruby_sourceline = nd_line(node); + rb_warn("useless use of %s in void context", useless); + ruby_sourceline = line; + } +} + +static void +void_stmts(node) + NODE *node; +{ + if (!RTEST(ruby_verbose)) return; + if (!node) return; + if (nd_type(node) != NODE_BLOCK) return; + + for (;;) { + if (!node->nd_next) return; + void_expr(node->nd_head); + node = node->nd_next; + } +} + +static NODE * +remove_begin(node) + NODE *node; +{ + NODE **n = &node; + while (*n) { + switch (nd_type(*n)) { + case NODE_NEWLINE: + n = &(*n)->nd_next; + continue; + case NODE_BEGIN: + *n = (*n)->nd_body; + default: + return node; + } + } + return node; +} + +static int +assign_in_cond(node) + NODE *node; +{ + switch (nd_type(node)) { + case NODE_MASGN: + yyerror("multiple assignment in conditional"); + return 1; + + case NODE_LASGN: + case NODE_DASGN: + case NODE_GASGN: + case NODE_IASGN: + break; + + case NODE_NEWLINE: + default: + return 0; + } + + switch (nd_type(node->nd_value)) { + case NODE_LIT: + case NODE_STR: + case NODE_NIL: + case NODE_TRUE: + case NODE_FALSE: + /* reports always */ + parser_warn(node->nd_value, "found = in conditional, should be =="); + return 1; + + case NODE_DSTR: + case NODE_XSTR: + case NODE_DXSTR: + case NODE_EVSTR: + case NODE_DREGX: + default: + break; + } +#if 0 + if (assign_in_cond(node->nd_value) == 0) { + parser_warning(node->nd_value, "assignment in condition"); + } +#endif + return 1; +} + +static int +e_option_supplied() +{ + if (strcmp(ruby_sourcefile, "-e") == 0) + return Qtrue; + return Qfalse; +} + +static void +warn_unless_e_option(node, str) + NODE *node; + const char *str; +{ + if (!e_option_supplied()) parser_warn(node, str); +} + +static void +warning_unless_e_option(node, str) + NODE *node; + const char *str; +{ + if (!e_option_supplied()) parser_warning(node, str); +} + +static NODE *cond0(); + +static NODE* +range_op(node) + NODE *node; +{ + enum node_type type; + + if (!e_option_supplied()) return node; + if (node == 0) return 0; + + value_expr(node); + node = cond0(node); + type = nd_type(node); + if (type == NODE_NEWLINE) { + node = node->nd_next; + type = nd_type(node); + } + if (type == NODE_LIT && FIXNUM_P(node->nd_lit)) { + warn_unless_e_option(node, "integer literal in conditional range"); + return call_op(node,tEQ,1,NEW_GVAR(rb_intern("$."))); + } + return node; +} + +static int +literal_node(node) + NODE *node; +{ + if (!node) return 1; /* same as NODE_NIL */ + switch (nd_type(node)) { + case NODE_LIT: + case NODE_STR: + case NODE_DSTR: + case NODE_EVSTR: + case NODE_DREGX: + case NODE_DREGX_ONCE: + case NODE_DSYM: + return 2; + case NODE_TRUE: + case NODE_FALSE: + case NODE_NIL: + return 1; + } + return 0; +} + +static NODE* +cond0(node) + NODE *node; +{ + if (node == 0) return 0; + assign_in_cond(node); + + switch (nd_type(node)) { + case NODE_DSTR: + case NODE_EVSTR: + case NODE_STR: + rb_warn("string literal in condition"); + break; + + case NODE_DREGX: + case NODE_DREGX_ONCE: + warning_unless_e_option(node, "regex literal in condition"); + local_cnt('_'); + local_cnt('~'); + return NEW_MATCH2(node, NEW_GVAR(rb_intern("$_"))); + + case NODE_AND: + case NODE_OR: + node->nd_1st = cond0(node->nd_1st); + node->nd_2nd = cond0(node->nd_2nd); + break; + + case NODE_DOT2: + case NODE_DOT3: + node->nd_beg = range_op(node->nd_beg); + node->nd_end = range_op(node->nd_end); + if (nd_type(node) == NODE_DOT2) nd_set_type(node,NODE_FLIP2); + else if (nd_type(node) == NODE_DOT3) nd_set_type(node, NODE_FLIP3); + node->nd_cnt = local_append(internal_id()); + if (!e_option_supplied()) { + int b = literal_node(node->nd_beg); + int e = literal_node(node->nd_end); + if ((b == 1 && e == 1) || (b + e >= 2 && RTEST(ruby_verbose))) { + parser_warn(node, "range literal in condition"); + } + } + break; + + case NODE_DSYM: + parser_warning(node, "literal in condition"); + break; + + case NODE_LIT: + if (TYPE(node->nd_lit) == T_REGEXP) { + warn_unless_e_option(node, "regex literal in condition"); + nd_set_type(node, NODE_MATCH); + local_cnt('_'); + local_cnt('~'); + } + else { + parser_warning(node, "literal in condition"); + } + default: + break; + } + return node; +} + +static NODE* +cond(node) + NODE *node; +{ + if (node == 0) return 0; + value_expr(node); + if (nd_type(node) == NODE_NEWLINE){ + node->nd_next = cond0(node->nd_next); + return node; + } + return cond0(node); +} + +static NODE* +logop(type, left, right) + enum node_type type; + NODE *left, *right; +{ + value_expr(left); + if (left && nd_type(left) == type) { + NODE *node = left, *second; + while ((second = node->nd_2nd) != 0 && nd_type(second) == type) { + node = second; + } + node->nd_2nd = NEW_NODE(type, second, right, 0); + return left; + } + return NEW_NODE(type, left, right, 0); +} + +static int +cond_negative(nodep) + NODE **nodep; +{ + NODE *c = *nodep; + + if (!c) return 0; + switch (nd_type(c)) { + case NODE_NOT: + *nodep = c->nd_body; + return 1; + case NODE_NEWLINE: + if (c->nd_next && nd_type(c->nd_next) == NODE_NOT) { + c->nd_next = c->nd_next->nd_body; + return 1; + } + } + return 0; +} + +static void +no_blockarg(node) + NODE *node; +{ + if (node && nd_type(node) == NODE_BLOCK_PASS) { + rb_compile_error("block argument should not be given"); + } +} + +static NODE * +ret_args(node) + NODE *node; +{ + if (node) { + no_blockarg(node); + if (nd_type(node) == NODE_ARRAY && node->nd_next == 0) { + node = node->nd_head; + } + if (node && nd_type(node) == NODE_SPLAT) { + node = NEW_SVALUE(node); + } + } + return node; +} + +static NODE * +new_yield(node) + NODE *node; +{ + long state = Qtrue; + + if (node) { + no_blockarg(node); + if (nd_type(node) == NODE_ARRAY && node->nd_next == 0) { + node = node->nd_head; + state = Qfalse; + } + if (node && nd_type(node) == NODE_SPLAT) { + state = Qtrue; + } + } + else { + state = Qfalse; + } + return NEW_YIELD(node, state); +} + +static NODE* +negate_lit(node) + NODE *node; +{ + switch (TYPE(node->nd_lit)) { + case T_FIXNUM: + node->nd_lit = LONG2FIX(-FIX2LONG(node->nd_lit)); + break; + case T_BIGNUM: + node->nd_lit = rb_funcall(node->nd_lit,tUMINUS,0,0); + break; + case T_FLOAT: + RFLOAT(node->nd_lit)->value = -RFLOAT(node->nd_lit)->value; + break; + default: + break; + } + return node; +} + +static NODE * +arg_blk_pass(node1, node2) + NODE *node1; + NODE *node2; +{ + if (node2) { + node2->nd_head = node1; + return node2; + } + return node1; +} + +static NODE* +arg_prepend(node1, node2) + NODE *node1, *node2; +{ + switch (nd_type(node2)) { + case NODE_ARRAY: + return list_concat(NEW_LIST(node1), node2); + + case NODE_SPLAT: + return arg_concat(node1, node2->nd_head); + + case NODE_BLOCK_PASS: + node2->nd_body = arg_prepend(node1, node2->nd_body); + return node2; + + default: + rb_bug("unknown nodetype(%d) for arg_prepend", nd_type(node2)); + } + return 0; /* not reached */ +} + +static NODE* +new_call(r,m,a) + NODE *r; + ID m; + NODE *a; +{ + if (a && nd_type(a) == NODE_BLOCK_PASS) { + a->nd_iter = NEW_CALL(r,m,a->nd_head); + return a; + } + return NEW_CALL(r,m,a); +} + +static NODE* +new_fcall(m,a) + ID m; + NODE *a; +{ + if (a && nd_type(a) == NODE_BLOCK_PASS) { + a->nd_iter = NEW_FCALL(m,a->nd_head); + return a; + } + return NEW_FCALL(m,a); +} + +static NODE* +new_super(a) + NODE *a; +{ + if (a && nd_type(a) == NODE_BLOCK_PASS) { + a->nd_iter = NEW_SUPER(a->nd_head); + return a; + } + return NEW_SUPER(a); +} + +static struct local_vars { + ID *tbl; + int nofree; + int cnt; + int dlev; + struct RVarmap* dyna_vars; + struct local_vars *prev; +} *lvtbl; + +static void +local_push(top) + int top; +{ + struct local_vars *local; + + local = ALLOC(struct local_vars); + local->prev = lvtbl; + local->nofree = 0; + local->cnt = 0; + local->tbl = 0; + local->dlev = 0; + local->dyna_vars = ruby_dyna_vars; + lvtbl = local; + if (!top) { + /* preserve reference for GC, but link should be cut. */ + rb_dvar_push(0, (VALUE)ruby_dyna_vars); + ruby_dyna_vars->next = 0; + } +} + +static void +local_pop() +{ + struct local_vars *local = lvtbl->prev; + + if (lvtbl->tbl) { + if (!lvtbl->nofree) xfree(lvtbl->tbl); + else lvtbl->tbl[0] = lvtbl->cnt; + } + ruby_dyna_vars = lvtbl->dyna_vars; + xfree(lvtbl); + lvtbl = local; +} + +static ID* +local_tbl() +{ + lvtbl->nofree = 1; + return lvtbl->tbl; +} + +static int +local_append(id) + ID id; +{ + if (lvtbl->tbl == 0) { + lvtbl->tbl = ALLOC_N(ID, 4); + lvtbl->tbl[0] = 0; + lvtbl->tbl[1] = '_'; + lvtbl->tbl[2] = '~'; + lvtbl->cnt = 2; + if (id == '_') return 0; + if (id == '~') return 1; + } + else { + REALLOC_N(lvtbl->tbl, ID, lvtbl->cnt+2); + } + + lvtbl->tbl[lvtbl->cnt+1] = id; + return lvtbl->cnt++; +} + +static int +local_cnt(id) + ID id; +{ + int cnt, max; + + if (id == 0) return lvtbl->cnt; + + for (cnt=1, max=lvtbl->cnt+1; cnttbl[cnt] == id) return cnt-1; + } + return local_append(id); +} + +static int +local_id(id) + ID id; +{ + int i, max; + + if (lvtbl == 0) return Qfalse; + for (i=3, max=lvtbl->cnt+1; itbl[i] == id) return Qtrue; + } + return Qfalse; +} + +static void +top_local_init() +{ + local_push(1); + lvtbl->cnt = ruby_scope->local_tbl?ruby_scope->local_tbl[0]:0; + if (lvtbl->cnt > 0) { + lvtbl->tbl = ALLOC_N(ID, lvtbl->cnt+3); + MEMCPY(lvtbl->tbl, ruby_scope->local_tbl, ID, lvtbl->cnt+1); + } + else { + lvtbl->tbl = 0; + } + if (ruby_dyna_vars) + lvtbl->dlev = 1; + else + lvtbl->dlev = 0; +} + +static void +top_local_setup() +{ + int len = lvtbl->cnt; + int i; + + if (len > 0) { + i = ruby_scope->local_tbl?ruby_scope->local_tbl[0]:0; + + if (i < len) { + if (i == 0 || (ruby_scope->flags & SCOPE_MALLOC) == 0) { + VALUE *vars = ALLOC_N(VALUE, len+1); + if (ruby_scope->local_vars) { + *vars++ = ruby_scope->local_vars[-1]; + MEMCPY(vars, ruby_scope->local_vars, VALUE, i); + rb_mem_clear(vars+i, len-i); + } + else { + *vars++ = (VALUE)ruby_scope; + rb_mem_clear(vars, len); + } + ruby_scope->local_vars = vars; + ruby_scope->flags |= SCOPE_MALLOC; + } + else { + VALUE *vars = ruby_scope->local_vars-1; + REALLOC_N(vars, VALUE, len+1); + ruby_scope->local_vars = vars+1; + rb_mem_clear(ruby_scope->local_vars+i, len-i); + } + if (ruby_scope->local_tbl && ruby_scope->local_vars[-1] == 0) { + if (!(ruby_scope->flags & SCOPE_CLONE)) + xfree(ruby_scope->local_tbl); + } + ruby_scope->local_tbl = local_tbl(); + } + } + local_pop(); +} + +#define DVAR_USED FL_USER6 + +static VALUE +dyna_var_lookup(id) + ID id; +{ + struct RVarmap *vars = ruby_dyna_vars; + + while (vars) { + if (vars->id == id) { + FL_SET(vars, DVAR_USED); + return Qtrue; + } + vars = vars->next; + } + return Qfalse; +} + +static struct RVarmap* +dyna_push() +{ + struct RVarmap* vars = ruby_dyna_vars; + + rb_dvar_push(0, 0); + lvtbl->dlev++; + return vars; +} + +static void +dyna_pop(vars) + struct RVarmap* vars; +{ + lvtbl->dlev--; + ruby_dyna_vars = vars; +} + +static int +dyna_in_block() +{ + return (lvtbl->dlev > 0); +} + +static NODE * +dyna_init(node, pre) + NODE *node; + struct RVarmap *pre; +{ + struct RVarmap *post = ruby_dyna_vars; + NODE *var; + + if (!node || !post || pre == post) return node; + for (var = 0; post != pre && post->id; post = post->next) { + if (FL_TEST(post, DVAR_USED)) { + var = NEW_DASGN_CURR(post->id, var); + } + } + return block_append(var, node); +} + +int +ruby_parser_stack_on_heap() +{ +#if defined(YYMALLOC) + return Qfalse; +#else + return Qtrue; +#endif +} + +void +rb_gc_mark_parser() +{ +#if defined YYMALLOC + rb_gc_mark((VALUE)parser_heap); +#elif defined yystacksize + if (yyvsp) rb_gc_mark_locations((VALUE *)yyvs, (VALUE *)yyvsp); +#endif + + if (!ruby_in_compile) return; + + rb_gc_mark_maybe((VALUE)yylval.node); + rb_gc_mark(ruby_debug_lines); + rb_gc_mark(lex_lastline); + rb_gc_mark(lex_input); + rb_gc_mark((VALUE)lex_strterm); +} + +void +rb_parser_append_print() +{ + ruby_eval_tree = + block_append(ruby_eval_tree, + NEW_FCALL(rb_intern("print"), + NEW_ARRAY(NEW_GVAR(rb_intern("$_"))))); +} + +void +rb_parser_while_loop(chop, split) + int chop, split; +{ + if (split) { + ruby_eval_tree = + block_append(NEW_GASGN(rb_intern("$F"), + NEW_CALL(NEW_GVAR(rb_intern("$_")), + rb_intern("split"), 0)), + ruby_eval_tree); + } + if (chop) { + ruby_eval_tree = + block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")), + rb_intern("chop!"), 0), ruby_eval_tree); + } + ruby_eval_tree = NEW_OPT_N(ruby_eval_tree); +} + +static struct { + ID token; + char *name; +} op_tbl[] = { + {tDOT2, ".."}, + {tDOT3, "..."}, + {'+', "+"}, + {'-', "-"}, + {'+', "+(binary)"}, + {'-', "-(binary)"}, + {'*', "*"}, + {'/', "/"}, + {'%', "%"}, + {tPOW, "**"}, + {tUPLUS, "+@"}, + {tUMINUS, "-@"}, + {tUPLUS, "+(unary)"}, + {tUMINUS, "-(unary)"}, + {'|', "|"}, + {'^', "^"}, + {'&', "&"}, + {tCMP, "<=>"}, + {'>', ">"}, + {tGEQ, ">="}, + {'<', "<"}, + {tLEQ, "<="}, + {tEQ, "=="}, + {tEQQ, "==="}, + {tNEQ, "!="}, + {tMATCH, "=~"}, + {tNMATCH, "!~"}, + {'!', "!"}, + {'~', "~"}, + {'!', "!(unary)"}, + {'~', "~(unary)"}, + {'!', "!@"}, + {'~', "~@"}, + {tAREF, "[]"}, + {tASET, "[]="}, + {tLSHFT, "<<"}, + {tRSHFT, ">>"}, + {tCOLON2, "::"}, + {'`', "`"}, + {0, 0} +}; + +static st_table *sym_tbl; +static st_table *sym_rev_tbl; + +void +Init_sym() +{ + sym_tbl = st_init_strtable_with_size(200); + sym_rev_tbl = st_init_numtable_with_size(200); +} + +static ID last_id = tLAST_TOKEN; + +static ID +internal_id() +{ + return ID_INTERNAL | (++last_id << ID_SCOPE_SHIFT); +} + +static int +is_special_global_name(m) + const char *m; +{ + switch (*m) { + case '~': case '*': case '$': case '?': case '!': case '@': + case '/': case '\\': case ';': case ',': case '.': case '=': + case ':': case '<': case '>': case '\"': + case '&': case '`': case '\'': case '+': + case '0': + ++m; + break; + case '-': + ++m; + if (is_identchar(*m)) m += mbclen(*m); + break; + default: + if (!ISDIGIT(*m)) return 0; + do ++m; while (ISDIGIT(*m)); + } + return !*m; +} + +int +rb_symname_p(name) + const char *name; +{ + const char *m = name; + int localid = Qfalse; + + if (!m) return Qfalse; + switch (*m) { + case '\0': + return Qfalse; + + case '$': + if (is_special_global_name(++m)) return Qtrue; + goto id; + + case '@': + if (*++m == '@') ++m; + goto id; + + case '<': + switch (*++m) { + case '<': ++m; break; + case '=': if (*++m == '>') ++m; break; + default: break; + } + break; + + case '>': + switch (*++m) { + case '>': case '=': ++m; break; + } + break; + + case '=': + switch (*++m) { + case '~': ++m; break; + case '=': if (*++m == '=') ++m; break; + default: return Qfalse; + } + break; + + case '*': + if (*++m == '*') ++m; + break; + + case '+': case '-': + if (*++m == '@') ++m; + break; + + case '|': case '^': case '&': case '/': case '%': case '~': case '`': + ++m; + break; + + case '[': + if (*++m != ']') return Qfalse; + if (*++m == '=') ++m; + break; + + default: + localid = !ISUPPER(*m); + id: + if (*m != '_' && !ISALPHA(*m) && !ismbchar(*m)) return Qfalse; + while (is_identchar(*m)) m += mbclen(*m); + if (localid) { + switch (*m) { + case '!': case '?': case '=': ++m; + } + } + break; + } + return *m ? Qfalse : Qtrue; +} + +ID +rb_intern(name) + const char *name; +{ + const char *m = name; + ID id; + int last; + + if (st_lookup(sym_tbl, (st_data_t)name, (st_data_t *)&id)) + return id; + + last = strlen(name)-1; + id = 0; + switch (*name) { + case '$': + id |= ID_GLOBAL; + if (is_special_global_name(++m)) goto new_id; + break; + case '@': + if (name[1] == '@') { + m++; + id |= ID_CLASS; + } + else { + id |= ID_INSTANCE; + } + m++; + break; + default: + if (name[0] != '_' && ISASCII(name[0]) && !ISALNUM(name[0])) { + /* operators */ + int i; + + for (i=0; op_tbl[i].token; i++) { + if (*op_tbl[i].name == *name && + strcmp(op_tbl[i].name, name) == 0) { + id = op_tbl[i].token; + goto id_regist; + } + } + } + + if (name[last] == '=') { + /* attribute assignment */ + char *buf = ALLOCA_N(char,last+1); + + strncpy(buf, name, last); + buf[last] = '\0'; + id = rb_intern(buf); + if (id > tLAST_TOKEN && !is_attrset_id(id)) { + id = rb_id_attrset(id); + goto id_regist; + } + id = ID_ATTRSET; + } + else if (ISUPPER(name[0])) { + id = ID_CONST; + } + else { + id = ID_LOCAL; + } + break; + } + if (!ISDIGIT(*m)) { + while (m <= name + last && is_identchar(*m)) { + m += mbclen(*m); + } + } + if (*m) id = ID_JUNK; + new_id: + id |= ++last_id << ID_SCOPE_SHIFT; + id_regist: + name = strdup(name); + st_add_direct(sym_tbl, (st_data_t)name, id); + st_add_direct(sym_rev_tbl, id, (st_data_t)name); + return id; +} + +char * +rb_id2name(id) + ID id; +{ + char *name; + + if (id < tLAST_TOKEN) { + int i = 0; + + for (i=0; op_tbl[i].token; i++) { + if (op_tbl[i].token == id) + return op_tbl[i].name; + } + } + + if (st_lookup(sym_rev_tbl, id, (st_data_t *)&name)) + return name; + + if (is_attrset_id(id)) { + ID id2 = (id & ~ID_SCOPE_MASK) | ID_LOCAL; + + again: + name = rb_id2name(id2); + if (name) { + char *buf = ALLOCA_N(char, strlen(name)+2); + + strcpy(buf, name); + strcat(buf, "="); + rb_intern(buf); + return rb_id2name(id); + } + if (is_local_id(id2)) { + id2 = (id & ~ID_SCOPE_MASK) | ID_CONST; + goto again; + } + } + return 0; +} + +static int +symbols_i(key, value, ary) + char *key; + ID value; + VALUE ary; +{ + rb_ary_push(ary, ID2SYM(value)); + return ST_CONTINUE; +} + +/* + * call-seq: + * Symbol.all_symbols => array + * + * Returns an array of all the symbols currently in Ruby's symbol + * table. + * + * Symbol.all_symbols.size #=> 903 + * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink, + * :chown, :EOFError, :$;, :String, + * :LOCK_SH, :"setuid?", :$<, + * :default_proc, :compact, :extend, + * :Tms, :getwd, :$=, :ThreadGroup, + * :wait2, :$>] + */ + +VALUE +rb_sym_all_symbols() +{ + VALUE ary = rb_ary_new2(sym_tbl->num_entries); + + st_foreach(sym_tbl, symbols_i, ary); + return ary; +} + +int +rb_is_const_id(id) + ID id; +{ + if (is_const_id(id)) return Qtrue; + return Qfalse; +} + +int +rb_is_class_id(id) + ID id; +{ + if (is_class_id(id)) return Qtrue; + return Qfalse; +} + +int +rb_is_instance_id(id) + ID id; +{ + if (is_instance_id(id)) return Qtrue; + return Qfalse; +} + +int +rb_is_local_id(id) + ID id; +{ + if (is_local_id(id)) return Qtrue; + return Qfalse; +} + +int +rb_is_junk_id(id) + ID id; +{ + if (is_junk_id(id)) return Qtrue; + return Qfalse; +} + +static void +special_local_set(c, val) + char c; + VALUE val; +{ + int cnt; + + top_local_init(); + cnt = local_cnt(c); + top_local_setup(); + ruby_scope->local_vars[cnt] = val; +} + +VALUE +rb_backref_get() +{ + VALUE *var = rb_svar(1); + if (var) { + return *var; + } + return Qnil; +} + +void +rb_backref_set(val) + VALUE val; +{ + VALUE *var = rb_svar(1); + if (var) { + *var = val; + } + else { + special_local_set('~', val); + } +} + +VALUE +rb_lastline_get() +{ + VALUE *var = rb_svar(0); + if (var) { + return *var; + } + return Qnil; +} + +void +rb_lastline_set(val) + VALUE val; +{ + VALUE *var = rb_svar(0); + if (var) { + *var = val; + } + else { + special_local_set('_', val); + } +} + +#ifdef YYMALLOC +#define HEAPCNT(n, size) ((n) * (size) / sizeof(YYSTYPE)) +#define NEWHEAP() rb_node_newnode(NODE_ALLOCA, 0, (VALUE)parser_heap, 0) +#define ADD2HEAP(n, c, p) ((parser_heap = (n))->u1.node = (p), \ + (n)->u3.cnt = (c), (p)) + +static void * +rb_parser_malloc(size) + size_t size; +{ + size_t cnt = HEAPCNT(1, size); + NODE *n = NEWHEAP(); + void *ptr = xmalloc(size); + + return ADD2HEAP(n, cnt, ptr); +} + +static void * +rb_parser_calloc(nelem, size) + size_t nelem, size; +{ + size_t cnt = HEAPCNT(nelem, size); + NODE *n = NEWHEAP(); + void *ptr = xcalloc(nelem, size); + + return ADD2HEAP(n, cnt, ptr); +} + +static void * +rb_parser_realloc(ptr, size) + void *ptr; + size_t size; +{ + NODE *n; + size_t cnt = HEAPCNT(1, size); + + if (ptr && (n = parser_heap) != NULL) { + do { + if (n->u1.node == ptr) { + n->u1.node = ptr = xrealloc(ptr, size); + if (n->u3.cnt) n->u3.cnt = cnt; + return ptr; + } + } while ((n = n->u2.node) != NULL); + } + n = NEWHEAP(); + ptr = xrealloc(ptr, size); + return ADD2HEAP(n, cnt, ptr); +} + +static void +rb_parser_free(ptr) + void *ptr; +{ + NODE **prev = &parser_heap, *n; + + while (n = *prev) { + if (n->u1.node == ptr) { + *prev = n->u2.node; + rb_gc_force_recycle((VALUE)n); + break; + } + prev = &n->u2.node; + } + xfree(ptr); +} +#endif -- cgit v1.2.3