Backport #1939 ยป patch.diff
parse.y | ||
---|---|---|
#include <errno.h>
|
||
#include <ctype.h>
|
||
#ifdef RIPPER
|
||
typedef struct RIPPER_YYSTYPE {
|
||
VALUE val;
|
||
NODE *node;
|
||
ID id;
|
||
int num;
|
||
} RIPPER_YYSTYPE;
|
||
#define YYSTYPE RIPPER_YYSTYPE
|
||
#endif
|
||
#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
|
||
#define YYMALLOC(size) rb_parser_malloc(parser, size)
|
||
... | ... | |
#define DVARS_SPECIAL_P(tbl) (!POINTER_P(tbl))
|
||
#define POINTER_P(val) ((VALUE)(val) & ~(VALUE)3)
|
||
#ifndef RIPPER
|
||
static int
|
||
vtable_size(const struct vtable *tbl)
|
||
{
|
||
... | ... | |
int nonspc;
|
||
struct token_info *next;
|
||
} token_info;
|
||
#endif
|
||
/*
|
||
Structure of Lexer Buffer:
|
||
... | ... | |
static NODE *new_args_gen(struct parser_params*,NODE*,NODE*,ID,NODE*,ID);
|
||
#define new_args(f,o,r,p,b) new_args_gen(parser, f,o,r,p,b)
|
||
#endif /* !RIPPER */
|
||
static void shadowing_lvar_gen(struct parser_params*,ID);
|
||
#define shadowing_lvar(name) shadowing_lvar_gen(parser, name)
|
||
#ifndef RIPPER
|
||
static NODE *negate_lit(NODE*);
|
||
static NODE *ret_args_gen(struct parser_params*,NODE*);
|
||
... | ... | |
#define gettable(id) gettable_gen(parser,id)
|
||
static NODE *assignable_gen(struct parser_params*,ID,NODE*);
|
||
#define assignable(id,node) assignable_gen(parser, id, node)
|
||
#else /* if RIPPER */
|
||
static void assignment_var_gen(struct parser_params*,ID);
|
||
#define assignment_var(id) assignment_var_gen(parser, id)
|
||
#endif /* !RIPPER */
|
||
static void new_bv_gen(struct parser_params*,ID);
|
||
#define new_bv(id) new_bv_gen(parser, id)
|
||
#ifndef RIPPER
|
||
static NODE *aryset_gen(struct parser_params*,NODE*,NODE*);
|
||
#define aryset(node1,node2) aryset_gen(parser, node1, node2)
|
||
static NODE *attrset_gen(struct parser_params*,NODE*,ID);
|
||
... | ... | |
static NODE *match_op_gen(struct parser_params*,NODE*,NODE*);
|
||
#define match_op(node1,node2) match_op_gen(parser, node1, node2)
|
||
#endif /* !RIPPER */
|
||
static void local_push_gen(struct parser_params*,int);
|
||
#define local_push(top) local_push_gen(parser,top)
|
||
static void local_pop_gen(struct parser_params*);
|
||
... | ... | |
#define dvar_defined(id) dvar_defined_gen(parser, id)
|
||
static int dvar_curr_gen(struct parser_params*,ID);
|
||
#define dvar_curr(id) dvar_curr_gen(parser, id)
|
||
#ifndef RIPPER
|
||
static void fixup_nodes(NODE **);
|
||
#endif /* !RIPPER */
|
||
extern int rb_dvar_defined(ID);
|
||
extern int rb_local_defined(ID);
|
||
#ifndef RIPPER
|
||
extern int rb_parse_in_eval(void);
|
||
extern int rb_parse_in_main(void);
|
||
... | ... | |
$<num>$ = compile_for_eval || rb_parse_in_main();
|
||
local_push($<num>$);
|
||
/*%
|
||
local_push(1);
|
||
%*/
|
||
}
|
||
compstmt
|
||
... | ... | |
}
|
||
}
|
||
ruby_eval_tree = NEW_SCOPE(0, block_append(ruby_eval_tree, $2));
|
||
local_pop();
|
||
/*%
|
||
$$ = $2;
|
||
parser->result = dispatch1(program, $$);
|
||
%*/
|
||
local_pop();
|
||
}
|
||
;
|
||
... | ... | |
cmd_brace_block : tLBRACE_ARG
|
||
{
|
||
/*%%%*/
|
||
dyna_push();
|
||
/*%%%*/
|
||
$<num>$ = ruby_sourceline;
|
||
/*%
|
||
%*/
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_ITER($3,$4);
|
||
nd_set_line($$, $<num>2);
|
||
dyna_pop();
|
||
/*%
|
||
$$ = dispatch2(brace_block, escape_Qundef($3), $4);
|
||
%*/
|
||
dyna_pop();
|
||
}
|
||
;
|
||
... | ... | |
/*%%%*/
|
||
$$ = assignable($1, 0);
|
||
/*%
|
||
assignment_var($<id>1);
|
||
$$ = $1;
|
||
%*/
|
||
}
|
||
... | ... | |
/*%%%*/
|
||
if (!($$ = assignable($1, 0))) $$ = NEW_BEGIN(0);
|
||
/*%
|
||
assignment_var($<id>1);
|
||
$$ = dispatch1(var_field, $1);
|
||
%*/
|
||
}
|
||
... | ... | |
{
|
||
if (in_def || in_single)
|
||
yyerror("class definition in method body");
|
||
/*%%%*/
|
||
local_push(0);
|
||
/*%%%*/
|
||
$<num>$ = ruby_sourceline;
|
||
/*%
|
||
%*/
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_CLASS($2, $5, $3);
|
||
nd_set_line($$, $<num>4);
|
||
local_pop();
|
||
/*%
|
||
$$ = dispatch3(class, $2, $3, $5);
|
||
%*/
|
||
local_pop();
|
||
}
|
||
| k_class tLSHFT expr
|
||
{
|
||
... | ... | |
{
|
||
$<num>$ = in_single;
|
||
in_single = 0;
|
||
/*%%%*/
|
||
local_push(0);
|
||
/*%
|
||
%*/
|
||
}
|
||
bodystmt
|
||
k_end
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_SCLASS($3, $7);
|
||
fixpos($$, $3);
|
||
local_pop();
|
||
/*%
|
||
$$ = dispatch2(sclass, $3, $7);
|
||
%*/
|
||
local_pop();
|
||
in_def = $<num>4;
|
||
in_single = $<num>6;
|
||
}
|
||
... | ... | |
{
|
||
if (in_def || in_single)
|
||
yyerror("module definition in method body");
|
||
/*%%%*/
|
||
local_push(0);
|
||
/*%%%*/
|
||
$<num>$ = ruby_sourceline;
|
||
/*%
|
||
%*/
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_MODULE($2, $4);
|
||
nd_set_line($$, $<num>3);
|
||
local_pop();
|
||
/*%
|
||
$$ = dispatch2(module, $2, $4);
|
||
%*/
|
||
local_pop();
|
||
}
|
||
| k_def fname
|
||
{
|
||
$<id>$ = cur_mid;
|
||
cur_mid = $2;
|
||
in_def++;
|
||
/*%%%*/
|
||
local_push(0);
|
||
/*%
|
||
%*/
|
||
}
|
||
f_arglist
|
||
bodystmt
|
||
... | ... | |
reduce_nodes(&body);
|
||
$$ = NEW_DEFN($2, $4, body, NOEX_PRIVATE);
|
||
fixpos($$, $4);
|
||
local_pop();
|
||
/*%
|
||
$$ = dispatch3(def, $2, $4, $5);
|
||
%*/
|
||
local_pop();
|
||
in_def--;
|
||
cur_mid = $<id>3;
|
||
}
|
||
... | ... | |
{
|
||
in_single++;
|
||
lex_state = EXPR_END; /* force for args */
|
||
/*%%%*/
|
||
local_push(0);
|
||
/*%
|
||
%*/
|
||
}
|
||
f_arglist
|
||
bodystmt
|
||
... | ... | |
reduce_nodes(&body);
|
||
$$ = NEW_DEFS($2, $5, $7, body);
|
||
fixpos($$, $2);
|
||
local_pop();
|
||
/*%
|
||
$$ = dispatch5(defs, $2, $3, $5, $7, $8);
|
||
%*/
|
||
local_pop();
|
||
in_single--;
|
||
}
|
||
| keyword_break
|
||
... | ... | |
/*%%%*/
|
||
$$ = assignable($1, 0);
|
||
/*%
|
||
assignment_var($<id>1);
|
||
$$ = dispatch1(mlhs_paren, $1);
|
||
%*/
|
||
}
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_MASGN($1, assignable($4, 0));
|
||
/*%
|
||
assignment_var($<id>4);
|
||
$$ = mlhs_add_star($1, $4);
|
||
%*/
|
||
}
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_MASGN($1, NEW_POSTARG(assignable($4, 0), $6));
|
||
/*%
|
||
assignment_var($<id>4);
|
||
$$ = mlhs_add_star($1, $4);
|
||
%*/
|
||
}
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_MASGN(0, assignable($2, 0));
|
||
/*%
|
||
assignment_var($<id>2);
|
||
$$ = mlhs_add_star(mlhs_new(), $2);
|
||
%*/
|
||
}
|
||
... | ... | |
#if 0
|
||
TODO: Check me
|
||
#endif
|
||
assignment_var($<id>2);
|
||
$$ = mlhs_add_star($2, $4);
|
||
%*/
|
||
}
|
||
... | ... | |
bvar : tIDENTIFIER
|
||
{
|
||
/*%%%*/
|
||
new_bv($1);
|
||
/*%%%*/
|
||
/*%
|
||
$$ = $1;
|
||
%*/
|
||
... | ... | |
;
|
||
lambda : {
|
||
/*%%%*/
|
||
dyna_push();
|
||
/*%
|
||
%*/
|
||
$<num>$ = lpar_beg;
|
||
lpar_beg = ++paren_nest;
|
||
}
|
||
... | ... | |
/*%%%*/
|
||
$$ = $2;
|
||
$$->nd_body = NEW_SCOPE($2->nd_head, $3);
|
||
dyna_pop();
|
||
/*%
|
||
$$ = dispatch2(lambda, $2, $3);
|
||
%*/
|
||
dyna_pop();
|
||
}
|
||
;
|
||
... | ... | |
do_block : keyword_do_block
|
||
{
|
||
/*%%%*/
|
||
dyna_push();
|
||
/*%%%*/
|
||
$<num>$ = ruby_sourceline;
|
||
/*% %*/
|
||
}
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_ITER($3,$4);
|
||
nd_set_line($$, $<num>2);
|
||
dyna_pop();
|
||
/*%
|
||
$$ = dispatch2(do_block, escape_Qundef($3), $4);
|
||
%*/
|
||
dyna_pop();
|
||
}
|
||
;
|
||
... | ... | |
brace_block : '{'
|
||
{
|
||
/*%%%*/
|
||
dyna_push();
|
||
/*%%%*/
|
||
$<num>$ = ruby_sourceline;
|
||
/*%
|
||
%*/
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_ITER($3,$4);
|
||
nd_set_line($$, $<num>2);
|
||
dyna_pop();
|
||
/*%
|
||
$$ = dispatch2(brace_block, escape_Qundef($3), $4);
|
||
%*/
|
||
dyna_pop();
|
||
}
|
||
| keyword_do
|
||
{
|
||
/*%%%*/
|
||
dyna_push();
|
||
/*%%%*/
|
||
$<num>$ = ruby_sourceline;
|
||
/*%
|
||
%*/
|
||
... | ... | |
/*%%%*/
|
||
$$ = NEW_ITER($3,$4);
|
||
nd_set_line($$, $<num>2);
|
||
dyna_pop();
|
||
/*%
|
||
$$ = dispatch2(do_block, escape_Qundef($3), $4);
|
||
%*/
|
||
dyna_pop();
|
||
}
|
||
;
|
||
... | ... | |
/*%%%*/
|
||
$$ = assignable($1, 0);
|
||
/*%
|
||
assignment_var($<id>1);
|
||
$$ = dispatch1(var_field, $1);
|
||
%*/
|
||
}
|
||
... | ... | |
/*%%%*/
|
||
if (!is_local_id($1))
|
||
yyerror("formal argument must be local variable");
|
||
shadowing_lvar($1);
|
||
/*%
|
||
%*/
|
||
shadowing_lvar($<id>1);
|
||
$$ = $1;
|
||
}
|
||
;
|
||
f_arg_item : f_norm_arg
|
||
{
|
||
arg_var($<id>1);
|
||
/*%%%*/
|
||
arg_var($1);
|
||
$$ = NEW_ARGS_AUX($1, 1);
|
||
/*%
|
||
%*/
|
||
}
|
||
| tLPAREN f_margs rparen
|
||
{
|
||
/*%%%*/
|
||
ID tid = internal_id();
|
||
arg_var(tid);
|
||
/*%%%*/
|
||
if (dyna_in_block()) {
|
||
$2->nd_value = NEW_DVAR(tid);
|
||
}
|
||
... | ... | |
f_opt : tIDENTIFIER '=' arg_value
|
||
{
|
||
shadowing_lvar($<id>1);
|
||
arg_var($<id>1);
|
||
/*%%%*/
|
||
if (!is_local_id($1))
|
||
yyerror("formal argument must be local variable");
|
||
shadowing_lvar($1);
|
||
arg_var($1);
|
||
$$ = NEW_OPT_ARG(0, assignable($1, $3));
|
||
/*%
|
||
assignment_var($<id>1);
|
||
$$ = rb_assoc_new($1, $3);
|
||
%*/
|
||
}
|
||
... | ... | |
f_block_opt : tIDENTIFIER '=' primary_value
|
||
{
|
||
shadowing_lvar($<id>1);
|
||
arg_var($<id>1);
|
||
/*%%%*/
|
||
if (!is_local_id($1))
|
||
yyerror("formal argument must be local variable");
|
||
shadowing_lvar($1);
|
||
arg_var($1);
|
||
$$ = NEW_OPT_ARG(0, assignable($1, $3));
|
||
/*%
|
||
assignment_var($<id>1);
|
||
$$ = rb_assoc_new($1, $3);
|
||
%*/
|
||
}
|
||
... | ... | |
f_rest_arg : restarg_mark tIDENTIFIER
|
||
{
|
||
shadowing_lvar($<id>2);
|
||
arg_var($2);
|
||
/*%%%*/
|
||
if (!is_local_id($2))
|
||
yyerror("rest argument must be local variable");
|
||
shadowing_lvar($2);
|
||
arg_var($2);
|
||
$$ = $2;
|
||
/*%
|
||
$$ = dispatch1(rest_param, $2);
|
||
... | ... | |
}
|
||
| restarg_mark
|
||
{
|
||
ID tid = internal_id();
|
||
arg_var(tid);
|
||
/*%%%*/
|
||
$$ = internal_id();
|
||
arg_var($$);
|
||
$$ = tid;
|
||
/*%
|
||
$$ = dispatch1(rest_param, Qnil);
|
||
%*/
|
||
... | ... | |
yyerror("block argument must be local variable");
|
||
else if (!dyna_in_block() && local_id($2))
|
||
yyerror("duplicated block argument name");
|
||
shadowing_lvar($2);
|
||
arg_var($2);
|
||
$$ = $2;
|
||
/*%
|
||
$$ = dispatch1(blockarg, $2);
|
||
%*/
|
||
shadowing_lvar($<id>2);
|
||
arg_var($<id>2);
|
||
}
|
||
;
|
||
... | ... | |
# define heredoc_restore(n) parser_heredoc_restore(parser,n)
|
||
# define whole_match_p(e,l,i) parser_whole_match_p(parser,e,l,i)
|
||
#ifdef RIPPER
|
||
/* FIXME */
|
||
# define local_id(x) 1
|
||
# define dyna_in_block() 1
|
||
#endif /* RIPPER */
|
||
#ifndef RIPPER
|
||
# define set_yylval_str(x) yylval.node = NEW_STR(x)
|
||
# define set_yylval_num(x) yylval.num = x
|
||
... | ... | |
#else
|
||
# define set_yylval_str(x) (void)(x)
|
||
# define set_yylval_num(x) (void)(x)
|
||
# define set_yylval_id(x) (void)(x)
|
||
# define set_yylval_id(x) yylval.id = x
|
||
# define set_yylval_literal(x) (void)(x)
|
||
# define set_yylval_node(x) (void)(x)
|
||
# define yylval_id() SYM2ID(yylval.val)
|
||
# define yylval_id() yylval.id
|
||
#endif
|
||
#ifdef RIPPER
|
||
... | ... | |
static int
|
||
lvar_defined_gen(struct parser_params *parser, ID id)
|
||
{
|
||
#ifndef RIPPER
|
||
return (dyna_in_block() && dvar_defined(id)) || local_id(id);
|
||
#else
|
||
return 0;
|
||
#endif
|
||
}
|
||
/* emacsen -*- hack */
|
||
... | ... | |
}
|
||
return 0;
|
||
}
|
||
#endif /* !RIPPER */
|
||
static void
|
||
assignment_var_gen(struct parser_params *parser, ID name)
|
||
{
|
||
if (!name || !is_local_id(name)) return;
|
||
|
||
if (dyna_in_block()) {
|
||
if (!dvar_defined(name) && !local_id(name)) {
|
||
dyna_var(name);
|
||
}
|
||
} else {
|
||
if (!local_id(name)) {
|
||
local_var(name);
|
||
}
|
||
}
|
||
}
|
||
static void
|
||
shadowing_lvar_gen(struct parser_params *parser, ID name)
|
||
... | ... | |
shadowing_lvar(name);
|
||
dyna_var(name);
|
||
}
|
||
#ifndef RIPPER
|
||
static NODE *
|
||
aryset_gen(struct parser_params *parser, NODE *recv, NODE *idx)
|
||
... | ... | |
return node;
|
||
}
|
||
#endif /* !RIPPER */
|
||
static void
|
||
local_push_gen(struct parser_params *parser, int inherit_dvars)
|
||
{
|
||
... | ... | |
return (vtable_included(lvtbl->args, id) ||
|
||
vtable_included(lvtbl->vars, id));
|
||
}
|
||
#ifndef RIPPER
|
||
VALUE rb_reg_compile(VALUE str, int options, const char *sourcefile, int sourceline);
|
||
VALUE rb_reg_check_preprocess(VALUE);
|
||
... | ... | |
global_symbols.op_sym + tLAST_TOKEN);
|
||
}
|
||
#endif /* !RIPPER */
|
||
static ID
|
||
internal_id_gen(struct parser_params *parser)
|
||
{
|
||
... | ... | |
id += ((tLAST_TOKEN - ID_INTERNAL) >> ID_SCOPE_SHIFT) + 1;
|
||
return ID_INTERNAL | (id << ID_SCOPE_SHIFT);
|
||
}
|
||
#ifndef RIPPER
|
||
static int
|
||
is_special_global_name(const char *m, const char *e, rb_encoding *enc)
|
test/ripper/test_parser_events.rb | ||
---|---|---|
end
|
||
=end
|
||
def test_local_variables
|
||
cmd = 'command(w,[regexp_literal(xstring_add(xstring_new(),25 # ),/)])'
|
||
div = 'binary(ref(w),/,25)'
|
||
|
||
assert_equal "[#{cmd}]", parse('w /25 # /')
|
||
assert_equal "[assign(var_field(w),1),#{div}]", parse("w = 1; w /25 # /")
|
||
assert_equal "[fcall(p,[],&block([w],[#{div}]))]", parse("p{|w|w /25 # /\n}")
|
||
assert_equal "[def(p,[w],body_stmt([binary(ref(w),/,25)],,,))]", parse("def p(w)\nw /25 # /\nend")
|
||
end
|
||
end
|
||
rescue LoadError
|