From 6e3090413652b6592346556149fed1e9aec5495d Mon Sep 17 00:00:00 2001 From: Yukihiro Matsumoto Date: Wed, 10 Aug 1994 15:54:46 +0900 Subject: version 0.50 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit http://cache.ruby-lang.org/pub/ruby/1.0/ruby-0.50.tar.gz Wed Aug 10 15:54:46 1994 Yukihiro Matsumoto (matz@ix-02) * variable.c: -vオプションが指定されている時は初期化されていない, 大域変数, インスタンス変数, ローカル変数を参照した時点でwarning を出すようにした. Tue Aug 9 11:50:48 1994 Yukihiro Matsumoto (matz@ix-02) * bignum.c: 冪乗に関しても多倍長演算を行なうように. 特に浮動小数点 数の範囲を越えた時の処理を的確に行なうように. * eval.c: メソッド定義後は構文木から, メソッド定義部分を外す. 無駄 な再定義が起こらないようにするためと2重にfree()されないため. * array.c(Fary_aref): 引数が1つでFixnumの時, Range checkを行なわな いように修正. * eval.c: 引数の数をコンパイル時に計算して若干の高速化. Mon Aug 8 13:06:24 1994 Yukihiro Matsumoto (matz@ix-02) * object.c: nilによる比較連鎖をなくした. * parse.y: bit演算子の優先順位を比較演算子よりも強くした. Cとは異 なることになるが, 直観には合致する. * gc.c: クラスを解放する時, 個々のメソッド毎にキャッシュをクリアす るのではなく, クラス単位でクリアするように. Thu Aug 4 18:45:09 1994 Yukihiro Matsumoto (matz@ix-02) * methods.c(method_free): 解放されたメソッドに関してキャッシュをク リアしておく必要があった. * gc.c: Dataクラスのデータ部分をfree()し忘れていた. Wed Aug 3 09:58:14 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y: def func .. end形式による関数メソッドの定義はなくなった. * methods.c: func形式のメソッドをなくした. あっても, あまり意味が ないので. * eval.c: $0への代入でps(1)の出力が変化するように. * io.c(Fsyscall): syscall()を実現. Mon Aug 1 13:41:11 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y: ダブルクォートで囲まれた文字列や正規表現中で"#{変数名}" または"#変数名"という形式で変数の内容を埋め込むことができるよう になった. * io.c: 関数メソッドsystem2()はなくなった. 今はバッククォートがあ るからね. * parse.y: `cmd`によってコマンドを文字列に展開することができるよう になった. * parse.y: __FILE__, __LINE__を追加. それぞれファイル名(文字列), 行番号(整数)を値とする疑似変数. Fri Jul 29 13:16:07 1994 Yukihiro Matsumoto (matz@ix-02) * methods.h: メソッドをオブジェクトとして扱うのをやめる. メソッド のメモリ管理にはリファレンスカウントを使うことにした. これでオブ ジェクトの数が減ってほんの少しだけGCが速くなる(かな). * purifyによってメモリ関係のバグを検査した(見つかる,見つかる…). * gc.c: GCをプログラマが変数をマークする形式から, スタックとレジス タからマークする方法に変更. 移植性が下がるような気もするが, siod やscmでも採用されているから多分大丈夫だろう. Linux on i486でも動 作を確認した. Wed Jul 27 16:13:13 1994 Yukihiro Matsumoto (matz@ix-02) * eval.c(Eval): トップレベルでは構造木をfreeしないように. どうせ解 放されるから時間の無駄である. * array.c, dict.c: "=="を構造一致に変更. Fri Jul 22 10:14:09 1994 Yukihiro Matsumoto (matz@ix-02) * error.c: 組み込みタイプの名前を登録し忘れていた. Thu Jul 21 14:06:48 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y(freenode),eval.c(Eval): 解析木を解放し忘れていた. Mon Jul 18 10:19:15 1994 Yukihiro Matsumoto (matz@ix-02) * parse.y: 多重代入を処理するルールにバグがあって, 3要素以上の多重 代入に失敗していた. * eval.c(rb_eval): 多重代入で, 右辺が配列でない時には`to_a'メソッ ドで配列に変換して代入するようにした. 今までの仕様だと右辺値が第 1要素にそのまま代入されていたが, structなど配列に変換できるもの は変換した方が嬉しい気がする. * dbm.c,dict.c(delete_if): メソッド追加. * process.c(wait,waitpid): システムコールwaitpidまたはwait4がある 時はそちらを使うように. configureもそれらをチェックするように変更. * dbm.c, dict.c(clear): メソッド追加. --- parse.y | 388 +++++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 296 insertions(+), 92 deletions(-) (limited to 'parse.y') diff --git a/parse.y b/parse.y index 0f4f640934..ff1a7f669d 100644 --- a/parse.y +++ b/parse.y @@ -17,6 +17,7 @@ #include "env.h" #include "node.h" #include "st.h" +#include #include "ident.h" #define is_id_nonop(id) ((id)>LAST_TOKEN) @@ -35,6 +36,9 @@ struct op_tbl { NODE *eval_tree = Qnil; static int in_regexp; +char *sourcefile; /* current source file */ +int sourceline; /* current line no. */ + enum { KEEP_STATE = 0, /* don't change lex_state. */ EXPR_BEG, /* ignore newline, +/- is a sign. */ @@ -57,6 +61,7 @@ static NODE *list_concat(); static NODE *list_copy(); static NODE *call_op(); +static NODE *gettable(); static NODE *asignable(); static NODE *aryset(); static NODE *attrset(); @@ -82,7 +87,6 @@ static void setup_top_local(); %token CLASS MODULE DEF - FUNC UNDEF INCLUDE IF @@ -111,16 +115,19 @@ static void setup_top_local(); RETRY SELF NIL + _FILE_ + _LINE_ -%token IDENTIFIER GVAR IVAR CONSTANT -%token INTEGER FLOAT STRING REGEXP +%token IDENTIFIER GVAR IVAR CONSTANT +%token INTEGER FLOAT STRING XSTRING REGEXP +%token STRING2 XSTRING2 DREGEXP %type singleton inc_list %type literal numeric %type compexpr exprs expr expr2 primary var_ref %type if_tail opt_else cases resque ensure opt_using %type call_args opt_args args f_arglist f_args f_arg -%type assoc_list assocs assoc +%type assoc_list assocs assoc regexp %type mlhs mlhs_head mlhs_tail lhs %type superclass variable symbol %type fname fname0 op rest_arg end_mark @@ -152,10 +159,10 @@ static void setup_top_local(); %nonassoc DOT2 DOT3 %left OR %left AND -%left '|' '^' -%left '&' %nonassoc CMP EQ NEQ MATCH NMATCH %left '>' GEQ '<' LEQ +%left '|' '^' +%left '&' %left LSHFT RSHFT %left '+' '-' %left '*' '/' '%' @@ -241,24 +248,7 @@ expr : CLASS IDENTIFIER superclass if ($7 && $7 != DEF) { Error("unmatched end keyword(expected `def')"); } - $$ = NEW_DEFN($2, NEW_RFUNC($4, $5), MTH_METHOD); - pop_local(); - cur_mid = Qnil; - } - | DEF FUNC fname - { - if (cur_mid || in_single) - Error("nested method definition"); - cur_mid = $3; - push_local(); - } - f_arglist compexpr - END end_mark - { - if ($8 && $8 != DEF) { - Error("unmatched end keyword(expected `def')"); - } - $$ = NEW_DEFN($3, NEW_RFUNC($5, $6), MTH_FUNC); + $$ = NEW_DEFN($2, NEW_RFUNC($4, $5)); pop_local(); cur_mid = Qnil; } @@ -421,6 +411,14 @@ op : COLON2 { $$ = COLON2; } f_arglist : '(' f_args rparen { + if ($2) { + NODE *list = $2->nd_frml; + int i; + + for (i=0; list; list=list->nd_next, i++) + ; + $2->nd_cnt = i; + } $$ = $2; } | term @@ -457,7 +455,7 @@ f_arg : IDENTIFIER { if (!is_local_id($1)) Error("formal argument must be local variable"); - $$ = NEW_LIST(local_cnt($1)); + $$ = NEW_QLIST(local_cnt($1)); } | f_arg comma IDENTIFIER { @@ -887,26 +885,32 @@ args : expr2 } primary : var_ref - | '(' compexpr rparen + | literal { - $$ = $2; + literalize($1); + $$ = NEW_LIT($1); } - | STRING { literalize($1); $$ = NEW_STR($1); } + | STRING2 + | XSTRING + { + literalize($1); + $$ = NEW_XSTR($1); + } + | XSTRING2 + | '/' {in_regexp = 1;} regexp + { + $$ = $3; + } | primary '[' args rbracket { value_expr($1); $$ = NEW_CALL($1, AREF, $3); } - | literal - { - literalize($1); - $$ = NEW_LIT($1); - } | '[' opt_args rbracket { if ($2 == Qnil) @@ -949,16 +953,23 @@ primary : var_ref Error("super called outside of method"); $$ = NEW_ZSUPER(); } + | '(' compexpr rparen + { + $$ = $2; + } literal : numeric | '\\' symbol { $$ = INT2FIX($2); } - | '/' {in_regexp = 1;} REGEXP + +regexp : REGEXP { - $$ = $3; + literalize($1); + $$ = NEW_LIT($1); } + | DREGEXP symbol : fname0 | IVAR @@ -983,27 +994,7 @@ variable : IDENTIFIER var_ref : variable { - if ($1 == SELF) { - $$ = NEW_SELF(); - } - else if ($1 == NIL) { - $$ = NEW_NIL(); - } - else if (is_local_id($1)) { - if (local_id($1)) - $$ = NEW_LVAR($1); - else - $$ = NEW_MVAR($1); - } - else if (is_global_id($1)) { - $$ = NEW_GVAR($1); - } - else if (is_instance_id($1)) { - $$ = NEW_IVAR($1); - } - else if (is_const_id($1)) { - $$ = NEW_CVAR($1); - } + $$ = gettable($1); } assoc_list : /* none */ @@ -1027,7 +1018,6 @@ assoc : expr2 ASSOC expr2 end_mark : CLASS { $$ = CLASS; } | MODULE { $$ = MODULE; } | DEF { $$ = DEF; } - | FUNC { $$ = FUNC; } | IF { $$ = IF; } | UNLESS { $$ = UNLESS; } | CASE { $$ = CASE; } @@ -1054,7 +1044,6 @@ rbrace : '}' { yyerrok; } comma : ',' { yyerrok; } %% -#include #include #include #include "regex.h" @@ -1075,11 +1064,9 @@ char *strdup(); #define EXPAND_B 1 #define LEAVE_BS 2 +static NODE *var_extend(); static void read_escape(); -char *sourcefile; /* current source file */ -int sourceline; /* current line no. */ - static char *lex_p; static int lex_len; @@ -1135,7 +1122,9 @@ static struct kwtable { int id; int state; } kwtable [] = { - "__END__", 0, KEEP_STATE, + "__END__", 0, KEEP_STATE, + "__FILE__", _FILE_, EXPR_END, + "__LINE__", _LINE_, EXPR_END, "break", BREAK, EXPR_END, "case", CASE, KEEP_STATE, "class", CLASS, KEEP_STATE, @@ -1147,7 +1136,6 @@ static struct kwtable { "end", END, EXPR_END, "ensure", ENSURE, EXPR_BEG, "for", FOR, KEEP_STATE, - "func", FUNC, KEEP_STATE, "if", IF, KEEP_STATE, "in", IN, EXPR_BEG, "include", INCLUDE, EXPR_BEG, @@ -1170,6 +1158,8 @@ static struct kwtable { "yield", YIELD, EXPR_BEG, }; +static int strstart; + yylex() { register int c; @@ -1179,6 +1169,7 @@ yylex() if (in_regexp) { int in_brack = 0; int re_start = sourceline; + NODE *list = Qnil; in_regexp = 0; newtok(); @@ -1190,6 +1181,12 @@ yylex() case ']': in_brack = 0; break; + + case '#': + list = var_extend(list, '/'); + if (list == (NODE*)-1) return 0; + continue; + case '\\': if ((c = nextc()) == -1) { sourceline = re_start; @@ -1213,9 +1210,21 @@ yylex() break; tokfix(); - yylval.val = regexp_new(tok(), toklen()); lex_state = EXPR_END; - return REGEXP; + if (list) { + if (toklen() > 0) { + VALUE ss = str_new(tok(), toklen()); + literalize(ss); + list_append(list, NEW_STR(ss)); + } + list->type = NODE_DREGX; + yylval.node = list; + return DREGEXP; + } + else { + yylval.val = regexp_new(tok(), toklen()); + return REGEXP; + } case -1: Error("unterminated regexp"); @@ -1335,12 +1344,17 @@ retry: return '>'; case '"': + case '`': { - int strstart = sourceline; + char term = c; + NODE *list = Qnil; + ID id; + strstart = sourceline; newtok(); - while ((c = nextc()) != '"') { + while ((c = nextc()) != term) { if (c == -1) { + unterm_str: sourceline = strstart; Error("unterminated string meets end of file"); return 0; @@ -1352,12 +1366,17 @@ retry: else if (c == '\n') { sourceline++; } + else if (c == '#') { + list = var_extend(list, term); + if (list == (NODE*)-1) return 0; + continue; + } else if (c == '\\') { c = nextc(); if (c == '\n') { sourceline++; } - else if (c == '"') { + else if (c == term) { tokadd(c); } else { @@ -1369,22 +1388,34 @@ retry: tokadd(c); } tokfix(); - yylval.val = str_new(tok(), toklen()); lex_state = EXPR_END; - return STRING; + if (list == Qnil) { + yylval.val = str_new(tok(), toklen()); + return (term == '"') ? STRING : XSTRING; + } + else { + if (toklen() > 0) { + VALUE ss = str_new(tok(), toklen()); + literalize(ss); + list_append(list, NEW_STR(ss)); + } + yylval.node = list; + if (term == '"') { + return STRING2; + } + else { + list->type = NODE_XSTR2; + return XSTRING2; + } + } } case '\'': { - int strstart = sourceline; - - newtok(); + strstart = sourceline; + newtok(); while ((c = nextc()) != '\'') { - if (c == -1) { - sourceline = strstart; - Error("unterminated string meets end of file"); - return 0; - } + if (c == -1) goto unterm_str; if (ismbchar(c)) { tokadd(c); c = nextc(); @@ -1639,7 +1670,6 @@ retry: case ',': case ';': - case '`': case '[': case '(': case '{': @@ -1765,6 +1795,103 @@ retry: } } +static NODE* +var_extend(list, term) + NODE *list; + char term; +{ + int c, t; + VALUE ss; + ID id; + + c = nextc(); + switch (c) { + default: + tokadd('#'); + pushback(); + return list; + case '@': case '%': + t = nextc(); + pushback(); + if (!is_identchar(t)) { + tokadd('#'); + tokadd(c); + return list; + } + case '$': + case '{': + break; + } + + ss = str_new(tok(), toklen()); + if (list == Qnil) { + literalize(ss); + list = NEW_STR2(ss); + } + else if (toklen() > 0) { + literalize(ss); + list_append(list, NEW_STR(ss)); + } + newtok(); + if (c == '{') { + while ((c = nextc()) != '}') { + if (c == -1) { + unterm_str: + sourceline = strstart; + Error("unterminated string meets end of file"); + return (NODE*)-1; + } + if (isspace(c)) { + Error("Invalid variable name in string"); + break; + } + if (c == term) { + Error("Inmature variable name in string"); + pushback(); + return list; + } + tokadd(c); + } + } + else { + switch (c) { + case '$': + tokadd(c); + c = nextc(); + if (c == -1) goto unterm_str; + if (!is_identchar(c)) { + tokadd(c); + goto fetch_id; + } + /* through */ + case '@': case '%': + tokadd(c); + c = nextc(); + break; + } + while (is_identchar(c)) { + tokadd(c); + if (ismbchar(c)) { + c = nextc(); + tokadd(c); + } + c = nextc(); + } + pushback(); + } + fetch_id: + tokfix(); + if (strcmp("__LINE__", tok()) == 0) + id = _LINE_; + else if (strcmp("__FILE__", tok()) == 0) + id = _FILE_; + else + id = rb_intern(tok()); + list_append(list, gettable(id)); + newtok(); + return list; +} + static void read_escape(flag) int flag; @@ -1890,6 +2017,7 @@ read_escape(flag) if (flag & LEAVE_BS) { tokadd('\\'); } + case '#': tokadd(c); break; } @@ -1957,11 +2085,12 @@ static NODE* list_append(head, tail) NODE *head, *tail; { - if (head == Qnil) return NEW_ARRAY(tail); + if (head == Qnil) return NEW_LIST(tail); if (head->nd_last == Qnil) head->nd_last = head; - - head->nd_last->nd_next = NEW_ARRAY(tail); + + head->nd_last->nd_next = + head->type == NODE_QLIST?NEW_QLIST(tail):NEW_LIST(tail); head->nd_last = head->nd_last->nd_next; return head; } @@ -2007,9 +2136,17 @@ void freenode(node) case NODE_BLOCK: case NODE_ARRAY: freenode(node->nd_head); + case NODE_STR2: + case NODE_XSTR2: + case NODE_DREGX: + case NODE_QLIST: freenode(node->nd_next); break; + case NODE_HASH: + freenode(node->nd_head); + break; case NODE_IF: + case NODE_UNLESS: case NODE_WHEN: case NODE_PROT: freenode(node->nd_cond); @@ -2026,57 +2163,85 @@ void freenode(node) break; case NODE_DO: case NODE_FOR: + freenode(node->nd_var); freenode(node->nd_ibdy); freenode(node->nd_iter); break; case NODE_LASGN: case NODE_GASGN: case NODE_IASGN: + case NODE_CASGN: freenode(node->nd_value); break; + case NODE_MASGN: + freenode(node->nd_value); + freenode(node->nd_head); + break; case NODE_CALL: case NODE_SUPER: freenode(node->nd_recv); - case NODE_CALL2: freenode(node->nd_args); break; + case NODE_CALL2: + { + NODE *list = node->nd_next, *tmp; + while (list) { + tmp = list; + list = list->nd_next; + free(tmp); + } + } + break; case NODE_DEFS: freenode(node->nd_recv); + case NODE_DEFN: + freenode(node->nd_defn); break; case NODE_RETURN: case NODE_YIELD: freenode(node->nd_stts); break; case NODE_STR: + case NODE_XSTR: case NODE_LIT: unliteralize(node->nd_lit); break; - case NODE_CONST: - unliteralize(node->nd_cval); - break; case NODE_ARGS: freenode(node->nd_frml); break; case NODE_SCOPE: - free(node->nd_tbl); + if (node->nd_tbl) free(node->nd_tbl); freenode(node->nd_body); break; - case NODE_DEFN: + case NODE_DOT3: + freenode(node->nd_beg); + freenode(node->nd_end); + break; + case NODE_CLASS: + case NODE_MODULE: + freenode(node->nd_body); + break; + case NODE_ATTRSET: + case NODE_CVAR: + case NODE_CONST: + case NODE_ZSUPER: case NODE_ZARRAY: case NODE_CFUNC: case NODE_BREAK: case NODE_CONTINUE: + case NODE_REDO: case NODE_RETRY: case NODE_LVAR: case NODE_GVAR: case NODE_IVAR: case NODE_MVAR: - case NODE_CLASS: - case NODE_MODULE: case NODE_INC: + case NODE_UNDEF: + case NODE_ALIAS: case NODE_NIL: + case NODE_SELF: break; - default: + default: Bug("freenode: unknown node type %d", node->type); break; } @@ -2147,6 +2312,42 @@ call_op(recv, id, narg, arg1) } } +static NODE* +gettable(id) + ID id; +{ + if (id == SELF) { + return NEW_SELF(); + } + else if (id == NIL) { + return NEW_NIL(); + } + else if (id == _LINE_) { + return NEW_LIT(INT2FIX(sourceline)); + } + else if (id == _FILE_) { + VALUE s = str_new2(sourcefile); + + literalize(s); + return NEW_STR(s); + } + else if (is_local_id(id)) { + if (local_id(id)) + return NEW_LVAR(id); + else + return NEW_MVAR(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_CVAR(id); + } +} + static NODE* asignable(id, val) ID id; @@ -2162,6 +2363,9 @@ asignable(id, val) lhs = Qnil; Error("Can't asign to nil"); } + else if (id == _LINE_ || id == _FILE_) { + Error("Can't asign to special identifier"); + } else if (is_local_id(id)) { lhs = NEW_LASGN(id, val); } @@ -2348,7 +2552,7 @@ setup_top_local() the_env->local_vars = ALLOC_N(VALUE, lvtbl->cnt); bzero(the_env->local_vars, lvtbl->cnt * sizeof(VALUE)); } - else { + else if (lvtbl->tbl[0] < lvtbl->cnt) { int i; REALLOC_N(the_env->local_vars, VALUE, lvtbl->cnt); -- cgit v1.2.3