diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2016-03-23 08:35:43 +0900 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2016-03-23 08:51:25 +0900 |
| commit | 21a3ae08f7dc252e4cf6d34e5ebead0124fb0117 (patch) | |
| tree | 082c0c947c338b718d743743b9304b3e965d1173 | |
| parent | 7d0612528e564ac07bb0435004fe04c46af295a2 (diff) | |
| download | mruby-21a3ae08f7dc252e4cf6d34e5ebead0124fb0117.tar.gz mruby-21a3ae08f7dc252e4cf6d34e5ebead0124fb0117.zip | |
add safe-navigation (aka lonely) operator `&.`
| -rw-r--r-- | mrbgems/mruby-compiler/core/codegen.c | 24 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/core/node.h | 1 | ||||
| -rw-r--r-- | mrbgems/mruby-compiler/core/parse.y | 151 |
3 files changed, 109 insertions, 67 deletions
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 059e5ee8d..ced2d5a28 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -826,13 +826,23 @@ gen_values(codegen_scope *s, node *t, int val) #define CALL_MAXARGS 127 static void -gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val) +gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe) { mrb_sym sym = name ? name : sym(tree->cdr->car); - int idx; + int idx, skip; int n = 0, noop = 0, sendv = 0, blk = 0; codegen(s, tree->car, VAL); /* receiver */ + if (safe) { + int recv = cursp()-1; + genop(s, MKOP_A(OP_LOADNIL, cursp())); + push(); + genop(s, MKOP_AB(OP_MOVE, cursp(), recv)); + pop(); + idx = new_msym(s, mrb_intern_lit(s->mrb, "==")); + genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1)); + skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0)); + } idx = new_msym(s, sym); tree = tree->cdr->cdr->car; if (tree) { @@ -905,6 +915,9 @@ gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val) } } } + if (safe) { + dispatch(s, skip); + } if (val) { push(); } @@ -968,7 +981,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val) case NODE_CALL: push(); - gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL); + gen_call(s, tree, attrsym(s, sym(tree->cdr->car)), sp, NOVAL, 0); pop(); if (val) { genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val); @@ -1511,7 +1524,10 @@ codegen(codegen_scope *s, node *tree, int val) case NODE_FCALL: case NODE_CALL: - gen_call(s, tree, 0, 0, val); + gen_call(s, tree, 0, 0, val, 0); + break; + case NODE_SCALL: + gen_call(s, tree, 0, 0, val, 1); break; case NODE_DOT2: diff --git a/mrbgems/mruby-compiler/core/node.h b/mrbgems/mruby-compiler/core/node.h index 4f3669aca..9636dd759 100644 --- a/mrbgems/mruby-compiler/core/node.h +++ b/mrbgems/mruby-compiler/core/node.h @@ -38,6 +38,7 @@ enum node_type { NODE_CVDECL, NODE_OP_ASGN, NODE_CALL, + NODE_SCALL, NODE_FCALL, NODE_VCALL, NODE_SUPER, diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 9a4b7d90a..9793aea8c 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -72,6 +72,8 @@ typedef unsigned int stack_type; #define sym(x) ((mrb_sym)(intptr_t)(x)) #define nsym(x) ((node*)(intptr_t)(x)) +#define nint(x) ((node*)(intptr_t)(x)) +#define intn(x) ((int)(intptr_t)(x)) static inline mrb_sym intern_cstr_gen(parser_state *p, const char *s) @@ -408,9 +410,9 @@ new_self(parser_state *p) /* (:call a b c) */ static node* -new_call(parser_state *p, node *a, mrb_sym b, node *c) +new_call(parser_state *p, node *a, mrb_sym b, node *c, int pass) { - node *n = list4((node*)NODE_CALL, a, nsym(b), c); + node *n = list4(nint(pass?NODE_CALL:NODE_SCALL), a, nsym(b), c); NODE_LINENO(n, a); return n; } @@ -720,7 +722,7 @@ new_op_asgn(parser_state *p, node *a, mrb_sym op, node *b) static node* new_int(parser_state *p, const char *s, int base) { - return list3((node*)NODE_INT, (node*)strdup(s), (node*)(intptr_t)base); + return list3((node*)NODE_INT, (node*)strdup(s), nint(base)); } /* (:float . i) */ @@ -734,7 +736,7 @@ new_float(parser_state *p, const char *s) static node* new_str(parser_state *p, const char *s, int len) { - return cons((node*)NODE_STR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); + return cons((node*)NODE_STR, cons((node*)strndup(s, len), nint(len))); } /* (:dstr . a) */ @@ -748,7 +750,7 @@ new_dstr(parser_state *p, node *a) static node* new_xstr(parser_state *p, const char *s, int len) { - return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), (node*)(intptr_t)len)); + return cons((node*)NODE_XSTR, cons((node*)strndup(s, len), nint(len))); } /* (:xstr . a) */ @@ -783,14 +785,14 @@ new_dregx(parser_state *p, node *a, node *b) static node* new_back_ref(parser_state *p, int n) { - return cons((node*)NODE_BACK_REF, (node*)(intptr_t)n); + return cons((node*)NODE_BACK_REF, nint(n)); } /* (:nthref . n) */ static node* new_nth_ref(parser_state *p, int n) { - return cons((node*)NODE_NTH_REF, (node*)(intptr_t)n); + return cons((node*)NODE_NTH_REF, nint(n)); } /* (:heredoc . a) */ @@ -832,14 +834,14 @@ new_symbols(parser_state *p, node *a) static node* call_uni_op(parser_state *p, node *recv, const char *m) { - return new_call(p, recv, intern_cstr(m), 0); + return new_call(p, recv, intern_cstr(m), 0, 1); } /* (:call a op b) */ static node* call_bin_op(parser_state *p, node *recv, const char *m, node *arg1) { - return new_call(p, recv, intern_cstr(m), list1(list1(arg1))); + return new_call(p, recv, intern_cstr(m), list1(list1(arg1)), 1); } static void @@ -858,7 +860,7 @@ call_with_block(parser_state *p, node *a, node *b) { node *n; - switch ((enum node_type)(intptr_t)a->car) { + switch ((enum node_type)intn(a->car)) { case NODE_SUPER: case NODE_ZSUPER: if (!a->cdr) a->cdr = cons(0, b); @@ -905,7 +907,7 @@ ret_args(parser_state *p, node *n) static void assignable(parser_state *p, node *lhs) { - if ((int)(intptr_t)lhs->car == NODE_LVAR) { + if (intn(lhs->car) == NODE_LVAR) { local_add(p, sym(lhs->cdr)); } } @@ -915,7 +917,7 @@ var_reference(parser_state *p, node *lhs) { node *n; - if ((int)(intptr_t)lhs->car == NODE_LVAR) { + if (intn(lhs->car) == NODE_LVAR) { if (!local_var_p(p, sym(lhs->cdr))) { n = new_fcall(p, sym(lhs->cdr), 0); cons_free(lhs); @@ -931,7 +933,7 @@ typedef enum mrb_string_type string_type; static node* new_strterm(parser_state *p, string_type type, int term, int paren) { - return cons((node*)(intptr_t)type, cons((node*)0, cons((node*)(intptr_t)paren, (node*)(intptr_t)term))); + return cons(nint(type), cons((node*)0, cons(nint(paren), nint(term)))); } static void @@ -1012,10 +1014,10 @@ heredoc_end(parser_state *p) } else { /* next heredoc */ - p->lex_strterm->car = (node*)(intptr_t)parsing_heredoc_inf(p)->type; + p->lex_strterm->car = nint(parsing_heredoc_inf(p)->type); } } -#define is_strterm_type(p,str_func) ((int)(intptr_t)((p)->lex_strterm->car) & (str_func)) +#define is_strterm_type(p,str_func) (intn((p)->lex_strterm->car) & (str_func)) /* xxx ----------------------------- */ @@ -1111,6 +1113,7 @@ heredoc_end(parser_state *p) %type <id> fsym sym basic_symbol operation operation2 operation3 %type <id> cname fname op f_rest_arg f_block_arg opt_f_block_arg f_norm_arg f_opt_asgn %type <nd> heredoc words symbols +%type <num> call_op call_op2 /* 0:'&.', 1:'.', 2:'::' */ %token tUPLUS /* unary+ */ %token tUMINUS /* unary- */ @@ -1139,6 +1142,7 @@ heredoc_end(parser_state *p) %token tSTAR /* * */ %token tAMPER /* & */ %token tLAMBDA /* -> */ +%token tANDDOT /* &. */ %token tSYMBEG tREGEXP_BEG tWORDS_BEG tSYMBOLS_BEG %token tSTRING_BEG tXSTRING_BEG tSTRING_DVAR tLAMBEG %token <nd> tHEREDOC_BEG /* <<, <<- */ @@ -1320,15 +1324,15 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym } | primary_value '[' opt_call_args rbracket tOP_ASGN command_call { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); + $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6); } - | primary_value '.' tIDENTIFIER tOP_ASGN command_call + | primary_value call_op tIDENTIFIER tOP_ASGN command_call { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } - | primary_value '.' tCONSTANT tOP_ASGN command_call + | primary_value call_op tCONSTANT tOP_ASGN command_call { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN command_call { @@ -1337,7 +1341,7 @@ stmt : keyword_alias fsym {p->lstate = EXPR_FNAME;} fsym } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN command_call { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); } | backref tOP_ASGN command_call { @@ -1402,7 +1406,7 @@ command_call : command ; block_command : block_call - | block_call dot_or_colon operation2 command_args + | block_call call_op2 operation2 command_args ; cmd_brace_block : tLBRACE_ARG @@ -1427,23 +1431,23 @@ command : operation command_args %prec tLOWEST args_with_block(p, $2, $3); $$ = new_fcall(p, $1, $2); } - | primary_value '.' operation2 command_args %prec tLOWEST + | primary_value call_op operation2 command_args %prec tLOWEST { - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, $2); } - | primary_value '.' operation2 command_args cmd_brace_block + | primary_value call_op operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, $2); } | primary_value tCOLON2 operation2 command_args %prec tLOWEST { - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, tCOLON2); } | primary_value tCOLON2 operation2 command_args cmd_brace_block { args_with_block(p, $4, $5); - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, tCOLON2); } | keyword_super command_args { @@ -1559,19 +1563,19 @@ mlhs_node : variable } | primary_value '[' opt_call_args rbracket { - $$ = new_call(p, $1, intern("[]",2), $3); + $$ = new_call(p, $1, intern("[]",2), $3, '.'); } - | primary_value '.' tIDENTIFIER + | primary_value call_op tIDENTIFIER { - $$ = new_call(p, $1, $3, 0); + $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tIDENTIFIER { - $$ = new_call(p, $1, $3, 0); + $$ = new_call(p, $1, $3, 0, tCOLON2); } - | primary_value '.' tCONSTANT + | primary_value call_op tCONSTANT { - $$ = new_call(p, $1, $3, 0); + $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tCONSTANT { @@ -1598,19 +1602,19 @@ lhs : variable } | primary_value '[' opt_call_args rbracket { - $$ = new_call(p, $1, intern("[]",2), $3); + $$ = new_call(p, $1, intern("[]",2), $3, '.'); } - | primary_value '.' tIDENTIFIER + | primary_value call_op tIDENTIFIER { - $$ = new_call(p, $1, $3, 0); + $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tIDENTIFIER { - $$ = new_call(p, $1, $3, 0); + $$ = new_call(p, $1, $3, 0, tCOLON2); } - | primary_value '.' tCONSTANT + | primary_value call_op tCONSTANT { - $$ = new_call(p, $1, $3, 0); + $$ = new_call(p, $1, $3, 0, $2); } | primary_value tCOLON2 tCONSTANT { @@ -1744,19 +1748,19 @@ arg : lhs '=' arg } | primary_value '[' opt_call_args rbracket tOP_ASGN arg { - $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3), $5, $6); + $$ = new_op_asgn(p, new_call(p, $1, intern("[]",2), $3, '.'), $5, $6); } - | primary_value '.' tIDENTIFIER tOP_ASGN arg + | primary_value call_op tIDENTIFIER tOP_ASGN arg { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } - | primary_value '.' tCONSTANT tOP_ASGN arg + | primary_value call_op tCONSTANT tOP_ASGN arg { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, $2), $4, $5); } | primary_value tCOLON2 tIDENTIFIER tOP_ASGN arg { - $$ = new_op_asgn(p, new_call(p, $1, $3, 0), $4, $5); + $$ = new_op_asgn(p, new_call(p, $1, $3, 0, tCOLON2), $4, $5); } | primary_value tCOLON2 tCONSTANT tOP_ASGN arg { @@ -2239,7 +2243,7 @@ primary : literal } term { - $<nd>$ = cons(local_switch(p), (node*)(intptr_t)p->in_single); + $<nd>$ = cons(local_switch(p), nint(p->in_single)); p->in_single = 0; } bodystmt @@ -2249,7 +2253,7 @@ primary : literal SET_LINENO($$, $1); local_resume(p, $<nd>6->car); p->in_def = $<num>4; - p->in_single = (int)(intptr_t)$<nd>6->cdr; + p->in_single = intn($<nd>6->cdr); } | keyword_module cpath @@ -2571,18 +2575,18 @@ block_call : command do_block } $$ = $1; } - | block_call dot_or_colon operation2 opt_paren_args + | block_call call_op2 operation2 opt_paren_args { - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, $2); } - | block_call dot_or_colon operation2 opt_paren_args brace_block + | block_call call_op2 operation2 opt_paren_args brace_block { - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, $2); call_with_block(p, $$, $5); } - | block_call dot_or_colon operation2 command_args do_block + | block_call call_op2 operation2 command_args do_block { - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, $2); call_with_block(p, $$, $5); } ; @@ -2591,25 +2595,25 @@ method_call : operation paren_args { $$ = new_fcall(p, $1, $2); } - | primary_value '.' operation2 opt_paren_args + | primary_value call_op operation2 opt_paren_args { - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, $2); } | primary_value tCOLON2 operation2 paren_args { - $$ = new_call(p, $1, $3, $4); + $$ = new_call(p, $1, $3, $4, tCOLON2); } | primary_value tCOLON2 operation3 { - $$ = new_call(p, $1, $3, 0); + $$ = new_call(p, $1, $3, 0, tCOLON2); } - | primary_value '.' paren_args + | primary_value call_op paren_args { - $$ = new_call(p, $1, intern("call",4), $3); + $$ = new_call(p, $1, intern("call",4), $3, $2); } | primary_value tCOLON2 paren_args { - $$ = new_call(p, $1, intern("call",4), $3); + $$ = new_call(p, $1, intern("call",4), $3, tCOLON2); } | keyword_super paren_args { @@ -2621,7 +2625,7 @@ method_call : operation paren_args } | primary_value '[' opt_call_args rbracket { - $$ = new_call(p, $1, intern("[]",2), $3); + $$ = new_call(p, $1, intern("[]",2), $3, '.'); } ; @@ -3189,7 +3193,7 @@ singleton : var_ref yyerror(p, "can't define singleton method for ()."); } else { - switch ((enum node_type)(int)(intptr_t)$3->car) { + switch ((enum node_type)intn($3->car)) { case NODE_STR: case NODE_DSTR: case NODE_XSTR: @@ -3256,6 +3260,23 @@ dot_or_colon : '.' | tCOLON2 ; +call_op : '.' + { + $$ = '.'; + } + | tANDDOT + { + $$ = 0; + } + ; + +call_op2 : call_op + | tCOLON2 + { + $$ = tCOLON2; + } + ; + opt_terms : /* none */ | terms ; @@ -4476,6 +4497,10 @@ parser_yylex(parser_state *p) pushback(p, c); return tANDOP; } + else if (c == '.') { + p->lstate = EXPR_DOT; + return tANDDOT; + } else if (c == '=') { yylval.id = intern_c('&'); p->lstate = EXPR_BEG; |
