summaryrefslogtreecommitdiff
path: root/parse.y
diff options
context:
space:
mode:
authorYukihiro Matsumoto <matz@ruby-lang.org>1994-07-18 10:19:15 +0900
committerTakashi Kokubun <takashikkbn@gmail.com>2019-08-17 22:09:29 +0900
commit200e0ee2fd3c1c006c528874a88f684447215524 (patch)
treedf2252585f3000878a15123d98ccb08124306b90 /parse.y
version 0.49v0_49
https://cache.ruby-lang.org/pub/ruby/1.0/ruby-0.49.tar.gz 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): メソッド追加. Fri Jul 15 10:54:45 1994 Yukihiro Matsumoto (matz@ix-02) * array.c(Fary_fill,Fary_clear): メソッドを追加. * string.c(Fstr_split): $;の値が長さ1の文字列である時, これを正規 表現化しないで, 単なる文字として分割する. * string.c(Fstr_aset/Fstr_aref): インデックスが文字列の範囲外だっ た時の動作をArrayを参考に修正した. * array.c(astore,Fary_aset): 領域をreallocした後, ゼロでクリアする ように. 今まで配列にゴミが入っていた. * array.c: []/[]=でのインデックス関係を整理. 基本的に負のインデッ クスに代入しない限り例外は起きないように変更した. 必要に応じて適 当に解釈して, 必要ならば領域を拡張するように. Thu Jul 14 11:18:07 1994 Yukihiro Matsumoto (matz@ix-02) * autoexec.c: 削除. autoload関係の機能は今後検討しよう. * dict.c: 辞書クラスの正式名称をDictに変更した. 別名としてHashを用 意した. 今までDictionaryなどと長い名前にしていたが誰も使っていな かったしね. *BACKWARD INCOMPATIBILITY* * parse.y: Dictを生成する構文を追加. こちらを{..}にした. * parse.y: 配列を生成する構文を[..]に変更した. 過去のRubyスクリプ トとの互換性が保てないが, Dictを生成する構文を導入するに当たり, perl5に合わせて(意識して), 変更する時期は今しかないと考えた. *BACKWARD INCOMPATIBILITY* * eval.c(Feval): eval()でメソッドを定義する時, 定義されるクラスを メソッドの所属するクラスにした. 今まではObjectクラスに定義されて いた. * parse.y: ローカル引数がない時のeval()で落ちていた. Thu Jul 14 11:18:07 1994 Yukihiro Matsumoto (matz@ix-02) * eval.c: メソッドが存在しない時にはKernel:_undefined(id)が呼ばれ るように. しかし, rubyでは個々のメソッド毎の処理よりも, クラス 単位の処理が必要な気もするなあ. * autoexec.c: 削除. autoload関係の機能は今後検討しよう. * dict.c: 辞書クラスの正式名称をDictに変更した. 別名としてHashを用 意した. 今までDictionaryなどと長い名前にしていたが誰も使っていな かったしね. *BACKWARD INCOMPATIBILITY* * parse.y: Dictを生成する構文を追加. こちらを{..}にした. * parse.y: 配列を生成する構文を[..]に変更した. 過去のRubyスクリプ トとの互換性が保てないが, Dictを生成する構文を導入するに当たり, perl5に合わせて(意識して), 変更する時期は今しかないと考えた. *BACKWARD INCOMPATIBILITY* * eval.c(Feval): eval()でメソッドを定義する時, 定義されるクラスを メソッドの所属するクラスにした. 今まではObjectクラスに定義されて いた. * parse.y: ローカル引数がない時のeval()で落ちていた. Tue Jul 12 09:41:28 1994 Yukihiro Matsumoto (matz@ix-02) * pack.c: uuencode形式のサポート. * `-0'を`-R'に. 出力レコードセパレータをコマンドラインから指定する 方法はなくなった. どうも, 仕様がゆれるなあ. Mon Jul 11 09:51:24 1994 Yukihiro Matsumoto (matz@ix-02) * ruby.c: `-r'オプションは`-0'オプションになった. 当面は`-r'も有効 だが変更される可能性がある. `-R'は当面はそのまま. * version.c: version表示に日付を含めた. * parse.y: private methodの復活. RubyのprivateメソッドはC++におけ るprotected methodに該当するもので, `@'で始まる名前を持つ. * env.h: struct ENVIRONの定義を分離. * parse.y: `\$var', `\@var', `%var'も許すように. * variable.c(Fdefined): idも引数として受け付けるように. * parse.y: if文/unless文にキーワードthenを追加. thenなしというのは, 意外と間違いが多いので. もちろん省略できる. Sat Jul 9 02:16:04 1994 Yukihiro Matsumoto (matz@dyna) * eval.c(rb_eval): class/moduleの評価で新しいスコープを割り当てて いなかった. スコープ割り当て部分をrb_call()からrb_eval()に移した. * eval.c(rb_call): realloc()に渡される事のある, ローカル変数用の領 域をalloca()していた. たまに落ちるわけだ. * string.c(Fstr_times): 割り当てた領域を越えた部分を変更していた. Wed Jul 6 15:52:42 1994 Yukihiro Matsumoto (matz@ix-02) * socket.c: Socket -> BasicSocket, RawSocket -> Socket に改名. * string.c(ucfirst,lcfirst): 最初の1文字だけの大文字/小文字変換. * numeric.c(chr): 整数の文字列化メソッド. * inits.c, dbm.c: DBMが使えない時はクラスそのものを定義しないよう にした. 利用できないクラスはnilとすることを今後のポリシーとしよ う(いままではアクセスした時点でエラーが発生していた). autoexec() のあり方も検討が必要になりそうだ. * bignum.c(bigadd): バグ修正. Thu Jul 7 11:12:18 1994 Yukihiro Matsumoto (matz@ix-02) * eval.c(Fload,Feval): eval_treeをクリアし忘れていた. * _inspect: オブジェクトを可読形式の文字列に変換する(主にデバッグ 出力用). Wed Jul 6 00:57:18 1994 Yukihiro Matsumoto (matz@dyna) * numeric.c, bignum.c: 整数に対する`[]'演算子. nビット目がセットさ れているかどうかを返す. Tue Jul 5 12:48:39 1994 Yukihiro Matsumoto (matz@ix-02) * io.c(Feof): 追加. コマンドラインからなる仮想ファイルについても EOFが検出できるように. * ruby.c: -l/-r/-R/-Xオプションの追加. * ruby.c: -n/-pオプションのloopの付加などをメインルーチンに移動し た. これで, オプションの解析途中で(`-c'オプションのせいで)終了な どといったことはない. * io.c(Fgets): 高速化. 凝ったことをしない方が速かった. 虚しい. Mon Jul 4 15:55:48 1994 Yukihiro Matsumoto (matz@ix-02) * Socket:getsockname/getpeername - ようやく仕様が安定した. * io.c(Fgets): eachでgetsを記述するのではなく, getsでeachを記述す るようにした. Fri Jul 1 10:35:49 1994 Yukihiro Matsumoto (matz@ix-02) * $ENV[env] = nil/$ENV.delete(env)で環境変数を削除できるようになっ た. $ENV.deleteは環境変数の以前の値を返す. * !~の定義が間違っていた. * Dict,DBM:[]= - nilの代入によって要素を削除できるようになった. こ れにともないnilはDictの要素になれなくなった. * ソースの整理. 盲腸のような使われていないコードをなくしたり, 変数 名を付け変えたりした. Fri Jul 1 00:21:29 1994 Yukihiro Matsumoto (matz@dyna) * Array:join() - 要素数0の配列に対して空文字列を返す. * RawSocket:open(),socketpair() - 文字列で指定できるドメインとタイ プをいくつか追加した. Thu Jun 30 13:51:29 1994 Yukihiro Matsumoto (matz@ix-02) * -fオプションをなくした. 昔(loadのなかった頃)の名残なので, 今となっ ては必要ないだろう. * -sオプションを追加. perlの-sオプションと同じ動きをする. * RawSocketクラスを提供する. Socketに対するシステムコールレベルの アクセスが可能になった. Thu Jun 30 00:27:19 1994 Yukihiro Matsumoto (matz@dyna) * Socket - bug fixes. * linuxではsyscall(SYS_select)が正常に動作しない. * Socket:addr,peeraddr - 配列としてsockaddrの情報を返す. Wed Jun 29 00:14:20 1994 Yukihiro Matsumoto (matz@dyna) * Socket:setopt,getopt - setsockopt(2), getsockopt(2)へのアクセス を実現. * sprintf() - rubyにはunsignedは無いので, %uを取り除いた. * sprintf() - %b, %x, %oでは2の補数表現, %B, %X, %Oでは符号付き表 現で出力するように. ここ数日でsprintf()の仕様がゆらいでいたが, これで落ち着きそうだ. Tue Jun 28 14:42:03 1994 Yukihiro Matsumoto (matz@ix-02) * Bignum:<<,>> - 2の補数をとる処理を除いた. シフト演算には関係ない 処理だった. * Bignum:^ - bug fix. 符合が反対だった. * sprintf() - 2進出力子"%b"を追加. * sprintf() - %x, %oでFixnumを出力する時, 2の補数表示を行なわない. * sprintf() - %x, %oはやはり負の数の時は`-'を出力するように. Mon Jun 27 14:56:13 1994 Yukihiro Matsumoto (matz@ix-02) * sprintf() - Bignumについても%d, %oは(2の補数表現に変換して)正の 整数を表示するようにした. * Bignumに対する論理演算の定義を修正した. 負の数は2の補数表現であ るとみなし, かつ仮想的に左側に無限に1が連続しているような演算結 果を得る. * Fixnum:<<,>> - 符合付シフトに変更. * Bignum:>> - 負の整数のシフトに対応した. * __END__, ^D, ^Zでスクリプトを終了できる. * -xオプションを追加. #! ..rubyなる行まで読み飛ばす. * -cオプションを追加. コンパイルのみを行う. Sat Jun 25 01:37:21 1994 Yukihiro Matsumoto (matz@dyna) * Fixnum:<< - 必要に応じてBignumに拡張して左シフトするように. よっ て, シフト幅が32を越えるとCやPerlとは違った値を返す. Fri Jun 24 10:01:28 1994 Yukihiro Matsumoto (matz@ix-02) * ioctl()/fcntl() - システムコールを呼び出す前にバッファの大きさを 調節するようにした. * String:toupper/tolower - 文字列を置き換えたコピーを作るのではな く, 元の文字列の内容を変更するようにした. * inplace editを実現した. perlと同じように`-i'オプションで指定する. もっとも, こちらはMS-DOSのこととか考えてないけど. * デフォルトの出力先を追加した. 今までは$stdoutに代入するしか方法 はなかった. Fri Jun 17 10:55:08 1994 Yukihiro Matsumoto (matz@ix-02) * 環境変数にアクセスする方法としてgetenv()/setenv()以外に$ENVを用 意した. $ENVは文字列-文字列の辞書であるかのように動作するEnvDict オブジェクトが代入されている(eachはassocを与える). * nilに代入するとcore dumpした. コンパイル時のチェックを強化. * Struct: struct_new()の引数をGCプロテクトする必要がある. せめてス タック領域だけでもスキャンできるようにしなければいけないんだろう か? でも, 移植性がなあ. Fri Jun 17 01:01:46 1994 Yukihiro Matsumoto (matz@dyna) * Time::asctime() - 日付のフォーマットで日が落ちていた. * Stat: StatはEtcなどと同様にStructで実現したので, Statクラスは無 くなった. Thu Jun 16 10:32:23 1994 Yukihiro Matsumoto (matz@ix-02) * bignum.c: いくつかのバグを修正した. Fixnumを渡すべきところで普通 のintを渡していた. 失敗. * big2str() - 1桁ずつbaseで割る代わりに, 4桁ずつ割算を行なうように した. これで多倍長割算の回数が1/4になる. さらに整数->数(文字)へ の変換をテーブルを用いるようにした. * rb_ivar_get_1() - すでに何らかのインスタンス変数を持つオブジェク トでは, 未定義のインスタンス変数の値が不定値になっていた. * yylex() - インスタンス変数の認識に失敗していた. attr()は正しく動 作していたので, 混用すると動作しなかった. 全部違っていたから動い ていたのね. * Object:attr() - すでにアクセスメソッドが定義されている時にはデフォ ルトのアクセスメソッドを定義しないようにした. もっともアクセスメ ソッドと同名のメソッドの区別はRubyには存在しないけど, それは仕方 がないよね. * pack.c: エンディアンをautoconfで判定するようにしたので, v/Vが使 えるようになった. またntoh?()/hton?()も自前で用意した. * Stat: st_rdevをアクセスするメソッドを追加. さらにシステムがstat 構造体にst_blksize, st_blockを持っているかをautoconfでチェックす るようにした. * ドキュメントを少し整備した. * INT2FIX()のうち, 31bit幅が保証できないものは, int2inum()に置き換 えた. Wed Jun 15 10:18:27 1994 Yukihiro Matsumoto (matz@ix-02) * sprintf() - bignumの出力の時, 出力幅を正しく計算するようにした. * str2inum() - baseが0の時, baseを自動判定するように(0xで始まる時 16 進, 0で始まる時8進). Tue Jun 14 16:08:42 1994 Yukihiro Matsumoto (matz@ix-02) * gc.c: Bignumを追加するのを忘れていた. 組み込み型を追加した時には 必ずmark()とsweep()にその型に関する処理を追加する必要がある. * bignum: 割算も動いたような気がする. アルゴリズムを理解していない ので, 自信がない. Mon Jun 13 14:36:55 1994 Yukihiro Matsumoto (matz@ix-02) * まだサポートしていないメソッドなどがあるが, 曲がりなりにもBignum が使えるようになる. これでioctlも使える. Fri Jun 10 17:26:42 1994 Yukihiro Matsumoto (matz@ix-02) * Comparable: 基礎となるメソッドを`=='と`>'から`<=>'に変更した. 今 後Comparableのサブクラスは`<=>'だけを定義する必要がある. Wed Jun 8 13:12:18 1994 Yukihiro Matsumoto (matz@ix-02) * Need_Fixnum()をほとんどなくして, NUM2INT()で直接intに変換するこ とにした. これで31bitに丸めて桁落ちをおこす問題がなくなる. Tue Jun 7 09:45:31 1994 Yukihiro Matsumoto (matz@ix-02) * ruby.h: マクロFIXABLE(n)を追加. ついでにFIXNUM周りの定義を変更し て, 移植性を高めた(つもり). * C++の予約語であるnewを削除した. しかし, もうひとつの予約語である classに関しては, 置き換える単語が思いつかないこともあってそのま まになっている. * 31bitを越えそうなINT2FIX()を関数呼び出しに変えた. 将来bignumが導 入された時には自動的にbignumを返すようにする. * readline() - 引数の`-'は標準入力を意味するようになった. * ruby.h: 右シフトが論理シフトか算術シフトかは処理系依存のようなの で, ruby.hでcppを使ってチェックするようにした. これでうまくいく と思うのだが, 手元に符合付intを論理シフトする処理系がないので確 認できない. NEWS-OSのCCは確か右シフトはいつも論理シフトだったよ うな気がするんだけど…. Mon Jun 6 10:10:22 1994 Yukihiro Matsumoto (matz@ix-02) * FIX2INT()の定義を変更した. どうして昔はうまく動かなかったんだろ うか? もしかして, 右シフトの符号拡張は処理系依存? * FIX2INT()とFIX2UINT()を使い分けるようにした. もっともfixnumは31 ビットしかないので, 本質的な解決にはならないのだが(ioctlが組み込 みたかった). * printを関数的メソッドから通常メソッドに変更. 引数が与えられない 時にはレシーバをプリントするようにした. これでprintをメッセージ 形式でも実行できるようになった. 例: ruby -e 'readlines().sort.print' 上のスクリプトは, 引数として与えられた(あるいは標準入力から読み 込まれた)文字列を各行毎にソートして表示する. * eval.c: argc,argvパターンで引数を受けるメソッドに引数が一つも与 えられない時, argvがnilになっていた(argv[0]にアクセスすると落ち てしまう). * _exit()を追加. こちらは例外処理など行なわない. * dbmクラス: クラス名称をDBM(大文字)に統一した. Sat Jun 4 00:51:04 1994 Yukihiro Matsumoto (matz@dyna) * ループ変数にも属性や配列要素を指定できるようにした. Fri Jun 3 09:49:48 1994 Yukihiro Matsumoto (matz@ix-02) * 多重代入において, 属性代入, 配列要素への代入も行なえるようにした. * Need_Fixnum(): nilを0に変換するように. * Enumerable:min, max, index, includes - 追加. min, maxは要素が `<=>'メソッドを持つことを仮定している. * Dict/Dbm:length - 要素数を返すメソッド. * Dbmクラスにto_aメソッドを追加. * Sunにおけるsortの誤動作の件, 昨日の修正でfixされた. しかし, それ でなぜ動かなかったのかは明らかではないが…. 比較関数がどんな値を 返しても指定した領域外をアクセスするのはバグではないか. * ファイルの全内容を読んで, 各行を配列として返すメソッドはpythonを 参考にして`readlines'という名前にした. それにともないgetsに対し てreadlineという別名を用意した. Fri Jun 3 00:08:38 1994 Yukihiro Matsumoto (matz@dyna) * Array:sort - 判別関数の戻り値はFixnumではなく, Intであるべきだっ た. 間違い. Sunで動作がおかしかったのはこのせいかも知れない. Thu Jun 2 11:48:37 1994 Yukihiro Matsumoto (matz@ix-02) * IO:read_all() - ストリームの最後まで入力して, 各行を要素とする配 列を返すメソッドを追加. また関数メソッド read_all()も追加した. これは引数のファイルから読み込んで各行を要素とする配列を返す. 意 味的には def read_all() ary = {} while gets() ary.push($_) end end とほぼ等価である. * String:atoiメソッドを削除. to_aメソッドからaが配列であるとの連想 を呼んで, 混乱を招かないため. 代わりにto_iメソッドを使うこと. * 配列への変換メソッドto_aを導入した. 通常のオブジェクトは自分自身 を唯一の要素とする長さ1の配列を返す. 配列は自分自身を, 辞書はキー と値のペアの配列を返す. Enumeratedをincludeしたクラスは, eachが 返す各要素を含む配列を返す. * file.c: 不定個の引数を受けとるメソッド(chmod,chown,utimes)を書き 換えて, 整理した. それに伴い, 最初に全ての引数の型チェックを行な うようにした. 型チェックに失敗すると処理を行なわずに例外を発生さ せる. * configure.in: 不必要なテストを行なわないように修正した. Tue May 31 10:41:08 1994 Yukihiro Matsumoto (matz@ix-02) * String:pack(): 2進数の文字列変換(B,b)で0と1が逆だった. * Math.c: 実数系のメソッドに引数として整数が渡された時に自動的に変 換するようにした. * toupper(), tolower(): 文字列の判定ミスで変換されていなかった. * getopt_long()の仕様によって, スクリプトへの引数がインタプリタの 引数だと解釈されていた. 引数パターン文字列の先頭に`+'を追加. * config.hを削除した. DEFINEはMakefileで与えられる. * sprintf(): "%d"に文字列が与えられた時にはアドレスではなく内容を 整数に変換するようにした. ついでに浮動小数点数も変換するように変 更した. * regexp.c: rubyの拡張正規表現(\d, \D, \s, \S)の処理で割り当てた領 域を越えてバッファに書き込んでいた. 処理前にバッファをきちんと拡 張するようにした. これで昨日問題にしていたメモリの問題は解決でき たと思う. * yylex(): ダブルクォート文字列中でダブルクォートを表現するため のバックスラッシュ表現ができなかった. Mon May 30 10:07:42 1994 Yukihiro Matsumoto (matz@ix-02) * 演算子`!'の右辺も条件式であるとした. これによって, この演算子を 再定義する人は混乱するかも知れないが, 大多数のこの演算子を使う人 は混乱を避けることができると思う. * autoconfを使って, 自動的にMakefile, config.hを生成するようにした. これで, 大抵のマシンでは`configure'を実行した後, `make'一発でコ ンパイルできると思う. * clone: サブクラスに対して用いられた場合, 元のオブジェクトと同じ クラスのインスタンスを返すように(以前はビルトインクラスの場合を 考えてなかった). * ビルトインクラスのサブクラスも作れるように, リテラルのあるクラス にもnewメソッドを追加した. * malloc()で落ちる. purifyが必要かも知れない. * re.c: rb_global_variable()の呼びだし形式の間違い. 変数へのポイン タを渡さなければいけない. * parse.y: ローカル変数の扱いに引数の評価順に依存する移植性のない 部分があった. * attr(): 属性設定のバグを直した. いつ内部仕様が変わったんだろう…? Sat May 28 23:08:18 1994 Yukihiro Matsumoto (matz@dyna) * 正規表現キャッシュの文字列一致判定をポインタ一致から内容一致に変 更した. そういえば文字列リテラルは一回毎に新しくオブジェクトが生 成されるのだった. Fri May 27 11:42:00 1994 Yukihiro Matsumoto (matz@ix-02) * 正規表現の文字コードのデフォルトを漢字非対応にした. これによって 若干の高速化が図れる. * trから文字削除(delete), 文字圧縮(squeeze)を分離した. それにとも ないtrのオプション引数はなくなった. Thu May 26 10:32:55 1994 Yukihiro Matsumoto (matz@ix-02) * スクリプト読み込みルーチンを書き直して, 通常ファイル以外のファイ ル名や空文字列がスクリプトとして与えられた場合に対応した. また, 標準入力からスクリプトを読み込む時に, 一時ファイルが/tmpに残らな いようにした. * Fixnum:id2name - IDから文字列に戻す関数. String:internの逆. * Array: 配列の範囲外の要素をアクセスした時に例外を発生させずに, nilを返すようにした. 配列は自動的に拡張される. * string:stripを追加. * -nオプションが-eオプションを複数指定した時も動作するように. * parse.yで<sys/types.h>もインクルードするようにした. * fname周りの細かいbugを修正. Wed May 26 11:45:10 1994 Yukihiro Matsumoto (matz@dyna) * 定数をキャッシュするようにした. 繰り返しが多い場合には有効のはず だが, 一度しかアクセスしない場合は遅くなるなあ. Wed May 25 00:42:24 1994 Yukihiro Matsumoto (matz@dyna) * 多重代入文(foo, bar = 1, 2)の採用. * 条件式部に文字列あるいは正規表現リテラルをおくと`=~'演算子によっ て`$_'と比較される. 更に`...'の両辺では整数リテラルが`$.'と比較 される. Mon May 23 23:27:03 1994 Yukihiro Matsumoto (matz@dyna) * &式 形式はなくなった. 代わりにkernel:apply(id, args..)を導入. * def op () ..形式の導入. opは再定義可能な演算子. * constantの代入時チェック. 既に初期化されている定数に代入した場合 は例外が発生する. * 多重代入文. Thu May 19 22:57:07 1994 Yukihiro Matsumoto (matz@dyna) * 複合文でもvoid valueのチェックを行うようにした. * untilの動作の修正(do..until型だった). Wed May 18 01:06:25 1994 Yukihiro Matsumoto (matz@dyna) * 移植に関する若干の問題を修正. * 別名の構文を「def a b」にした. * until/unless: 演算子から制御文へ. 例外を捕捉する機能はそのまま. * 選択可能な機能をconfig.hからdefines.hに移動. Fri May 13 23:20:21 1994 Yukihiro Matsumoto (matz@dyna) * -yオプションを新設. -dオプションからコンパイラのデバッグ部分を分 離した. Tue Apr 25 20:17:33 1994 Yukihiro Matsumoto (matz@dyna) * マルチバイト文字列を識別子に使えるように. 個人的には使いたくは無 いけどなあ. * `-v'フラグの状態を$verboseでアクセスできるように. * CVSの導入に伴い, バージョン管理の方法を変更. * 真面目にChangeLogをつける事にした. Tue Mar 8 10:09:25 1994 Yukihiro Matsumoto (matz at nws119) * %変数名 によるクラス定数を導入. * undef メソッド によるメソッド定義の取り消しを導入. * rb_get_method_bodyではthe_envを変更せず, rb_call()で明示的に変更 するように. これでresponds_toなどで環境が破壊されない. Mon Mar 7 17:46:15 1994 Yukihiro Matsumoto (matz at nws119) * 「&文字列」形式. 「式.文字列」型のメッセージセンドはなくなった. * 自己代入形式(+=. -=, ...) * obj.attr = expr形式の採用. Thu Feb 24 16:23:28 1994 Yukihiro Matsumoto (matz at nws119) * toint, tofloat, print_stringをそれぞれto_i, to_f, to_sに変更. * String:clone - Copy on Writeの実現. Tue Feb 22 11:11:44 1994 Yukihiro Matsumoto (matz at nws119) * re.c: マッチした文字列の保存に失敗していた. * trap: 可能ならば処理に時間のかかるシステムコール(read, wait, sigpause, select)をフックして割り込み処理の即答性を高める(DOSな どでは無理だなあ). * trap: 割り込みをその場で処理するか(迅速だが危険), 安全なタイミン グで処理するかを選択できるように. Tue Feb 17 11:11:12 1994 Yukihiro Matsumoto (matz at nws119) * trap: 割り込みハンドラ. Wed Feb 16 12:29:12 1994 Yukihiro Matsumoto (matz at nws119) * String:crypt: 暗号化ルーチン * "::"演算子の追加. a::b は {a, b}と同義. a::b::c は {a, {b, c}}と 同義(右結合). 同義とはいうものの, "::"演算子を使った方が少しだけ メモリ効率が良い. * Dir.rmdir(), File.unlink(), File.utime() -- 各システムコールへの インタフェース. * kill -- kill(2) I/F * select(): readのチェックではstdioにバッファリングされているかど うかをチェックするように. Tue Feb 15 15:08:31 1994 Yukihiro Matsumoto (matz at nws119) * file.c: statをキャッシュするように. * File:utime()を追加. * unliteralize(): フラグを破壊していた. * Bug(): coreを吐くように. * String:tr -- tr(1)互換. 引数パターンがちょっと違うけど…. Mon Feb 14 18:24:13 1994 Yukihiro Matsumoto (matz at nws119) * unless, untilが例外も偽と見なすように. * select() -- select(2) I/F * Array:pack, String:unpack: perlのpack/unpackの同等品 Tue Feb 8 17:11:10 1994 Yukihiro Matsumoto (matz at nws119) * setenv()のないシステムのためにputenv()を使ったコードも用意した. Mon Feb 7 09:52:44 1994 Yukihiro Matsumoto (matz at nws119) * 引数の一番最後に`*'を置けるようにした. これでrest引数のリストを 操作する必要が少なくなる. Fri Feb 4 18:23:26 1994 Yukihiro Matsumoto (matz at nws119) * ruby-mode.elを書き直す. ずいぶんましになったと思う. * 文字列リテラルのCopy on Writeを実現. これで文字列がリテラルであ るからといっていちいちcloneしなくても済む. Tue Feb 1 09:21:09 1994 Yukihiro Matsumoto (matz at nws119) * sub(), gsub()で, マッチした文字列を$&, $1..$9でアクセスできるよ うにした. 同時にマッチした部分文字列をコピーしておくように(元の 文字列が変更されても状態を保存するため). Mon Jan 31 15:16:58 1994 Yukihiro Matsumoto (matz at nws119) * プライベートメソッドの仕様を変更. 今までは同じクラスのメソッドか らしかアクセスできなかったが, サブクラスのメソッドからもアクセス できるようにした(C++におけるprotected メンバ関数). * メソッドサーチのアルゴリズムを改善し, 10%程度の高速化を行なった. * 高速化. Cで記述されたメソッドを呼び出す時にはsetjmpを呼ばないよ うにした. これでCメソッドを多用する場合には3倍程度高速になった. Fri Jan 28 15:44:04 1994 Yukihiro Matsumoto (matz at nws119) * sh-modeを元にruby-mode.elを作る. 演算子で終る, 2行に渡る文には対 応していないけど…. Thu Jan 27 11:35:19 1994 Yukihiro Matsumoto (matz at nws119) * freenode(): NODE_NILの解放忘れ. * 字句解析部のバグ修正(コメントの後の状態を戻し忘れ). * protect .. endのバグ修正. GC_LINKのネストが不正だった. * joinのバグ修正(使っているオブジェクトをfreeしていた). * splitのバグ修正(アルゴリズムがおかしかった). * fork()を追加. Wed Jan 26 17:09:56 1994 Yukihiro Matsumoto (matz at nws119) * ファイルテストメソッドの追加. * rb_autoexec(): クラスを初めてアクセスした時の挙動を制御できるよ うにした. これでautoloadも実現できる. これにともないメソッド unknownはなくなった. Tue Jan 25 15:51:36 1994 Yukihiro Matsumoto (matz at nws119) * Dbmクラス, Mathモジュールを作成. * -Iオプションでサーチパスに追加できるように. * サーチパスを変数$load_pathに設定できるように. * load(): ダイナミックロードを使えるようにした. Tue Jan 18 14:14:01 1994 Yukihiro Matsumoto (matz at nws119) * Comparable:"<=>" * Float,Fixnum:"**" * Array:sort Fri Jan 14 16:53:37 1994 Yukihiro Matsumoto (matz at nws119) * version 0.07 * メソッドに関するドキュメントを充実させた. * String:index(): 引数positionを増やした. Thu Jan 13 15:13:52 1994 Yukihiro Matsumoto (matz at nws119) * 未初期化の変数アクセスをなくした. * 無駄なhash tableのアロケーションを削除. * Purify'd(on Sun) * ~RE と ~STRのコンパイル時展開の抑制. * Sunへ移植. signal()の戻り値. RDataのbug修正. * parse.y: nlsルールを削除. * yylex(): 改行と符合の解析部分を変更. * missing/strftime.c: 移植用. * Time:strftime: その他のメソッドもstrftimeを利用するように. * メソッド再定義時にメソッドキャッシュをクリアする. Fri Jan 7 15:23:20 1994 Yukihiro Matsumoto (matz at nws119) * Float:coerce(): FixnumとFloat以外の引数を与えられるた時には例外 を発生するように. * Stat: stat構造体の全てのメンバに対するアクセスメソッドを用意. * 未定義のクラス/モジュールへの参照がunknownメソッドを呼び出すよう にした. * baseline - version 0.06.
Diffstat (limited to 'parse.y')
-rw-r--r--parse.y2585
1 files changed, 2585 insertions, 0 deletions
diff --git a/parse.y b/parse.y
new file mode 100644
index 0000000..0f4f640
--- /dev/null
+++ b/parse.y
@@ -0,0 +1,2585 @@
+/************************************************
+
+ parse.y -
+
+ $Author: matz $
+ $Date: 1994/06/27 15:48:34 $
+ created at: Fri May 28 18:02:42 JST 1993
+
+ Copyright (C) 1994 Yukihiro Matsumoto
+
+************************************************/
+
+%{
+
+#define YYDEBUG 1
+#include "ruby.h"
+#include "env.h"
+#include "node.h"
+#include "st.h"
+
+#include "ident.h"
+#define is_id_nonop(id) ((id)>LAST_TOKEN)
+#define is_local_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_LOCAL)
+#define is_global_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_GLOBAL)
+#define is_instance_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_INSTANCE)
+#define is_variable_id(id) (is_id_nonop(id)&&((id)&ID_VARMASK))
+#define is_attrset_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_ATTRSET)
+#define is_const_id(id) (is_id_nonop(id)&&((id)&ID_SCOPE_MASK)==ID_CONST)
+
+struct op_tbl {
+ ID tok;
+ char *name;
+};
+
+NODE *eval_tree = Qnil;
+static int in_regexp;
+
+enum {
+ KEEP_STATE = 0, /* don't change lex_state. */
+ EXPR_BEG, /* ignore newline, +/- is a sign. */
+ EXPR_MID, /* newline significant, +/- is a sign. */
+ EXPR_END, /* +/- is a operator. newline significant */
+};
+
+static int lex_state;
+
+static ID cur_class = Qnil, cur_mid = Qnil;
+static int in_module, in_single;
+
+static void value_expr();
+static NODE *cond();
+static NODE *cond2();
+
+static NODE *block_append();
+static NODE *list_append();
+static NODE *list_concat();
+static NODE *list_copy();
+static NODE *call_op();
+
+static NODE *asignable();
+static NODE *aryset();
+static NODE *attrset();
+
+static void push_local();
+static void pop_local();
+static int local_cnt();
+static int local_id();
+static ID *local_tbl();
+
+struct global_entry* rb_global_entry();
+
+static void init_top_local();
+static void setup_top_local();
+%}
+
+%union {
+ struct node *node;
+ VALUE val;
+ ID id;
+}
+
+%token CLASS
+ MODULE
+ DEF
+ FUNC
+ UNDEF
+ INCLUDE
+ IF
+ THEN
+ ELSIF
+ ELSE
+ CASE
+ WHEN
+ UNLESS
+ UNTIL
+ WHILE
+ FOR
+ IN
+ DO
+ USING
+ PROTECT
+ RESQUE
+ ENSURE
+ END
+ REDO
+ BREAK
+ CONTINUE
+ RETURN
+ YIELD
+ SUPER
+ RETRY
+ SELF
+ NIL
+
+%token <id> IDENTIFIER GVAR IVAR CONSTANT
+%token <val> INTEGER FLOAT STRING REGEXP
+
+%type <node> singleton inc_list
+%type <val> literal numeric
+%type <node> compexpr exprs expr expr2 primary var_ref
+%type <node> if_tail opt_else cases resque ensure opt_using
+%type <node> call_args opt_args args f_arglist f_args f_arg
+%type <node> assoc_list assocs assoc
+%type <node> mlhs mlhs_head mlhs_tail lhs
+%type <id> superclass variable symbol
+%type <id> fname fname0 op rest_arg end_mark
+
+%token UPLUS /* unary+ */
+%token UMINUS /* unary- */
+%token POW /* ** */
+%token CMP /* <=> */
+%token EQ /* == */
+%token NEQ /* != <> */
+%token GEQ /* >= */
+%token LEQ /* <= */
+%token AND OR /* && and || */
+%token MATCH NMATCH /* =~ and !~ */
+%token DOT2 DOT3 /* .. and ... */
+%token AREF ASET /* [] and []= */
+%token LSHFT RSHFT /* << and >> */
+%token COLON2 /* :: */
+%token <id> SELF_ASGN /* +=, -= etc. */
+%token ASSOC /* => */
+
+/*
+ * precedence table
+ */
+
+%left YIELD RETURN
+%right '=' SELF_ASGN
+%right COLON2
+%nonassoc DOT2 DOT3
+%left OR
+%left AND
+%left '|' '^'
+%left '&'
+%nonassoc CMP EQ NEQ MATCH NMATCH
+%left '>' GEQ '<' LEQ
+%left LSHFT RSHFT
+%left '+' '-'
+%left '*' '/' '%'
+%right POW
+%right '!' '~' UPLUS UMINUS
+
+%token LAST_TOKEN
+
+%%
+program : {
+ lex_state = EXPR_BEG;
+ init_top_local();
+ }
+ compexpr
+ {
+ eval_tree = block_append(eval_tree, $2);
+ setup_top_local();
+ }
+
+compexpr : exprs opt_term
+
+exprs : /* none */
+ {
+ $$ = Qnil;
+ }
+ | expr
+ | exprs term expr
+ {
+ $$ = block_append($1, $3);
+ }
+ | exprs error expr
+ {
+ yyerrok;
+ $$ = $1;
+ }
+
+expr : CLASS IDENTIFIER superclass
+ {
+ if (cur_class || cur_mid || in_single)
+ Error("nested class definition");
+ cur_class = $2;
+ push_local();
+ }
+ compexpr
+ END end_mark
+ {
+ if ($7 && $7 != CLASS) {
+ Error("unmatched end keyword(expected `class')");
+ }
+ $$ = NEW_CLASS($2, $5, $3);
+ pop_local();
+ cur_class = Qnil;
+ }
+ | MODULE IDENTIFIER
+ {
+ if (cur_class != Qnil)
+ Error("nested module definition");
+ cur_class = $2;
+ in_module = 1;
+ push_local();
+ }
+ compexpr
+ END end_mark
+ {
+ if ($6 && $6 != MODULE) {
+ Error("unmatched end keyword(expected `module')");
+ }
+ $$ = NEW_MODULE($2, $4);
+ pop_local();
+ cur_class = Qnil;
+ in_module = 0;
+ }
+ | DEF fname
+ {
+ if (cur_mid || in_single)
+ Error("nested method definition");
+ cur_mid = $2;
+ push_local();
+ }
+ f_arglist compexpr
+ END end_mark
+ {
+ 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);
+ pop_local();
+ cur_mid = Qnil;
+ }
+ | DEF singleton '.' fname
+ {
+ value_expr($2);
+ in_single++;
+ push_local();
+ }
+ f_arglist
+ compexpr
+ END end_mark
+ {
+ if ($9 && $9 != DEF) {
+ Error("unmatched end keyword(expected `def')");
+ }
+ $$ = NEW_DEFS($2, $4, NEW_RFUNC($6, $7));
+ pop_local();
+ in_single--;
+ }
+ | UNDEF fname
+ {
+ $$ = NEW_UNDEF($2);
+ }
+ | DEF fname fname
+ {
+ $$ = NEW_ALIAS($2, $3);
+ }
+ | INCLUDE inc_list
+ {
+ if (cur_mid || in_single)
+ Error("include appeared in method definition");
+ $$ = $2;
+ }
+ | mlhs '=' args
+ {
+ NODE *rhs;
+
+ if ($3->nd_next == Qnil) {
+ rhs = $3->nd_head;
+ free($3);
+ }
+ else {
+ rhs = $3;
+ }
+
+ $$ = NEW_MASGN($1, rhs);
+ }
+ | expr2
+
+
+mlhs : mlhs_head
+ | mlhs_head mlhs_tail
+ {
+ $$ = list_concat($1, $2);
+ }
+
+mlhs_head : variable comma
+ {
+ $$ = NEW_LIST(asignable($1, Qnil));
+ }
+ | primary '[' args rbracket comma
+ {
+ $$ = NEW_LIST(aryset($1, $3, Qnil));
+ }
+ | primary '.' IDENTIFIER comma
+ {
+ $$ = NEW_LIST(attrset($1, $3, Qnil));
+ }
+
+mlhs_tail : lhs
+ {
+ $$ = NEW_LIST($1);
+ }
+ | mlhs_tail comma lhs
+ {
+ $$ = list_append($1, $3);
+ }
+
+lhs : variable
+ {
+ $$ = asignable($1, Qnil);
+ }
+ | primary '[' args rbracket
+ {
+ $$ = aryset($1, $3, Qnil);
+ }
+ | primary '.' IDENTIFIER
+ {
+ $$ = attrset($1, $3, Qnil);
+ }
+
+superclass : /* none */
+ {
+ $$ = Qnil;
+ }
+ | ':' IDENTIFIER
+ {
+ $$ = $2;
+ }
+
+inc_list : IDENTIFIER
+ {
+ $$ = NEW_INC($1);
+ }
+ | inc_list comma IDENTIFIER
+ {
+ $$ = block_append($1, NEW_INC($3));
+ }
+ | error
+ {
+ $$ = Qnil;
+ }
+ | inc_list comma error
+
+fname : fname0
+ | IVAR
+
+fname0 : IDENTIFIER
+ | IDENTIFIER '='
+ {
+ ID id = $1;
+
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+ $$ = id;
+ }
+ | op
+
+op : COLON2 { $$ = COLON2; }
+ | DOT2 { $$ = DOT2; }
+ | '|' { $$ = '|'; }
+ | '^' { $$ = '^'; }
+ | '&' { $$ = '&'; }
+ | CMP { $$ = CMP; }
+ | EQ { $$ = EQ; }
+ | NEQ { $$ = NEQ; }
+ | MATCH { $$ = MATCH; }
+ | NMATCH { $$ = NMATCH; }
+ | '>' { $$ = '>'; }
+ | GEQ { $$ = GEQ; }
+ | '<' { $$ = '<'; }
+ | LEQ { $$ = LEQ; }
+ | LSHFT { $$ = LSHFT; }
+ | RSHFT { $$ = RSHFT; }
+ | '+' { $$ = '+'; }
+ | '-' { $$ = '-'; }
+ | '*' { $$ = '*'; }
+ | '/' { $$ = '/'; }
+ | '%' { $$ = '%'; }
+ | POW { $$ = POW; }
+ | '!' { $$ = '!'; }
+ | '~' { $$ = '~'; }
+ | '!' '@' { $$ = '!'; }
+ | '~' '@' { $$ = '~'; }
+ | '-' '@' { $$ = UMINUS; }
+ | '+' '@' { $$ = UPLUS; }
+ | '[' ']' { $$ = AREF; }
+ | '[' ']' '=' { $$ = ASET; }
+
+f_arglist : '(' f_args rparen
+ {
+ $$ = $2;
+ }
+ | term
+ {
+ $$ = Qnil;
+ }
+
+f_args : /* no arg */
+ {
+ $$ = Qnil;
+ }
+ | f_arg
+ {
+ $$ = NEW_ARGS($1, -1);
+ }
+ | f_arg comma rest_arg
+ {
+ $$ = NEW_ARGS($1, $3);
+ }
+ | rest_arg
+ {
+ $$ = NEW_ARGS(Qnil, $1);
+ }
+ | f_arg error
+ {
+ $$ = NEW_ARGS($1, -1);
+ }
+ | error
+ {
+ $$ = Qnil;
+ }
+
+f_arg : IDENTIFIER
+ {
+ if (!is_local_id($1))
+ Error("formal argument must be local variable");
+ $$ = NEW_LIST(local_cnt($1));
+ }
+ | f_arg comma IDENTIFIER
+ {
+ if (!is_local_id($3))
+ Error("formal argument must be local variable");
+ $$ = list_append($1, local_cnt($3));
+ }
+
+rest_arg : '*' IDENTIFIER
+ {
+ if (!is_local_id($2))
+ Error("rest argument must be local variable");
+ $$ = local_cnt($2);
+ }
+
+singleton : var_ref
+ {
+ if ($1->type == NODE_SELF) {
+ $$ = NEW_SELF();
+ }
+ else if ($1->type == NODE_NIL) {
+ Error("Can't define single method for nil.");
+ freenode($1);
+ $$ = Qnil;
+ }
+ else {
+ $$ = $1;
+ }
+ }
+ | '(' compexpr rparen
+ {
+ switch ($2->type) {
+ case NODE_STR:
+ case NODE_LIT:
+ case NODE_ARRAY:
+ case NODE_ZARRAY:
+ Error("Can't define single method for literals.");
+ default:
+ break;
+ }
+ $$ = $2;
+ }
+
+expr2 : IF expr2 then
+ compexpr
+ if_tail
+ END end_mark
+ {
+ if ($7 && $7 != IF) {
+ Error("unmatched end keyword(expected `if')");
+ }
+ $$ = NEW_IF(cond($2), $4, $5);
+ }
+ | UNLESS expr2 then
+ compexpr opt_else END end_mark
+ {
+ if ($7 && $7 != UNLESS) {
+ Error("unmatched end keyword(expected `if')");
+ }
+ $$ = NEW_UNLESS(cond($2), $4, $5);
+ }
+ | CASE expr2 opt_term
+ cases
+ END end_mark
+ {
+ if ($6 && $6 != CASE) {
+ Error("unmatched end keyword(expected `case')");
+ }
+ value_expr($2);
+ $$ = NEW_CASE($2, $4);
+ }
+ | WHILE expr2 term compexpr END end_mark
+ {
+ if ($6 && $6 != WHILE) {
+ Error("unmatched end keyword(expected `while')");
+ }
+ $$ = NEW_WHILE(cond($2), $4);
+ }
+ | UNTIL expr2 term compexpr END end_mark
+ {
+ if ($6 && $6 != UNTIL) {
+ Error("unmatched end keyword(expected `until')");
+ }
+ $$ = NEW_UNTIL(cond($2), $4);
+ }
+ | FOR lhs IN expr2 term
+ compexpr
+ END end_mark
+ {
+ if ($8 && $8 != FOR) {
+ Error("unmatched end keyword(expected `for')");
+ }
+ value_expr($4);
+ $$ = NEW_FOR($2, $4, $6);
+ }
+ | DO expr2 opt_using
+ compexpr
+ END end_mark
+ {
+ if ($6 && $6 != DO) {
+ Error("unmatched end keyword(expected `do')");
+ }
+ value_expr($2);
+ $$ = NEW_DO($3, $2, $4);
+ }
+ | PROTECT
+ compexpr
+ resque
+ ensure
+ END end_mark
+ {
+ if ($6 && $6 != PROTECT) {
+ Error("unmatched end keyword(expected `protect')");
+ }
+ if ($3 == Qnil && $4 == Qnil) {
+ Warning("useless protect clause");
+ $$ = $2;
+ }
+ else {
+ $$ = NEW_PROT($2, $3, $4);
+ }
+ }
+ | REDO
+ {
+ $$ = NEW_REDO();
+ }
+ | BREAK
+ {
+ $$ = NEW_BREAK();
+ }
+ | CONTINUE
+ {
+ $$ = NEW_CONT();
+ }
+ | RETRY
+ {
+ $$ = NEW_RETRY();
+ }
+ | RETURN expr2
+ {
+ value_expr($2);
+ if (!cur_mid && !in_single)
+ Error("return appeared outside of method");
+ $$ = NEW_RET($2);
+ }
+ | RETURN
+ {
+ if (!cur_mid && !in_single)
+ Error("return appeared outside of method");
+ $$ = NEW_RET(Qnil);
+ }
+ | variable '=' expr2
+ {
+ value_expr($3);
+ $$ = asignable($1, $3);
+ }
+ | primary '[' args rbracket '=' expr2
+ {
+ value_expr($6);
+ $$ = aryset($1, $3, $6);
+ }
+ | primary '.' IDENTIFIER '=' expr2
+ {
+ value_expr($5);
+ $$ = attrset($1, $3, $5);
+ }
+ | variable SELF_ASGN expr2
+ {
+ NODE *val;
+
+ value_expr($3);
+ if (is_local_id($1)) {
+ val = NEW_LVAR($1);
+ }
+ else if (is_global_id($1)) {
+ val = NEW_GVAR($1);
+ }
+ else if (is_instance_id($1)) {
+ val = NEW_IVAR($1);
+ }
+ else {
+ val = NEW_CVAR($1);
+ }
+ $$ = asignable($1, call_op(val, $2, 1, $3));
+ }
+ | primary '[' args rbracket SELF_ASGN expr2
+ {
+ NODE *rval, *args;
+ value_expr($1);
+ value_expr($6);
+
+ args = list_copy($3);
+ rval = NEW_CALL2($1, AREF, args);
+
+ args = list_append($3, call_op(rval, $5, 1, $6));
+ $$ = NEW_CALL($1, ASET, args);
+ }
+ | primary '.' IDENTIFIER SELF_ASGN expr2
+ {
+ ID id = $3;
+ NODE *rval;
+
+ value_expr($1);
+ value_expr($5);
+
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+
+ rval = call_op(NEW_CALL2($1, $3, Qnil), $4, 1, $5);
+ $$ = NEW_CALL($1, id, NEW_LIST(rval));
+ }
+ | YIELD expr2
+ {
+ value_expr($2);
+ $$ = NEW_YIELD($2);
+ }
+ | expr2 DOT2 expr2
+ {
+ $$ = call_op($1, DOT2, 1, $3);
+ }
+ | expr2 DOT3 expr2
+ {
+ $$ = NEW_DOT3(cond2($1), cond2($3));
+ }
+ | expr2 '+' expr2
+ {
+ $$ = call_op($1, '+', 1, $3);
+ }
+ | expr2 '-' expr2
+ {
+ $$ = call_op($1, '-', 1, $3);
+ }
+ | expr2 '*' expr2
+ {
+ $$ = call_op($1, '*', 1, $3);
+ }
+ | expr2 '/' expr2
+ {
+ $$ = call_op($1, '/', 1, $3);
+ }
+ | expr2 '%' expr2
+ {
+ $$ = call_op($1, '%', 1, $3);
+ }
+ | expr2 POW expr2
+ {
+ $$ = call_op($1, POW, 1, $3);
+ }
+ | '+' expr2 %prec UPLUS
+ {
+ $$ = call_op($2, UPLUS, 0);
+
+ }
+ | '-' expr2 %prec UMINUS
+ {
+ $$ = call_op($2, UMINUS, 0);
+ }
+ | expr2 '|' expr2
+ {
+ $$ = call_op($1, '|', 1, $3);
+ }
+ | expr2 '^' expr2
+ {
+ $$ = call_op($1, '^', 1, $3);
+ }
+ | expr2 '&' expr2
+ {
+ $$ = call_op($1, '&', 1, $3);
+ }
+ | expr2 CMP expr2
+ {
+ $$ = call_op($1, CMP, 1, $3);
+ }
+ | expr2 '>' expr2
+ {
+ $$ = call_op($1, '>', 1, $3);
+ }
+ | expr2 GEQ expr2
+ {
+ $$ = call_op($1, GEQ, 1, $3);
+ }
+ | expr2 '<' expr2
+ {
+ $$ = call_op($1, '<', 1, $3);
+ }
+ | expr2 LEQ expr2
+ {
+ $$ = call_op($1, LEQ, 1, $3);
+ }
+ | expr2 EQ expr2
+ {
+ $$ = call_op($1, EQ, 1, $3);
+ }
+ | expr2 NEQ expr2
+ {
+ $$ = call_op($1, NEQ, 1, $3);
+ }
+ | expr2 MATCH expr2
+ {
+ $$ = call_op($1, MATCH, 1, $3);
+ }
+ | expr2 NMATCH expr2
+ {
+ $$ = call_op($1, NMATCH, 1, $3);
+ }
+ | '!' expr2
+ {
+ $$ = call_op(cond($2), '!', 0);
+ }
+ | '~' expr2
+ {
+ $$ = call_op($2, '~', 0);
+ }
+ | expr2 LSHFT expr2
+ {
+ $$ = call_op($1, LSHFT, 1, $3);
+ }
+ | expr2 RSHFT expr2
+ {
+ $$ = call_op($1, RSHFT, 1, $3);
+ }
+ | expr2 COLON2 expr2
+ {
+ $$ = call_op($1, COLON2, 1, $3);
+ }
+ | expr2 AND expr2
+ {
+ $$ = NEW_AND(cond($1), cond($3));
+ }
+ | expr2 OR expr2
+ {
+ $$ = NEW_OR(cond($1), cond($3));
+ }
+ |primary
+ {
+ $$ = $1;
+ }
+
+then : term
+ | THEN
+ | term THEN
+
+if_tail : opt_else
+ | ELSIF expr2 then
+ compexpr
+ if_tail
+ {
+ $$ = NEW_IF(cond($2), $4, $5);
+ }
+
+opt_else : /* none */
+ {
+ $$ = Qnil;
+ }
+ | ELSE compexpr
+ {
+ $$ = $2;
+ }
+
+opt_using : term
+ {
+ $$ = Qnil;
+ }
+ | opt_term USING lhs term
+ {
+ $$ = $3;
+ }
+
+cases : opt_else
+ | WHEN args term
+ compexpr
+ cases
+ {
+ $$ = NEW_WHEN($2, $4, $5);
+ }
+
+resque : /* none */
+ {
+ $$ = Qnil;
+ }
+ | RESQUE compexpr
+ {
+ if ($2 == Qnil)
+ $$ = (NODE*)1;
+ else
+ $$ = $2;
+ }
+
+ensure : /* none */
+ {
+ $$ = Qnil;
+ }
+ | ENSURE compexpr
+ {
+ $$ = $2;
+ }
+
+call_args : /* none */
+ {
+ $$ = Qnil;
+ }
+ | args
+ | '*' exprs
+ {
+ $$ = $2;
+ }
+ | args comma '*' exprs
+ {
+ $$ = call_op($1, '+', 1, $4);
+ }
+
+opt_args : /* none */
+ {
+ $$ = Qnil;
+ }
+ | args
+
+args : expr2
+ {
+ value_expr($1);
+ $$ = NEW_LIST($1);
+ }
+ | args comma expr2
+ {
+ value_expr($3);
+ $$ = list_append($1, $3);
+ }
+
+primary : var_ref
+ | '(' compexpr rparen
+ {
+ $$ = $2;
+ }
+
+ | STRING
+ {
+ literalize($1);
+ $$ = NEW_STR($1);
+ }
+ | primary '[' args rbracket
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, AREF, $3);
+ }
+ | literal
+ {
+ literalize($1);
+ $$ = NEW_LIT($1);
+ }
+ | '[' opt_args rbracket
+ {
+ if ($2 == Qnil)
+ $$ = NEW_ZARRAY(); /* zero length array*/
+ else {
+ $$ = $2;
+ }
+ }
+ | lbrace assoc_list rbrace
+ {
+ $$ = NEW_HASH($2);
+ }
+ | primary '.' IDENTIFIER '(' call_args rparen
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, $5);
+ }
+ | primary '.' IDENTIFIER
+ {
+ value_expr($1);
+ $$ = NEW_CALL($1, $3, Qnil);
+ }
+ | IDENTIFIER '(' call_args rparen
+ {
+ $$ = NEW_CALL(Qnil, $1, $3);
+ }
+ | IVAR '(' call_args rparen
+ {
+ $$ = NEW_CALL(Qnil, $1, $3);
+ }
+ | SUPER '(' call_args rparen
+ {
+ if (!cur_mid && !in_single)
+ Error("super called outside of method");
+ $$ = NEW_SUPER($3);
+ }
+ | SUPER
+ {
+ if (!cur_mid && !in_single)
+ Error("super called outside of method");
+ $$ = NEW_ZSUPER();
+ }
+
+literal : numeric
+ | '\\' symbol
+ {
+ $$ = INT2FIX($2);
+ }
+ | '/' {in_regexp = 1;} REGEXP
+ {
+ $$ = $3;
+ }
+
+symbol : fname0
+ | IVAR
+ | GVAR
+ | CONSTANT
+
+numeric : INTEGER
+ | FLOAT
+
+variable : IDENTIFIER
+ | IVAR
+ | GVAR
+ | CONSTANT
+ | NIL
+ {
+ $$ = NIL;
+ }
+ | SELF
+ {
+ $$ = SELF;
+ }
+
+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);
+ }
+ }
+
+assoc_list : /* none */
+ {
+ $$ = Qnil;
+ }
+ | assocs
+
+assocs : assoc
+ | assocs comma assoc
+ {
+ $$ = list_concat($1, $3);
+ }
+
+assoc : expr2 ASSOC expr2
+ {
+ $$ = NEW_LIST($1);
+ $$ = list_append($$, $3);
+ }
+
+end_mark : CLASS { $$ = CLASS; }
+ | MODULE { $$ = MODULE; }
+ | DEF { $$ = DEF; }
+ | FUNC { $$ = FUNC; }
+ | IF { $$ = IF; }
+ | UNLESS { $$ = UNLESS; }
+ | CASE { $$ = CASE; }
+ | WHILE { $$ = WHILE; }
+ | UNTIL { $$ = UNTIL; }
+ | FOR { $$ = FOR; }
+ | DO { $$ = DO; }
+ | PROTECT { $$ = PROTECT; }
+ | { $$ = Qnil;}
+
+opt_term : /* none */
+ | term
+
+term : sc
+ | nl
+
+sc : ';' { yyerrok; }
+nl : '\n' { yyerrok; }
+
+rparen : ')' { yyerrok; }
+rbracket : ']' { yyerrok; }
+lbrace : '{' { yyerrok; }
+rbrace : '}' { yyerrok; }
+comma : ',' { yyerrok; }
+%%
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "regex.h"
+
+#define is_identchar(c) ((c)!=-1&&(isalnum(c) || (c) == '_' || ismbchar(c)))
+
+static char *tokenbuf = NULL;
+static int tokidx, toksiz = 0;
+
+char *xmalloc();
+char *xrealloc();
+VALUE newregexp();
+VALUE newstring();
+VALUE newfloat();
+VALUE newinteger();
+char *strdup();
+
+#define EXPAND_B 1
+#define LEAVE_BS 2
+
+static void read_escape();
+
+char *sourcefile; /* current source file */
+int sourceline; /* current line no. */
+
+static char *lex_p;
+static int lex_len;
+
+lex_setsrc(src, ptr, len)
+ char *src;
+ char *ptr;
+ int len;
+{
+ sourcefile = (char*)strdup(src);
+
+ sourceline = 1;
+ lex_p = ptr;
+ lex_len = len;
+}
+
+#define nextc() ((--lex_len>=0)?(*lex_p++):-1)
+#define pushback() (lex_len++, lex_p--)
+
+#define tokfix() (tokenbuf[tokidx]='\0')
+#define tok() tokenbuf
+#define toklen() tokidx
+#define toknow() &toknbuf[tokidx]
+
+char *
+newtok()
+{
+ tokidx = 0;
+ if (!tokenbuf) {
+ toksiz = 60;
+ tokenbuf = ALLOC_N(char, 60);
+ }
+ if (toksiz > 1024) {
+ REALLOC_N(tokenbuf, char, 60);
+ }
+ return tokenbuf;
+}
+
+void
+tokadd(c)
+ char c;
+{
+ if (tokidx >= toksiz) {
+ toksiz *= 2;
+ REALLOC_N(tokenbuf, char, toksiz);
+ }
+ tokenbuf[tokidx++] = c;
+}
+
+#define LAST(v) ((v)-1 + sizeof(v)/sizeof(v[0]))
+
+static struct kwtable {
+ char *name;
+ int id;
+ int state;
+} kwtable [] = {
+ "__END__", 0, KEEP_STATE,
+ "break", BREAK, EXPR_END,
+ "case", CASE, KEEP_STATE,
+ "class", CLASS, KEEP_STATE,
+ "continue", CONTINUE, EXPR_END,
+ "def", DEF, KEEP_STATE,
+ "do", DO, KEEP_STATE,
+ "else", ELSE, EXPR_BEG,
+ "elsif", ELSIF, EXPR_BEG,
+ "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,
+ "module", MODULE, KEEP_STATE,
+ "nil", NIL, EXPR_END,
+ "protect", PROTECT, KEEP_STATE,
+ "redo", REDO, EXPR_END,
+ "resque", RESQUE, EXPR_BEG,
+ "retry", RETRY, EXPR_END,
+ "return", RETURN, EXPR_MID,
+ "self", SELF, EXPR_END,
+ "super", SUPER, EXPR_END,
+ "then", THEN, EXPR_BEG,
+ "undef", UNDEF, EXPR_BEG,
+ "unless", UNLESS, EXPR_BEG,
+ "until", UNTIL, EXPR_BEG,
+ "using", USING, KEEP_STATE,
+ "when", WHEN, EXPR_BEG,
+ "while", WHILE, KEEP_STATE,
+ "yield", YIELD, EXPR_BEG,
+};
+
+yylex()
+{
+ register int c;
+ struct kwtable *low = kwtable, *mid, *high = LAST(kwtable);
+ int last;
+
+ if (in_regexp) {
+ int in_brack = 0;
+ int re_start = sourceline;
+
+ in_regexp = 0;
+ newtok();
+ while (c = nextc()) {
+ switch (c) {
+ case '[':
+ in_brack = 1;
+ break;
+ case ']':
+ in_brack = 0;
+ break;
+ case '\\':
+ if ((c = nextc()) == -1) {
+ sourceline = re_start;
+ Error("unterminated regexp meets end of file");
+ return 0;
+ }
+ else if (c == '\n') {
+ sourceline++;
+ }
+ else if (in_brack && c == 'b') {
+ tokadd('\b');
+ }
+ else {
+ pushback();
+ read_escape(LEAVE_BS);
+ }
+ continue;
+
+ case '/': /* end of the regexp */
+ if (in_brack)
+ break;
+
+ tokfix();
+ yylval.val = regexp_new(tok(), toklen());
+ lex_state = EXPR_END;
+ return REGEXP;
+
+ case -1:
+ Error("unterminated regexp");
+ return 0;
+
+ default:
+ if (ismbchar(c)) {
+ tokadd(c);
+ c = nextc();
+ }
+ break;
+ }
+ tokadd(c);
+ }
+ }
+
+retry:
+ switch (c = nextc()) {
+ case '\0':
+ case '\004':
+ case '\032':
+ case -1: /* end of script. */
+ return 0;
+
+ /* white spaces */
+ case ' ': case '\t': case '\f': case '\r':
+ goto retry;
+
+ case '#': /* it's a comment */
+ while ((c = nextc()) != '\n') {
+ if (c == -1)
+ return 0;
+ }
+ /* fall through */
+ case '\n':
+ sourceline++;
+ if (lex_state == EXPR_BEG) goto retry;
+ lex_state = EXPR_BEG;
+ return '\n';
+
+ case '*':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '*') {
+ if (nextc() == '=') {
+ yylval.id = POW;
+ return SELF_ASGN;
+ }
+ pushback();
+ return POW;
+ }
+ else if (c == '=') {
+ yylval.id = '*';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '*';
+
+ case '!':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ return NEQ;
+ }
+ if (c == '~') {
+ return NMATCH;
+ }
+ pushback();
+ return '!';
+
+ case '=':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ return EQ;
+ }
+ if (c == '~') {
+ return MATCH;
+ }
+ else if (c == '>') {
+ return ASSOC;
+ }
+ pushback();
+ return '=';
+
+ case '<':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ if ((c = nextc()) == '>') {
+ return CMP;
+ }
+ pushback();
+ return LEQ;
+ }
+ if (c == '<') {
+ if (nextc() == '=') {
+ yylval.id = LSHFT;
+ return SELF_ASGN;
+ }
+ pushback();
+ return LSHFT;
+ }
+ pushback();
+ return '<';
+
+ case '>':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ return GEQ;
+ }
+ if (c == '>') {
+ if (nextc() == '=') {
+ yylval.id = RSHFT;
+ return SELF_ASGN;
+ }
+ pushback();
+ return RSHFT;
+ }
+ pushback();
+ return '>';
+
+ case '"':
+ {
+ int strstart = sourceline;
+
+ newtok();
+ while ((c = nextc()) != '"') {
+ if (c == -1) {
+ sourceline = strstart;
+ Error("unterminated string meets end of file");
+ return 0;
+ }
+ if (ismbchar(c)) {
+ tokadd(c);
+ c = nextc();
+ }
+ else if (c == '\n') {
+ sourceline++;
+ }
+ else if (c == '\\') {
+ c = nextc();
+ if (c == '\n') {
+ sourceline++;
+ }
+ else if (c == '"') {
+ tokadd(c);
+ }
+ else {
+ pushback();
+ read_escape(LEAVE_BS | EXPAND_B);
+ }
+ continue;
+ }
+ tokadd(c);
+ }
+ tokfix();
+ yylval.val = str_new(tok(), toklen());
+ lex_state = EXPR_END;
+ return STRING;
+ }
+
+ case '\'':
+ {
+ int strstart = sourceline;
+
+ newtok();
+ while ((c = nextc()) != '\'') {
+ if (c == -1) {
+ sourceline = strstart;
+ Error("unterminated string meets end of file");
+ return 0;
+ }
+ if (ismbchar(c)) {
+ tokadd(c);
+ c = nextc();
+ }
+ else if (c == '\n') {
+ sourceline++;
+ }
+ else if (c == '\\') {
+ c = nextc();
+ switch (c) {
+ case '\n':
+ sourceline++;
+ continue;
+
+ case '\'':
+ c = '\'';
+ break;
+ case '\\':
+ c = '\\';
+ break;
+
+ default:
+ tokadd('\\');
+ }
+ }
+ tokadd(c);
+ }
+ tokfix();
+ yylval.val = str_new(tok(), toklen());
+ lex_state = EXPR_END;
+ return STRING;
+ }
+
+ case '?':
+ if ((c = nextc()) == '\\') {
+ newtok();
+ read_escape(EXPAND_B);
+ c = tok()[0];
+ }
+ c &= 0xff;
+ yylval.val = INT2FIX(c);
+ lex_state = EXPR_END;
+ return INTEGER;
+
+ case '&':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '&') {
+ return AND;
+ }
+ else if (c == '=') {
+ yylval.id = '&';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '&';
+
+ case '|':
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '|') {
+ return OR;
+ }
+ else if (c == '=') {
+ yylval.id = '|';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '|';
+
+ case '+':
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ c = nextc();
+ pushback();
+ if (isdigit(c)) {
+ goto start_num;
+ }
+ }
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ yylval.id = '+';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '+';
+
+ case '-':
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ c = nextc();
+ pushback();
+ if (isdigit(c)) {
+ c = '-';
+ goto start_num;
+ }
+ }
+ lex_state = EXPR_BEG;
+ if ((c = nextc()) == '=') {
+ yylval.id = '-';
+ return SELF_ASGN;
+ }
+ pushback();
+ return '-';
+
+ case '.':
+ if ((c = nextc()) == '.') {
+ if ((c = nextc()) == '.') {
+ return DOT3;
+ }
+ pushback();
+ lex_state = EXPR_BEG;
+ return DOT2;
+ }
+ pushback();
+ if (!isdigit(c)) {
+ lex_state = EXPR_BEG;
+ return '.';
+ }
+ c = '.';
+ /* fall through */
+
+ 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;
+
+ lex_state = EXPR_END;
+ newtok();
+ if (c == '0') {
+ c = nextc();
+ if (c == 'x' || c == 'X') {
+ /* hexadecimal */
+ while (c = nextc()) {
+ if (!isxdigit(c)) break;
+ tokadd(c);
+ }
+ pushback();
+ tokfix();
+ yylval.val = str2inum(tok(), 16);
+ return INTEGER;
+ }
+ else if (c >= '0' && c <= '9') {
+ /* octal */
+ do {
+ tokadd(c);
+ c = nextc();
+ } while (c >= '0' && c <= '9');
+ pushback();
+ tokfix();
+#if 0
+ yylval.val = INT2FIX(strtoul(tok(), Qnil, 8));
+#else
+ yylval.val = str2inum(tok(), 8);
+#endif
+ return INTEGER;
+ }
+ }
+ if (c == '-' || c == '+') {
+ tokadd(c);
+ c = nextc();
+ }
+
+ is_float = seen_point = seen_e = 0;
+
+ for (;;) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ tokadd(c);
+ break;
+
+ case '.':
+ if (seen_point) {
+ goto decode_num;
+ }
+ c = nextc();
+ if (!isdigit(c)) {
+ pushback();
+ goto decode_num;
+ }
+ tokadd('.');
+ tokadd(c);
+ is_float++;
+ seen_point++;
+ break;
+
+ case 'e':
+ case 'E':
+ if (seen_e) {
+ goto decode_num;
+ }
+ tokadd(c);
+ seen_e++;
+ is_float++;
+ if ((c = nextc()) == '-' || c == '+')
+ tokadd(c);
+ else
+ continue;
+ break;
+
+ case '_': /* `_' in decimal just ignored */
+ break;
+
+ default:
+ goto decode_num;
+ }
+ c = nextc();
+ }
+
+ decode_num:
+ pushback();
+ tokfix();
+ if (is_float) {
+ double atof();
+
+ yylval.val = float_new(atof(tok()));
+ return FLOAT;
+ }
+ yylval.val = str2inum(tok(), 10);
+ return INTEGER;
+ }
+
+ case ']':
+ case '}':
+ case ')':
+ lex_state = EXPR_END;
+ return c;
+
+ case ':':
+ lex_state = EXPR_BEG;
+ if (nextc() == ':') {
+ return COLON2;
+ }
+ pushback();
+ return ':';
+
+ case '/':
+ lex_state = EXPR_BEG;
+ if (nextc() == '=') {
+ yylval.id = '/';
+ return SELF_ASGN;
+ }
+ pushback();
+ return c;
+
+ case '^':
+ lex_state = EXPR_BEG;
+ if (nextc() == '=') {
+ yylval.id = '^';
+ return SELF_ASGN;
+ }
+ pushback();
+ return c;
+
+ case ',':
+ case ';':
+ case '`':
+ case '[':
+ case '(':
+ case '{':
+ case '~':
+ lex_state = EXPR_BEG;
+ return c;
+
+ case '\\':
+ c = nextc();
+ if (c == '\n') goto retry; /* skip \\n */
+ lex_state = EXPR_BEG;
+ pushback();
+ return '\\';
+
+ case '%':
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
+ /* class constant */
+ newtok();
+ tokadd('%');
+ c = nextc();
+ break;
+ }
+ else {
+ lex_state = EXPR_BEG;
+ if (nextc() == '=') {
+ yylval.id = '%';
+ return SELF_ASGN;
+ }
+ pushback();
+ return c;
+ }
+
+ case '$':
+ newtok();
+ tokadd(c);
+ c = nextc();
+ switch (c) {
+ case '*': /* $*: argv */
+ case '$': /* $$: pid */
+ case '?': /* $?: last status */
+ case '!': /* $!: error string */
+ case '@': /* $@: error position */
+ case '/': /* $/: input record separator */
+ case '\\': /* $\: output record separator */
+ case ',': /* $,: output field separator */
+ case '.': /* $.: last read line number */
+ case '_': /* $_: last read line string */
+ case '&': /* $&: last match */
+ case '~': /* $~: match-data */
+ case '=': /* $=: ignorecase */
+ tokadd(c);
+ tokadd('\0');
+ goto id_fetch;
+
+ default:
+ if (is_identchar(c))
+ break;
+ pushback();
+ return tok()[0];
+ }
+ break;
+
+ case '@':
+ c = nextc();
+ if (!is_identchar(c)) {
+ pushback();
+ return '@';
+ }
+ newtok();
+ tokadd('@');
+ break;
+
+ default:
+ if (c != '_' && !isalpha(c) && !ismbchar(c)) {
+ Error("Invalid char '%c' in expression", c);
+ goto retry;
+ }
+
+ newtok();
+ break;
+ }
+
+ while (is_identchar(c)) {
+ tokadd(c);
+ if (ismbchar(c)) {
+ c = nextc();
+ tokadd(c);
+ }
+ c = nextc();
+ }
+ pushback();
+ tokfix();
+
+ /* See if it is a reserved word. */
+ while (low <= high) {
+ mid = low + (high - low)/2;
+ if (( c = strcmp(mid->name, tok())) == 0) {
+ if (mid->state != KEEP_STATE) {
+ lex_state = mid->state;
+ }
+ return mid->id;
+ }
+ else if (c < 0) {
+ low = mid + 1;
+ }
+ else {
+ high = mid - 1;
+ }
+ }
+
+ id_fetch:
+ lex_state = EXPR_END;
+ yylval.id = rb_intern(tok());
+ switch (tok()[0]) {
+ case '%':
+ return CONSTANT;
+ case '$':
+ return GVAR;
+ case '@':
+ return IVAR;
+ default:
+ return IDENTIFIER;
+ }
+}
+
+static void
+read_escape(flag)
+ int flag;
+{
+ char c;
+
+ switch (c = nextc()) {
+ case '\\': /* Backslash */
+ tokadd('\\');
+ break;
+
+ case 'n': /* newline */
+ tokadd('\n');
+ break;
+
+ case 't': /* horizontal tab */
+ tokadd('\t');
+ break;
+
+ case 'r': /* carriage-return */
+ tokadd('\r');
+ break;
+
+ case 'f': /* form-feed */
+ tokadd('\r');
+ break;
+
+ case 'v': /* vertical tab */
+ tokadd('\13');
+ break;
+
+ case 'a':
+ tokadd('\a');
+ break;
+
+ case 'e': /* escape */
+ tokadd(033);
+ break;
+
+ case 'M':
+ if ((c = nextc()) != '-') {
+ Error("Invalid escape character syntax");
+ tokadd('\0');
+ return;
+ }
+ if ((c = nextc()) == '\\') {
+ read_escape(flag);
+ tokenbuf[tokidx-1] |= 0200; /* kludge */
+ }
+ else {
+ tokadd((c & 0xff) | 0200);
+ }
+ break;
+
+ case 'C':
+ if ((c = nextc()) != '-') {
+ Error("Invalid escape character syntax");
+ tokadd('\0');
+ return;
+ }
+ case '^':
+ if ((c = nextc())== '\\') {
+ read_escape (flag);
+ tokenbuf[tokidx-1] &= 0237; /* kludge */
+ }
+ else if (c == '?')
+ tokadd(0177);
+ else
+ tokadd(c & 0237);
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ { /* octal constant */
+ register int i = c - '0';
+ register int count = 0;
+
+ while (++count < 3) {
+ if ((c = nextc()) >= '0' && c <= '7') {
+ i *= 8;
+ i += c - '0';
+ }
+ else {
+ pushback();
+ break;
+ }
+ }
+ tokadd(i&0xff);
+ }
+ break;
+
+ case 'x': /* hex constant */
+ {
+ register int i = c - '0';
+ register int count = 0;
+
+ while (++count < 2) {
+ c = nextc();
+ if ((c = nextc()) >= '0' && c <= '9') {
+ i *= 16;
+ i += c - '0';
+ }
+ else if ((int)index("abcdefABCDEF", (c = nextc()))) {
+ i *= 16;
+ i += toupper(c) - 'A' + 10;
+ }
+ else {
+ pushback();
+ break;
+ }
+ }
+ tokadd(i&0xff);
+ }
+ break;
+
+ case 'b': /* backspace */
+ if (flag & EXPAND_B) {
+ tokadd('\b');
+ return;
+ }
+ /* go turough */
+ default:
+ if (flag & LEAVE_BS) {
+ tokadd('\\');
+ }
+ tokadd(c);
+ break;
+ }
+}
+
+NODE*
+newnode(type, a0, a1, a2)
+ enum node_type type;
+ NODE *a0, *a1, *a2;
+{
+ NODE *n = ALLOC(NODE);
+
+ n->type = type;
+ n->line = sourceline;
+ n->src = sourcefile;
+
+ n->u1.node = a0;
+ n->u2.node = a1;
+ n->u3.node = a2;
+
+ return n;
+}
+
+static NODE*
+block_append(head, tail)
+ NODE *head, *tail;
+{
+ extern int verbose;
+
+ if (tail == Qnil) return head;
+ if (head == Qnil) return tail;
+
+ if (head->type != NODE_BLOCK)
+ head = NEW_BLOCK(head);
+
+ if (head->nd_last == Qnil) head->nd_last = head;
+
+ if (verbose) {
+ switch (head->nd_last->nd_head->type) {
+ case NODE_BREAK:
+ case NODE_CONTINUE:
+ case NODE_REDO:
+ case NODE_RETURN:
+ case NODE_RETRY:
+ Warning("statement not reached");
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (tail->type == NODE_BLOCK) {
+ head->nd_last->nd_next = tail;
+ head->nd_last = tail->nd_last;
+ }
+ else {
+ head->nd_last->nd_next = NEW_BLOCK(tail);
+ head->nd_last = head->nd_last->nd_next;
+ }
+ return head;
+}
+
+static NODE*
+list_append(head, tail)
+ NODE *head, *tail;
+{
+ if (head == Qnil) return NEW_ARRAY(tail);
+
+ if (head->nd_last == Qnil) head->nd_last = head;
+
+ head->nd_last->nd_next = NEW_ARRAY(tail);
+ head->nd_last = head->nd_last->nd_next;
+ return head;
+}
+
+static NODE*
+list_concat(head, tail)
+ NODE *head, *tail;
+{
+ NODE *last;
+
+ if (head->type != NODE_ARRAY || tail->type != NODE_ARRAY)
+ Bug("list_concat() called with non-list");
+
+ last = (head->nd_last)?head->nd_last:head;
+ last->nd_next = tail;
+ head->nd_last = tail->nd_last;
+
+ return head;
+}
+
+static NODE*
+list_copy(list)
+ NODE *list;
+{
+ NODE *tmp;
+
+ if (list == Qnil) return Qnil;
+
+ tmp = Qnil;
+ while(list) {
+ tmp = list_append(tmp, list->nd_head);
+ list = list->nd_next;
+ }
+ return tmp;
+}
+
+void freenode(node)
+ NODE *node;
+{
+ if (node == Qnil) return;
+
+ switch (node->type) {
+ case NODE_BLOCK:
+ case NODE_ARRAY:
+ freenode(node->nd_head);
+ freenode(node->nd_next);
+ break;
+ case NODE_IF:
+ case NODE_WHEN:
+ case NODE_PROT:
+ freenode(node->nd_cond);
+ freenode(node->nd_body);
+ freenode(node->nd_else);
+ break;
+ case NODE_CASE:
+ case NODE_WHILE:
+ case NODE_UNTIL:
+ case NODE_AND:
+ case NODE_OR:
+ freenode(node->nd_head);
+ freenode(node->nd_body);
+ break;
+ case NODE_DO:
+ case NODE_FOR:
+ freenode(node->nd_ibdy);
+ freenode(node->nd_iter);
+ break;
+ case NODE_LASGN:
+ case NODE_GASGN:
+ case NODE_IASGN:
+ freenode(node->nd_value);
+ break;
+ case NODE_CALL:
+ case NODE_SUPER:
+ freenode(node->nd_recv);
+ case NODE_CALL2:
+ freenode(node->nd_args);
+ break;
+ case NODE_DEFS:
+ freenode(node->nd_recv);
+ break;
+ case NODE_RETURN:
+ case NODE_YIELD:
+ freenode(node->nd_stts);
+ break;
+ case NODE_STR:
+ 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);
+ freenode(node->nd_body);
+ break;
+ case NODE_DEFN:
+ case NODE_ZARRAY:
+ case NODE_CFUNC:
+ case NODE_BREAK:
+ case NODE_CONTINUE:
+ 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_NIL:
+ break;
+ default:
+ Bug("freenode: unknown node type %d", node->type);
+ break;
+ }
+ free(node);
+}
+
+struct call_arg {
+ ID id;
+ VALUE recv;
+ int narg;
+ VALUE arg;
+};
+
+static VALUE
+call_lit(arg)
+ struct call_arg *arg;
+{
+ return rb_funcall(arg->recv, arg->id, arg->narg, arg->arg);
+}
+
+static VALUE
+except_lit()
+{
+ extern VALUE errstr;
+
+ Error("%s", RSTRING(errstr)->ptr);
+ return Qnil;
+}
+
+static NODE *
+call_op(recv, id, narg, arg1)
+ NODE *recv;
+ ID id;
+ int narg;
+ NODE *arg1;
+{
+ NODE *args;
+
+ value_expr(recv);
+ if (narg == 1)
+ value_expr(arg1);
+
+ if (recv->type != NODE_LIT || recv->type != NODE_STR
+ || (narg == 0 && id == '~'
+ && (TYPE(recv->nd_lit)==T_REGEXP || TYPE(recv->nd_lit)==T_STRING))
+ || arg1->type == NODE_LIT || arg1->type == NODE_STR) {
+ if (narg > 0) {
+ args = NEW_ARRAY(arg1);
+ args->nd_argc = 1;
+ }
+ else {
+ args = Qnil;
+ }
+ return NEW_CALL(recv, id, args);
+ }
+ else {
+ struct call_arg arg_data;
+ NODE *result;
+
+ arg_data.recv = recv->nd_lit;
+ arg_data.id = id;
+ arg_data.narg = narg;
+ if (narg == 1) arg_data.arg = arg1->nd_lit;
+ result = NEW_LIT(rb_resque(call_lit, &arg_data, except_lit, Qnil));
+ freenode(recv);
+ if (narg == 1) freenode(arg1);
+ return result;
+ }
+}
+
+static NODE*
+asignable(id, val)
+ ID id;
+ NODE *val;
+{
+ NODE *lhs;
+
+ if (id == SELF) {
+ lhs = Qnil;
+ Error("Can't change the value of self");
+ }
+ else if (id == NIL) {
+ lhs = Qnil;
+ Error("Can't asign to nil");
+ }
+ else if (is_local_id(id)) {
+ lhs = NEW_LASGN(id, val);
+ }
+ else if (is_global_id(id)) {
+ lhs = NEW_GASGN(id, val);
+ }
+ else if (is_instance_id(id)) {
+ lhs = NEW_IASGN(id, val);
+ }
+ else if (is_const_id(id)) {
+ if (cur_mid || in_single)
+ Error("class constant asigned in method body");
+ lhs = NEW_CASGN(id, val);
+ }
+ else {
+ Bug("bad id for variable");
+ }
+ return lhs;
+}
+
+static NODE *
+aryset(recv, idx, val)
+ NODE *recv, *idx, *val;
+{
+ NODE *args;
+
+ value_expr(recv);
+ return NEW_CALL(recv, ASET, list_append(idx, val));
+}
+
+static NODE *
+attrset(recv, id, val)
+ NODE *recv, *val;
+ ID id;
+{
+ value_expr(recv);
+
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+
+ return NEW_CALL(recv, id, NEW_ARRAY(val));
+}
+
+static void
+value_expr(node)
+ NODE *node;
+{
+ switch (node->type) {
+ case NODE_RETURN:
+ case NODE_CONTINUE:
+ case NODE_BREAK:
+ case NODE_REDO:
+ case NODE_RETRY:
+ Error("void value expression");
+ break;
+
+ case NODE_BLOCK:
+ if (node->nd_last)
+ return value_expr(node->nd_last->nd_head);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static NODE*
+cond(node)
+ NODE *node;
+{
+ value_expr(node);
+ if (node->type == NODE_STR) {
+ return call_op(NEW_GVAR(rb_intern("$_")),MATCH,1,node);
+ }
+ else if (node->type == NODE_LIT && TYPE(node->nd_lit) == T_REGEXP) {
+ return call_op(node,MATCH,1,NEW_GVAR(rb_intern("$_")));
+ }
+ return node;
+}
+
+static NODE*
+cond2(node)
+ NODE *node;
+{
+ node = cond(node);
+ if (node->type == NODE_LIT) {
+ if (FIXNUM_P(node->nd_lit)) {
+ return call_op(node,EQ,1,NEW_GVAR(rb_intern("$.")));
+ }
+ }
+ return node;
+}
+
+st_table *new_idhash();
+
+static struct local_vars {
+ ID *tbl;
+ int cnt;
+ struct local_vars *next;
+} *lvtbl;
+
+static void
+push_local()
+{
+ struct local_vars *local;
+
+ local = ALLOC(struct local_vars);
+ local->next = lvtbl;
+ local->cnt = 0;
+ local->tbl = Qnil;
+ lvtbl = local;
+}
+
+void
+pop_local()
+{
+ struct local_vars *local = lvtbl;
+
+ lvtbl = local->next;
+ if (local->tbl) local->tbl[0] = local->cnt;
+ free(local);
+}
+
+static ID*
+local_tbl()
+{
+ return lvtbl->tbl;
+}
+
+static int
+local_cnt(id)
+ ID id;
+{
+ int cnt, max;
+
+ if (id == 0) return lvtbl->cnt;
+
+ for (cnt=0, max=lvtbl->cnt; cnt<max ;cnt++) {
+ if (lvtbl->tbl[cnt+1] == id) return cnt;
+ }
+
+ if (lvtbl->tbl == Qnil)
+ lvtbl->tbl = ALLOC_N(ID, 2);
+ else
+ REALLOC_N(lvtbl->tbl, ID, lvtbl->cnt+2);
+
+ lvtbl->tbl[lvtbl->cnt+1] = id;
+ return lvtbl->cnt++;
+}
+
+static int
+local_id(id)
+ ID id;
+{
+ int i, max;
+
+ if (lvtbl == Qnil) return FALSE;
+ for (i=1, max=lvtbl->cnt+1; i<max; i++) {
+ if (lvtbl->tbl[i] == id) return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+init_top_local()
+{
+ if (lvtbl == Qnil) {
+ push_local();
+ }
+ else if (the_env->local_tbl) {
+ lvtbl->cnt = the_env->local_tbl[0];
+ }
+ else {
+ lvtbl->cnt = 0;
+ }
+ lvtbl->tbl = the_env->local_tbl;
+}
+
+static void
+setup_top_local()
+{
+ if (lvtbl->cnt > 0) {
+ if (the_env->local_vars == Qnil) {
+ the_env->local_vars = ALLOC_N(VALUE, lvtbl->cnt);
+ bzero(the_env->local_vars, lvtbl->cnt * sizeof(VALUE));
+ }
+ else {
+ int i;
+
+ REALLOC_N(the_env->local_vars, VALUE, lvtbl->cnt);
+ for (i=lvtbl->tbl[0]; i<lvtbl->cnt; i++) {
+ the_env->local_vars[i] = Qnil;
+ }
+ }
+ lvtbl->tbl[0] = lvtbl->cnt;
+ the_env->local_tbl = lvtbl->tbl;
+ }
+ else {
+ the_env->local_vars = Qnil;
+ }
+}
+
+void
+yyappend_print()
+{
+ eval_tree =
+ block_append(eval_tree, NEW_CALL(Qnil, rb_intern("print"),
+ NEW_ARRAY(NEW_GVAR(rb_intern("$_")))));
+}
+
+void
+yywhole_loop(chop, split)
+ int chop, split;
+{
+ if (split) {
+ eval_tree =
+ block_append(NEW_GASGN(rb_intern("$F"),
+ NEW_CALL(NEW_GVAR(rb_intern("$_")),
+ rb_intern("split"), Qnil)),
+ eval_tree);
+ }
+ if (chop) {
+ eval_tree =
+ block_append(NEW_CALL(NEW_GVAR(rb_intern("$_")),
+ rb_intern("chop"), Qnil), eval_tree);
+ }
+ eval_tree = NEW_WHILE(NEW_CALL(0,rb_intern("gets"),0),eval_tree);
+}
+
+static struct op_tbl rb_op_tbl[] = {
+ DOT2, "..",
+ '+', "+",
+ '-', "-",
+ '+', "+(binary)",
+ '-', "-(binary)",
+ '*', "*",
+ '/', "/",
+ '%', "%",
+ POW, "**",
+ UPLUS, "+(unary)",
+ UMINUS, "-(unary)",
+ UPLUS, "+@",
+ UMINUS, "-@",
+ '|', "|",
+ '^', "^",
+ '&', "&",
+ CMP, "<=>",
+ '>', ">",
+ GEQ, ">=",
+ '<', "<",
+ LEQ, "<=",
+ EQ, "==",
+ NEQ, "!=",
+ MATCH, "=~",
+ NMATCH, "!~",
+ '!', "!",
+ '~', "~",
+ '!', "!(unary)",
+ '~', "~(unary)",
+ '!', "!@",
+ '~', "~@",
+ AREF, "[]",
+ ASET, "[]=",
+ LSHFT, "<<",
+ RSHFT, ">>",
+ COLON2, "::",
+ Qnil, Qnil,
+};
+
+char *rb_id2name();
+char *rb_class2name();
+
+st_table *rb_symbol_tbl;
+
+#define sym_tbl rb_symbol_tbl
+
+void
+Init_sym()
+{
+ int strcmp();
+
+ sym_tbl = st_init_table(strcmp, st_strhash);
+}
+
+ID
+rb_intern(name)
+ char *name;
+{
+ static ID last_id = LAST_TOKEN;
+ int id;
+ int last;
+
+ if (st_lookup(sym_tbl, name, &id))
+ return id;
+
+ id = ++last_id;
+ id <<= 3;
+ switch (name[0]) {
+ case '$':
+ id |= ID_GLOBAL;
+ break;
+ case '@':
+ id |= ID_INSTANCE;
+ break;
+ case '%':
+ if (name[1] != '\0') {
+ id |= ID_CONST;
+ break;
+ }
+ /* fall through */
+ default:
+ if (name[0] != '_' && !isalpha(name[0]) && !ismbchar(name[0])) {
+ /* operator */
+ int i;
+
+ id = Qnil;
+ for (i=0; rb_op_tbl[i].tok; i++) {
+ if (strcmp(rb_op_tbl[i].name, name) == 0) {
+ id = rb_op_tbl[i].tok;
+ break;
+ }
+ }
+ if (id == Qnil) Bug("Unknown operator `%s'", name);
+ break;
+ }
+ last = strlen(name)-1;
+ if (name[last] == '=') {
+ /* attribute asignment */
+ char *buf = (char*)alloca(last+1);
+
+ strncpy(buf, name, last);
+ buf[last] = '\0';
+ id = rb_intern(buf);
+ id &= ~ID_SCOPE_MASK;
+ id |= ID_ATTRSET;
+ }
+ else {
+ id |= ID_LOCAL;
+ }
+ break;
+ }
+ st_add_direct(sym_tbl, strdup(name), id);
+ return id;
+}
+
+static char *find_ok;
+
+static
+id_find(name, id1, id2)
+ char *name;
+ ID id1, id2;
+{
+ if (id1 == id2) {
+ find_ok = name;
+ return ST_STOP;
+ }
+ return ST_CONTINUE;
+}
+
+char *
+rb_id2name(id)
+ ID id;
+{
+ find_ok = Qnil;
+
+ if (id < LAST_TOKEN) {
+ int i = 0;
+
+ for (i=0; rb_op_tbl[i].tok; i++) {
+ if (rb_op_tbl[i].tok == id)
+ return rb_op_tbl[i].name;
+ }
+ }
+
+ st_foreach(sym_tbl, id_find, id);
+ if (!find_ok && is_attrset_id(id)) {
+ char *res;
+ ID id2;
+
+ id2 = (id & ~ID_SCOPE_MASK) | ID_LOCAL;
+ res = rb_id2name(id2);
+
+ if (res) {
+ char *buf = (char*)alloca(strlen(res)+2);
+
+ strcpy(buf, res);
+ strcat(buf, "=");
+ rb_intern(buf);
+ return rb_id2name(id);
+ }
+ }
+ return find_ok;
+}
+
+char *
+rb_class2name(class)
+ struct RClass *class;
+{
+ extern st_table *rb_class_tbl;
+
+ find_ok = Qnil;
+
+ switch (TYPE(class)) {
+ case T_CLASS:
+ case T_MODULE:
+ case T_ICLASS:
+ break;
+ default:
+ Fail("0x%x is not a class/module", class);
+ }
+
+ while (FL_TEST(class, FL_SINGLE)) {
+ class = (struct RClass*)class->super;
+ }
+
+ st_foreach(rb_class_tbl, id_find, class);
+ if (find_ok) {
+ return rb_id2name((ID)find_ok);
+ }
+ Bug("class 0x%x not named", class);
+}