diff options
author | Yukihiro Matsumoto <matz@ruby-lang.org> | 1994-07-18 10:19:15 +0900 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2019-08-17 22:09:29 +0900 |
commit | 200e0ee2fd3c1c006c528874a88f684447215524 (patch) | |
tree | df2252585f3000878a15123d98ccb08124306b90 /eval.c |
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 'eval.c')
-rw-r--r-- | eval.c | 1858 |
1 files changed, 1858 insertions, 0 deletions
diff --git a/eval.c b/eval.c new file mode 100644 index 0000000000..f4d6d545ae --- /dev/null +++ b/eval.c @@ -0,0 +1,1858 @@ +/************************************************ + + eval.c - + + $Author: matz $ + $Date: 1994/06/27 15:48:23 $ + created at: Thu Jun 10 14:22:17 JST 1993 + + Copyright (C) 1994 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "env.h" +#include "node.h" +#include "ident.h" +#include <stdio.h> +#include <setjmp.h> +#include "st.h" + +static ID match, each; +VALUE errstr, errat; +extern NODE *eval_tree; + +struct ENVIRON *the_env, *top_env; + +#define PUSH_ENV() {\ + struct ENVIRON _this;\ + if (the_env) _this = *the_env; else bzero(&_this, sizeof(_this));\ + _this.prev = the_env;\ + _this.flags = 0;\ + the_env = &_this;\ + +#define POP_ENV() the_env = the_env->prev; } + +struct BLOCK { + NODE *var; + NODE *body; + struct ENVIRON env; + int level; +}; + +#define SET_BLOCK(b,node) (b.level=tag_level,b.var=node->nd_var,\ + b.body=node->nd_body,b.env=*the_env,\ + the_env->block= &b) + +static int tag_level, target_level; +static struct tag { + int level; + jmp_buf buf; + struct gc_list *gclist; + struct ENVIRON *env; +} *prot_tag; + +#define PUSH_TAG() {\ + struct tag _this;\ + struct tag *_oldtag = prot_tag;\ + &_oldtag;\ + _this.level= ++tag_level;\ + _this.gclist= GC_List;\ + _this.env= the_env;\ + prot_tag = &_this;\ + +#define POP_TAG() \ + tag_level--;\ + prot_tag = _oldtag;\ +} + +#define EXEC_TAG() (setjmp(prot_tag->buf)) +#define JUMP_TAG(val) {\ + the_env = prot_tag->env;\ + GC_List = prot_tag->gclist;\ + longjmp(prot_tag->buf,(val));\ +} + +#define TAG_RETURN 1 +#define TAG_BREAK 2 +#define TAG_CONTINUE 3 +#define TAG_RETRY 4 +#define TAG_REDO 5 +#define TAG_FAIL 6 +#define TAG_EXIT 7 + +#define IN_BLOCK 0x08 + +static VALUE rb_eval(); +VALUE Feval(); + +VALUE Argv; +static VALUE rb_call(); +VALUE rb_apply(); + +static void asign(); + +static VALUE last_val; + +extern VALUE rb_stderr; + +extern int sourceline; +extern char *sourcefile; + +static ID last_func; +static void +error_print() +{ + if (errat) { + fwrite(RSTRING(errat)->ptr, 1, RSTRING(errat)->len, stderr); + if (last_func) { + fprintf(stderr, ":in method `%s': ", rb_id2name(last_func)); + } + else { + fprintf(stderr, ": "); + } + } + + if (errstr) { + fwrite(RSTRING(errstr)->ptr, 1, RSTRING(errstr)->len, stderr); + } + else { + fprintf(stderr, "unhandled failure.\n"); + } + rb_trap_exit(); + exit(1); +} + +main(argc, argv) + int argc; + char *argv[]; +{ + int state; + + PUSH_ENV(); + top_env = the_env; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_main(argc, argv); + } + POP_TAG(); + + switch (state) { + case 0: + break; + case TAG_RETURN: + Fatal("unexpected return"); + break; + case TAG_CONTINUE: + Fatal("unexpected continue"); + break; + case TAG_BREAK: + Fatal("unexpected break"); + break; + case TAG_REDO: + Fatal("unexpected redo"); + break; + case TAG_RETRY: + Fatal("retry outside of protect clause"); + break; + case TAG_FAIL: + PUSH_TAG() + error_print(); + POP_TAG(); + break; + case TAG_EXIT: + rb_trap_exit(); + exit(FIX2UINT(last_val)); + break; + default: + Bug("Unknown longjmp status %d", state); + break; + } + POP_ENV(); + exit(0); +} + +VALUE rb_readonly_hook(); + +VALUE Progname; + +static VALUE +Eval() +{ + int state; + NODE *tree; + + if (match == Qnil) match = rb_intern("=~"); + if (each == Qnil) each = rb_intern("each"); + + tree = eval_tree; + eval_tree = Qnil; + + return rb_eval(tree); +} + +VALUE +TopLevel(script, argc, argv) + char *script; + int argc; + char **argv; +{ + int i; + + the_class = (struct RClass*)C_Object; + + rb_define_variable("$!", &errstr, Qnil, Qnil); + errat = Qnil; /* clear for execution */ + + Progname = str_new2(script); + rb_define_variable("$0", &Progname, Qnil, Qnil); + + rb_define_variable("$ARGV", &Argv, Qnil, Qnil); + rb_define_variable("$*", &Argv, Qnil, Qnil); + Argv = ary_new2(argc); + for (i=0; i < argc; i++) { + Fary_push(Argv, str_new2(argv[i])); + } + return Eval(); +} + +void +rb_trap_eval(cmd) + VALUE cmd; +{ + PUSH_ENV(); + the_env->self = top_env->self; + the_env->current_module = top_env->current_module; + the_env->local_vars = top_env->local_vars; + the_class = (struct RClass*)C_Object; + + Feval(Qself, cmd); + POP_ENV(); +} + +static int +setup_arg_1(node, args) + NODE *node; + VALUE *args; +{ + int argc; + + if (node->type == NODE_ARRAY) { + for (argc=0; node; node=node->nd_next) argc++; + argc++; + } + else { + *args = rb_eval(node); + if (TYPE(*args) != T_ARRAY) + Fail("*`argument' must be array"); + argc = RARRAY(*args)->len + 1; + } + return argc; +} + +static void +setup_arg_2(node, args, argc, argv) + NODE *node; + VALUE args; + int argc; + VALUE *argv; +{ + int i; + + bzero(argv, sizeof(VALUE)*argc); + if (node->type == NODE_ARRAY) { + for (i=1;node;node=node->nd_next) { + argv[i++] = rb_eval(node->nd_head); + } + } + else { + for (i=1;i<argc;i++) { + argv[i] = RARRAY(args)->ptr[i-1]; + } + } +} + +#define SETUP_ARGS {\ + VALUE args;\ + GC_LINK;\ + GC_PRO2(args);\ + argc = setup_arg_1(node->nd_args, &args);\ + argv = (VALUE*)alloca(sizeof(VALUE)*argc);\ + GC_PRO4(argv, argc);\ + setup_arg_2(node->nd_args, args, argc, argv);\ + GC_UNLINK;\ +} + +static VALUE +rb_eval(node) + register NODE *node; +{ + int state; + int go_out = 0; + VALUE result; + + &go_out; + again: + if (node == Qnil) return Qnil; + + sourceline = node->line; + sourcefile = node->src; + +#ifdef SAFE_SIGHANDLE + { + extern int trap_pending; + + if (trap_pending) { + rb_trap_exec(); + } + } +#endif + + switch (node->type) { + case NODE_BLOCK: + while (node->nd_next) { + rb_eval(node->nd_head); + node = node->nd_next; + } + node = node->nd_head; + goto again; + + case NODE_SELF: + return Qself; + + case NODE_NIL: + return Qnil; + + case NODE_IF: + if (rb_eval(node->nd_cond)) { + node = node->nd_body; + } + else { + node = node->nd_else; + } + if (node) goto again; + return Qnil; + + case NODE_UNLESS: + { + VALUE res; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + res = rb_eval(node->nd_cond); + } + POP_TAG(); + if (state == 0) + ; + else if (state == TAG_FAIL) { + res = Qnil; + } + else { + JUMP_TAG(state); + } + + if (res == Qnil) { + node = node->nd_body; + } + else { + node = node->nd_else; + } + if (node) goto again; + return res; + } + + case NODE_CASE: + { + VALUE val; + + GC_LINK; + GC_PRO3(val, rb_eval(node->nd_head)); + + node = node->nd_body; + while (node) { + if (node->type == NODE_WHEN) { + NODE *tag = node->nd_head; + + while (tag) { + if (rb_funcall(rb_eval(tag->nd_head), match, 1, val)){ + result = rb_eval(node->nd_body); + goto exit_case; + } + tag = tag->nd_next; + } + } + else { + result = rb_eval(node); + goto exit_case; + } + node = node->nd_next; + } + exit_case: + GC_UNLINK; + } + return result; + + case NODE_WHILE: + PUSH_TAG(); + switch (state = EXEC_TAG()) { + case 0: + while_cont: + while (rb_eval(node->nd_cond)) { + while_redo: + rb_eval(node->nd_body); + } + break; + case TAG_REDO: + goto while_redo; + case TAG_CONTINUE: + goto while_cont; + default: + go_out++; + case TAG_BREAK: + break; + } + while_out: + POP_TAG(); + if (go_out) JUMP_TAG(state); + return Qnil; + + case NODE_UNTIL: + for (;;) { + VALUE res; + + PUSH_TAG(); + switch (state = EXEC_TAG()) { + case 0: + res = rb_eval(node->nd_cond); + break; + + case TAG_FAIL: + res = Qnil; + break; + + default: + go_out++; + } + POP_TAG(); + if (go_out) JUMP_TAG(state); + if (res) return res; + + PUSH_TAG(); + switch (state = EXEC_TAG()) { + case 0: + until_redo: + rb_eval(node->nd_body); + break; + case TAG_REDO: + goto until_redo; + case TAG_CONTINUE: + break; + case TAG_BREAK: + goto until_break; + default: + go_out++; + } + POP_TAG(); + if (go_out) JUMP_TAG(state); + } + until_break: + break; + + case NODE_DO: + case NODE_FOR: + { + struct BLOCK block; + + PUSH_ENV(); + SET_BLOCK(block, node); + PUSH_TAG(); + + state = EXEC_TAG(); + if (state == 0) { + if (node->type == NODE_DO) { + the_env->iterator = 1; + rb_eval(node->nd_iter); + } + else { + VALUE recv; + + GC_LINK; + GC_PRO2(recv); + recv = rb_eval(node->nd_iter); + the_env->iterator = 1; + result = rb_call(CLASS_OF(recv), recv, each, 1, Qnil, + MTH_METHOD); + GC_UNLINK; + } + } + POP_TAG(); + POP_ENV(); + switch (state) { + case 0: + break; + case IN_BLOCK|TAG_BREAK: + if (target_level != tag_level) { + JUMP_TAG(state); + } + result = Qnil; + break; + case IN_BLOCK|TAG_RETURN: + if (target_level == tag_level) { + state = TAG_RETURN; + } + /* fall through */ + default: + JUMP_TAG(state); + } + } + return result; + + case NODE_YIELD: + { + VALUE val; + + GC_LINK; + GC_PRO3(val, rb_eval(node->nd_stts)); + result = rb_yield(val); + GC_UNLINK; + } + return result; + + case NODE_PROT: + GC_LINK; + GC_PRO2(result); + + PUSH_TAG(); + + switch (state = EXEC_TAG()) { + case 0: + retry_entry: + result = rb_eval(node->nd_head); + break; + + case TAG_FAIL: + if (node->nd_resq) { + if (node->nd_resq == (NODE*)1) { + state = 0; + } + else { + PUSH_TAG(); + state = EXEC_TAG(); + if (state == 0) result = rb_eval(node->nd_resq); + POP_TAG(); + if (state == TAG_RETRY) { + goto retry_entry; + } + } + if (state == 0) { + errstr = errat = Qnil; + last_func = 0; + } + } + break; + } + POP_TAG(); + + /* ensure clause */ + rb_eval(node->nd_ensr); + GC_UNLINK; + + if (state != 0) { + JUMP_TAG(state); + } + return result; + + case NODE_AND: + if ((result = rb_eval(node->nd_1st)) == Qnil) return result; + node = node->nd_2nd; + goto again; + + case NODE_OR: + if ((result = rb_eval(node->nd_1st)) != Qnil) return result; + node = node->nd_2nd; + goto again; + + case NODE_DOT3: + if (node->nd_state == 0) { + if (rb_eval(node->nd_beg)) { + node->nd_state = 1; + return TRUE; + } + return FALSE; + } + else { + if (rb_eval(node->nd_end)) { + node->nd_state = 0; + } + return TRUE; + } + break; + + case NODE_BREAK: + JUMP_TAG(TAG_BREAK); + break; + + case NODE_CONTINUE: + JUMP_TAG(TAG_CONTINUE); + break; + + case NODE_RETRY: + JUMP_TAG(TAG_RETRY); + break; + + case NODE_RETURN: + if (node->nd_stts) last_val = rb_eval(node->nd_stts); + JUMP_TAG(TAG_RETURN); + break; + + case NODE_CALL: + case NODE_CALL2: + { + VALUE recv, *argv; + int argc, last_iter; + enum mth_scope scope; + + last_iter = the_env->iterator; + the_env->iterator = 0; /* recv & args are not iter. */ + recv = node->nd_recv?rb_eval(node->nd_recv):Qself; + if (node->nd_args) { + SETUP_ARGS; + } + else { + argc = 1; + argv = &recv; + } + the_env->iterator = last_iter; /* restore iter. level */ + + scope = node->nd_recv?MTH_METHOD:MTH_FUNC; + return rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,scope); + } + break; + + case NODE_SUPER: + case NODE_ZSUPER: + { + int last_iter; + int i, argc; + VALUE *argv; + + last_iter = the_env->iterator; /* recv & args are not iter. */ + the_env->iterator = 0; + + if (node->nd_args) { + SETUP_ARGS; + } + else if (node->type == NODE_ZSUPER) { + argc = the_env->argc; + argv = the_env->argv; + } + else { + argc = 1; + argv = Qnil; + } + + /* restore iter. level */ + switch (last_iter) { + case 1: /* SUPER called as iter. */ + case 2: /* iter. called SUPER */ + the_env->iterator = 1; + break; + default: /* otherwise SUPER is not iter. */ + break; + } + + result = rb_call(the_env->last_class->super, Qself, + the_env->last_func, argc, argv, Qnil, MTH_FUNC); + the_env->iterator = last_iter; + } + return result; + + case NODE_SCOPE: + { + VALUE result; + + PUSH_ENV(); + PUSH_TAG(); + if (node->nd_cnt > 0) { + the_env->local_vars = ALLOC_N(VALUE, node->nd_cnt); + bzero(the_env->local_vars, sizeof(VALUE)*node->nd_cnt); + the_env->local_tbl = node->nd_tbl; + } + else { + the_env->local_vars = Qnil; + the_env->local_tbl = Qnil; + } + if ((state = EXEC_TAG()) == 0) { + result = rb_eval(node->nd_body); + } + POP_TAG(); + if (the_env->local_vars) free(the_env->local_vars); + POP_ENV(); + if (state != 0) JUMP_TAG(state); + + return result; + } + + case NODE_MASGN: + { + VALUE val = rb_eval(node->nd_value); + NODE *list = node->nd_head; + int i, len; + + GC_LINK; + GC_PRO(val); + if (TYPE(val) != T_ARRAY) { + val = rb_funcall(val, rb_intern("to_a"), 0, Qnil); + if (TYPE(val) != T_ARRAY) { + Bug("to_a did not return Array"); + } + } + len = RARRAY(val)->len; + for (i=0; list && i<len; i++) { + asign(list->nd_head, RARRAY(val)->ptr[i]); + list = list->nd_next; + } + while (list) { + asign(list->nd_head, Qnil); + list = list->nd_next; + } + GC_UNLINK; + return val; + } + + case NODE_LASGN: + if (the_env->local_vars == Qnil) + Bug("unexpected local variable asignment"); + return the_env->local_vars[node->nd_cnt] = rb_eval(node->nd_value); + + case NODE_GASGN: + { + VALUE val; + + GC_LINK; GC_PRO3(val, rb_eval(node->nd_value)); + rb_gvar_set(node->nd_entry, val); + GC_UNLINK; + return val; + } + case NODE_IASGN: + { + VALUE val; + + GC_LINK; GC_PRO3(val, rb_eval(node->nd_value)); + rb_ivar_set(node->nd_vid, val); + GC_UNLINK; + return val; + } + case NODE_CASGN: + { + VALUE val; + + GC_LINK; GC_PRO3(val, rb_eval(node->nd_value)); + rb_const_set(node->nd_vid, val); + GC_UNLINK; + return val; + } + break; + + case NODE_LVAR: + if (the_env->local_vars == Qnil) + Bug("unexpected local variable"); + return the_env->local_vars[node->nd_cnt]; + + case NODE_GVAR: + return rb_gvar_get(node->nd_entry); + case NODE_IVAR: + return rb_ivar_get(node->nd_vid); + case NODE_MVAR: + return rb_mvar_get(node->nd_vid); + + case NODE_CVAR: + { + VALUE val = rb_const_get(node->nd_vid); + node->type = NODE_CONST; + node->nd_cval = val; + return val; + } + + case NODE_CONST: + return node->nd_cval; + + case NODE_HASH: + { + extern VALUE C_Dict; + extern VALUE Fdic_new(); + NODE *list; + + VALUE hash = Fdic_new(C_Dict); + VALUE key, val; + + GC_LINK; + GC_PRO(hash); GC_PRO2(key); GC_PRO2(val); + list = node->nd_head; + while (list) { + key = rb_eval(list->nd_head); + list = list->nd_next; + if (list == Qnil) + Bug("odd number list for hash"); + val = rb_eval(list->nd_head); + list = list->nd_next; + Fdic_aset(hash, key, val); + } + GC_UNLINK; + return hash; + } + break; + + case NODE_ZARRAY: /* zero length list */ + return ary_new(); + + case NODE_ARRAY: + { + VALUE ary; + int i; + NODE *list; + + GC_LINK; + for (i=0, list=node; list; list=list->nd_next) i++; + GC_PRO3(ary, ary_new2(i)); + for (i=0;node;node=node->nd_next) { + RARRAY(ary)->ptr[i++] = rb_eval(node->nd_head); + RARRAY(ary)->len = i; + } + GC_UNLINK; + + return ary; + } + break; + + case NODE_STR: + return str_new3(node->nd_lit); + + case NODE_LIT: + return node->nd_lit; + + case NODE_ATTRSET: + if (the_env->argc != 2) + Fail("Wrong # of arguments(%d for 1)", the_env->argc - 1); + return rb_ivar_set(node->nd_vid, the_env->argv[1]); + + case NODE_ARGS: + { + NODE *local; + int i, len; + + local = node->nd_frml; + for (i=0; local; local=local->nd_next,i++) + ; + + len = the_env->argc - 1; + if (i > len || (node->nd_rest == -1 && i < len)) + Fail("Wrong # of arguments(%d for %d)", len, i); + + local = node->nd_frml; + if (the_env->local_vars == Qnil) + Bug("unexpected local variable asignment"); + + for (i=1;local;i++) { + the_env->local_vars[(int)local->nd_head] = the_env->argv[i]; + local = local->nd_next; + } + if (node->nd_rest >= 0) { + if (the_env->argc == 1) + the_env->local_vars[node->nd_rest] = ary_new(); + else + the_env->local_vars[node->nd_rest] = + ary_new4(the_env->argc-i, the_env->argv+i); + } + } + return Qnil; + + case NODE_DEFN: + { + rb_add_method(the_class,node->nd_mid,node->nd_defn,node->nd_scope); + } + return Qnil; + + case NODE_DEFS: + { + VALUE recv = rb_eval(node->nd_recv); + + if (recv == Qnil) { + Fail("Can't define method \"%s\" for nil", + rb_id2name(node->nd_mid)); + } + rb_add_method(rb_single_class(recv), + node->nd_mid, node->nd_defn, MTH_METHOD); + } + return Qnil; + + case NODE_UNDEF: + { + rb_add_method(the_class, node->nd_mid, Qnil, MTH_UNDEF); + } + return Qnil; + + case NODE_ALIAS: + { + rb_alias(the_class, node->nd_new, node->nd_old); + } + return Qnil; + + case NODE_CLASS: + { + VALUE super, class; + + if (node->nd_super) { + super = rb_id2class(node->nd_super); + if (super == Qnil) { + Fail("undefined superclass %s", + rb_id2name(node->nd_super)); + } + } + else { + super = C_Object; + } + if (class = rb_id2class(node->nd_cname)) { + if (verbose) { + Warning("redefine class %s", rb_id2name(node->nd_cname)); + } + unliteralize(class); + } + + PUSH_ENV(); + the_class = (struct RClass*) + rb_define_class_id(node->nd_cname, super); + Qself = (VALUE)the_class; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_eval(node->nd_body); + } + POP_TAG(); + POP_ENV(); + if (state) JUMP_TAG(state); + } + return Qnil; + + case NODE_MODULE: + { + VALUE module; + + if (module = rb_id2class(node->nd_cname)) { + if (verbose) { + Warning("redefine module %s", rb_id2name(node->nd_cname)); + } + unliteralize(module); + } + + PUSH_ENV(); + the_class = (struct RClass*)rb_define_module_id(node->nd_cname); + Qself = (VALUE)the_class; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_eval(node->nd_body); + } + POP_TAG(); + POP_ENV(); + if (state) JUMP_TAG(state); + } + return Qnil; + + case NODE_INC: + { + struct RClass *module; + + module = (struct RClass*)rb_id2class(node->nd_modl); + if (module == Qnil) { + Fail("undefined module %s", rb_id2name(node->nd_modl)); + } + rb_include_module(the_class, module); + } + return Qnil; + + default: + Bug("unknown node type %d", node->type); + } + return Qnil; /* not reached */ +} + +VALUE +obj_responds_to(obj, msg) + VALUE obj; + struct RString *msg; +{ + ID id; + + if (FIXNUM_P(msg)) { + id = FIX2INT(msg); + } + else { + Check_Type(msg, T_STRING); + id = rb_intern(msg->ptr); + } + + if (rb_get_method_body(CLASS_OF(obj), id, 0, MTH_FUNC)) { + return TRUE; + } + return FALSE; +} + +void +rb_exit(status) + int status; +{ + last_val = INT2FIX(status); + JUMP_TAG(TAG_EXIT); +} + +VALUE +Fexit(obj, args) + VALUE obj, args; +{ + VALUE status; + + if (rb_scan_args(args, "01", &status) == 1) { + Need_Fixnum(status); + } + else { + status = INT2FIX(0); + } + last_val = status; + JUMP_TAG(TAG_EXIT); + + return Qnil; /* not reached */ +} + +void +rb_break() +{ + if (the_env->flags & DURING_ITERATE) { + JUMP_TAG(TAG_BREAK); + } + else { + Fatal("unexpected break"); + } +} + +void +rb_redo() +{ + if (the_env->flags & DURING_ITERATE) { + JUMP_TAG(TAG_REDO); + } + else { + Fatal("unexpected redo"); + } +} + +void +rb_retry() +{ + if (the_env->flags & DURING_RESQUE) { + JUMP_TAG(TAG_RETRY); + } + else { + Fatal("unexpected retry"); + } +} + +void +rb_fail(mesg) + VALUE mesg; +{ + char buf[BUFSIZ]; + + if (errat == Qnil || sourcefile) { + if (the_env->last_func) { + last_func = the_env->last_func; + } + sprintf(buf, "%s:%d", sourcefile, sourceline); + errat = str_new2(buf); + } + + if (mesg) { + if (RSTRING(mesg)->ptr[RSTRING(mesg)->len - 1] == '\n') { + errstr = mesg; + } + else { + errstr = Fstr_clone(mesg); + str_cat(errstr, "\n", 1); + } + } + + if (prot_tag->level == 0) error_print(); + JUMP_TAG(TAG_FAIL); +} + +VALUE +Ffail(self, args) + VALUE self, args; +{ + VALUE mesg; + + rb_scan_args(args, "01", &mesg); + + if (mesg) Check_Type(mesg, T_STRING); + rb_fail(mesg); + + return Qnil; /* not reached */ +} + +iterator_p() +{ + return ITERATOR_P(); +} + +VALUE +rb_yield(val) + VALUE val; +{ + struct BLOCK *block; + int state; + int go_out; + VALUE result; + int cnt; + + &go_out; + block = the_env->block; + if (!ITERATOR_P()) { + Fail("yield called out of iterator"); + } + + PUSH_ENV(); + block->env.prev = the_env->prev; + the_env = &(block->env); + the_env->flags = the_env->prev->flags; + if (block->var) { + asign(block->var, val); + } + + go_out = 0; + PUSH_TAG(); + switch (state = EXEC_TAG()) { + retry: + case 0: + if (block->body->type == NODE_CFUNC) { + the_env->flags |= DURING_ITERATE; + result = (*block->body->nd_cfnc)(val, block->body->nd_argc); + } + else { + result = rb_eval(block->body); + } + break; + case TAG_RETRY: + goto retry; + case TAG_CONTINUE: + break; + case TAG_BREAK: + case TAG_RETURN: + target_level = block->level; + state = IN_BLOCK|state; + default: + go_out++; + break; + } + POP_TAG(); + POP_ENV(); + if (go_out) JUMP_TAG(state); + + return result; +} + +static void +asign(lhs, val) + NODE *lhs; + VALUE val; +{ + switch (lhs->type) { + case NODE_GASGN: + rb_gvar_set(lhs->nd_entry, val); + break; + + case NODE_IASGN: + rb_ivar_set(lhs->nd_vid, val); + break; + + case NODE_LASGN: + if (the_env->local_vars == Qnil) + Bug("unexpected iterator variable asignment"); + the_env->local_vars[lhs->nd_cnt] = val; + break; + + case NODE_CASGN: + rb_const_set(lhs->nd_vid, val); + break; + + case NODE_CALL: + { + VALUE recv; + GC_LINK; + GC_PRO3(recv, rb_eval(lhs->nd_recv)); + if (lhs->nd_args->nd_head == Qnil) { + /* attr set */ + rb_funcall(recv, lhs->nd_mid, 1, val); + } + else { + /* array set */ + VALUE args; + + GC_PRO3(args, rb_eval(lhs->nd_args)); + RARRAY(args)->ptr[RARRAY(args)->len-1] = val; + rb_apply(recv, lhs->nd_mid, args); + } + GC_UNLINK; + } + break; + + default: + Bug("bug in iterator variable asignment"); + break; + } +} + +VALUE +rb_iterate(it_proc, data1, bl_proc, data2) + VALUE (*it_proc)(), (*bl_proc)(); + char *data1, *data2; +{ + int state; + VALUE retval; + NODE *node = NEW_CFUNC(bl_proc, data2); + struct BLOCK block; + + PUSH_ENV(); + block.level = tag_level; + block.var = Qnil; + block.body = node; + block.env = *the_env; + the_env->block = █ + PUSH_TAG(); + + state = EXEC_TAG(); + if (state == 0) { + the_env->iterator = 1; + retval = (*it_proc)(data1); + } + POP_TAG(); + POP_ENV(); + + freenode(node); + + switch (state) { + case 0: + break; + case IN_BLOCK|TAG_BREAK: + if (target_level != tag_level) { + JUMP_TAG(state); + } + retval = Qnil; + break; + case IN_BLOCK|TAG_RETURN: + if (target_level == tag_level) { + state = TAG_RETURN; + } + /* fall through */ + default: + JUMP_TAG(state); + } + + return retval; +} + +VALUE +rb_resque(b_proc, data1, r_proc, data2) + VALUE (*b_proc)(), (*r_proc)(); + char *data1, *data2; +{ + int state; + int go_out; + VALUE result; + + &go_out; + go_out = 0; + PUSH_TAG(); + switch (state = EXEC_TAG()) { + case 0: + retry_entry: + result = (*b_proc)(data1); + break; + + case TAG_FAIL: + if (r_proc) { + PUSH_TAG(); + state = EXEC_TAG(); + if (state == 0) { + the_env->flags |= DURING_RESQUE; + result = (*r_proc)(data2); + } + POP_TAG(); + switch (state) { + case TAG_RETRY: + goto retry_entry; + case 0: + break; + default: + go_out++; + break; + } + } + if (state == 0) { + errstr = errat = Qnil; + } + break; + + default: + break; + } + POP_TAG(); + if (go_out) JUMP_TAG(state); + + return result; +} + +VALUE +rb_ensure(b_proc, data1, e_proc, data2) + VALUE (*b_proc)(), (*e_proc)(); + char *data1, *data2; +{ + int state; + VALUE result; + + GC_LINK; + GC_PRO2(result); + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = (*b_proc)(data1); + } + POP_TAG(); + + (*e_proc)(data2); + if (state != 0) { + JUMP_TAG(state); + } + GC_UNLINK; + return result; +} + +struct st_table *new_idhash(); + +static void +rb_undefined(obj, id) + VALUE obj, id; +{ + VALUE desc = obj_as_string(obj); + + if (RSTRING(desc)->len > 160) { + desc = Fkrn_to_s(obj); + } + Fail("undefined method `%s' for \"%s\"(%s)", + rb_id2name(NUM2INT(id)), + RSTRING(desc)->ptr, + rb_class2name(CLASS_OF(obj))); +} + +static VALUE +rb_call(class, recv, mid, argc, argv, scope) + struct RClass *class; + VALUE recv, *argv; + int argc; + ID mid; + enum mth_scope scope; +{ + int state; + int go_out = 0; + int c = argc - 1; + NODE *body; + VALUE result; + + &go_out; + PUSH_ENV(); + the_env->flags |= DURING_CALL; + the_env->argc = argc; + the_env->argv = argv; + Qself = recv; + if (argv) argv[0] = recv; + if (the_env->iterator != 0) the_env->iterator++; + + if ((body = rb_get_method_body(class, mid, 1, scope)) == Qnil) { + rb_undefined(recv, mid); + } + + if (body->type == NODE_CFUNC) { + int len = body->nd_argc; + + if (len >= 0 && c != len) { + Fail("Wrong # of arguments for(%d for %d)", c, body->nd_argc); + } + + if (len == -2) { + result = (*body->nd_cfnc)(recv, ary_new4(argc-1, argv+1)); + } + else if (len == -1) { + result = (*body->nd_cfnc)(argc, argv); + } + else if (len >= 0) { + switch (c) { + case 0: + result = (*body->nd_cfnc)(recv); + break; + case 1: + result = (*body->nd_cfnc)(recv, argv[1]); + break; + case 2: + result = (*body->nd_cfnc)(recv, argv[1], argv[2]); + break; + case 3: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3]); + break; + case 4: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4]); + break; + case 5: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5]); + break; + case 6: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6]); + break; + case 7: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7]); + break; + case 8: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8]); + break; + case 9: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8], argv[9]); + break; + case 10: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8], argv[9], + argv[7], argv[8], argv[9], + argv[10]); + break; + case 11: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8], argv[9], + argv[7], argv[8], argv[9], + argv[10], argv[11]); + break; + case 12: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8], argv[9], + argv[7], argv[8], argv[9], + argv[10], argv[11], argv[12]); + break; + case 13: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8], argv[9], + argv[7], argv[8], argv[9], + argv[10], argv[11], argv[12], + argv[13]); + break; + case 14: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8], argv[9], + argv[7], argv[8], argv[9], + argv[10], argv[11], argv[12], + argv[13], argv[14]); + break; + case 15: + result = (*body->nd_cfnc)(recv, argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6], + argv[7], argv[8], argv[9], + argv[7], argv[8], argv[9], + argv[10], argv[11], argv[12], + argv[13], argv[14], argv[15]); + break; + default: + Fail("too many arguments(%d)", len); + break; + } + } + else { + Bug("bad argc(%d) specified for `%s(%s)'", + len, rb_class2name(class), rb_id2name(mid)); + } + } + else { + the_env->file = sourcefile; + the_env->line = sourceline; + the_env->local_vars = Qnil; + the_env->local_tbl = Qnil; + + PUSH_TAG(); + switch (state = EXEC_TAG()) { + case 0: + result = rb_eval(body); + break; + case TAG_CONTINUE: + Fatal("unexpected continue"); + break; + case TAG_BREAK: + Fatal("unexpected break"); + break; + case TAG_REDO: + Fatal("unexpected redo"); + break; + case TAG_RETRY: + Fatal("retry outside of protect clause"); + break; + case TAG_RETURN: + result = last_val; + break; + default: + go_out++; + } + POP_TAG(); + } + + POP_ENV(); + + if (go_out) JUMP_TAG(state); + + return result; +} + +VALUE +rb_apply(recv, mid, args) + VALUE recv, args; + ID mid; +{ + VALUE *argv; + int argc, i; + + argc = RARRAY(args)->len + 1; + argv = (VALUE*)alloca(sizeof(VALUE)*argc); + for (i=1;i<argc;i++) { + argv[i] = RARRAY(args)->ptr[i-1]; + } + argv[0] = Qnil; + return rb_call(CLASS_OF(recv), recv, mid, argc, argv, MTH_FUNC); +} + +VALUE +Fapply(recv, args) + VALUE recv, args; +{ + VALUE vid, rest; + ID mid; + + rb_scan_args(args, "1*", &vid, &rest); + if (TYPE(vid) == T_STRING) { + mid = rb_intern(RSTRING(vid)->ptr); + } + else { + mid = NUM2INT(vid); + } + return rb_apply(recv, mid, rest); +} + +#include <varargs.h> + +VALUE +rb_funcall(recv, mid, n, va_alist) + VALUE recv; + ID mid; + int n; + va_dcl +{ + va_list ar; + int argc; + VALUE *argv; + + if (n > 0) { + int i; + + argc = n + 1; + argv = (VALUE*)alloca(sizeof(VALUE)*argc); + + va_start(ar); + for (i=1;i<argc;i++) { + argv[i] = va_arg(ar, VALUE); + } + argv[0] = Qnil; + va_end(ar); + } + else { + argc = 1; + argv = Qnil; + } + + return rb_call(CLASS_OF(recv), recv, mid, argc, argv, MTH_FUNC); +} + +VALUE +Fcaller(obj, args) + VALUE obj, args; +{ + VALUE level, file, res, ary; + int lev; + struct ENVIRON *e; + + rb_scan_args(args, "01", &level); + if (level == Qnil) { + lev = 1; + } + else { + lev = FIX2UINT(level); + } + if (lev < 0) Fail("negative level: %d", lev); + + e = the_env; + + while (lev > 0) { + e = e->prev; + if (e == Qnil) Fail("no caller"); + if (!(e->flags & DURING_CALL)) continue; + lev--; + } + if (e->file == Qnil) Fail("initial frame"); + + GC_LINK; + GC_PRO3(file, str_new2(e->file)); + GC_PRO3(ary, e->argv?ary_new4(e->argc, e->argv):ary_new3(1, Qself)); + res = ary_new3(4, file, INT2FIX(e->line), + str_new2(rb_id2name(e->last_func)), ary); + GC_UNLINK; + + return res; +} + +int in_eval = 0; +extern int nerrs; + +VALUE +Feval(obj, src) + VALUE obj; + struct RString *src; +{ + VALUE result; + int state; + NODE *node; + char *oldsrc = sourcefile; + + Check_Type(src, T_STRING); + PUSH_TAG(); + PUSH_ENV(); + the_env->in_eval = 1; + node = eval_tree; + + if (the_env->prev) { + the_class = the_env->prev->current_module; + } + else { + the_class = (struct RClass*)C_Object; + } + + if ((state = EXEC_TAG()) == 0) { + lex_setsrc("(eval)", src->ptr, src->len); + eval_tree = Qnil; + yyparse(); + sourcefile = oldsrc; + if (nerrs == 0) + result = Eval(); + freenode(eval_tree); + } + eval_tree = node; + POP_ENV(); + POP_TAG(); + if (state) printf("exception in eval()\n"); + if (state) JUMP_TAG(state); + + if (nerrs > 0) { + VALUE mesg; + + GC_LINK; + GC_PRO3(mesg, errstr); + nerrs = 0; + errstr = str_new2("syntax error in eval():\n"); + str_cat(errstr, RSTRING(mesg)->ptr, RSTRING(mesg)->len); + rb_fail(errstr); + GC_UNLINK; + } + + return result; +} + +VALUE rb_load_path; + +char *dln_find_file(); + +static char* +find_file(file) + char *file; +{ + extern VALUE rb_load_path; + VALUE sep, vpath; + char *path, *found; + + if (file[0] == '/') return file; + + GC_LINK; + GC_PRO2(sep); GC_PRO2(vpath); + + if (rb_load_path) { + Check_Type(rb_load_path, T_ARRAY); + sep = str_new2(":"); + vpath = ary_join(rb_load_path, sep); + path = RSTRING(vpath)->ptr; + obj_free(sep); + sep = Qnil; + } + else { + path = Qnil; + } + + found = dln_find_file(file, path); + if (found == Qnil) Fail("No such file to load -- %s", file); + + if (vpath) obj_free(vpath); + GC_UNLINK; + + return found; +} + +VALUE +Fload(obj, fname) + VALUE obj; + struct RString *fname; +{ + extern VALUE TopSelf; + int state; + VALUE result; + NODE *node; + char *file; + + Check_Type(fname, T_STRING); + file = find_file(fname->ptr); + +#ifdef USE_DLN + { + static int rb_dln_init = 0; + extern char *rb_dln_argv0; + int len = strlen(file); + + if (len > 2 && file[len-1] == 'o' && file[len-2] == '.') { + if (rb_dln_init == 0 && dln_init(rb_dln_argv0) == -1) { + Fail("%s: %s", rb_dln_argv0, dln_strerror()); + } + + if (dln_load(file) == -1) + Fail(dln_strerror()); + + return TRUE; + } + } +#endif + + PUSH_TAG(); + PUSH_ENV(); + the_class = (struct RClass*)C_Object; + Qself = TopSelf; + the_env->in_eval = 1; + node = eval_tree; + state = EXEC_TAG(); + if (state == 0) { + eval_tree = Qnil; + rb_load_file(file); + if (nerrs == 0) { + result = Eval(); + } + freenode(eval_tree); + } + eval_tree = node; + POP_ENV(); + POP_TAG(); + if (nerrs > 0) { + rb_fail(errstr); + } + if (state) JUMP_TAG(state); + + return TRUE; +} + +static VALUE rb_loadfiles; + +Frequire(obj, fname) + VALUE obj; + struct RString *fname; +{ + char *file; + VALUE *p, *pend; + + Check_Type(fname, T_STRING); + file = find_file(fname->ptr); + + p = RARRAY(rb_loadfiles)->ptr; + pend = p+ RARRAY(rb_loadfiles)->len; + while (p < pend) { + Check_Type(*p, T_STRING); + if (strcmp(RSTRING(*p)->ptr, file) == 0) return FALSE; + } + Fary_push(rb_loadfiles, str_new2(file)); + + Fload(obj, fname); + return TRUE; +} + +char *getenv(); +char *index(); + +#ifndef RUBY_LIB +#define RUBY_LIB "/usr/local/lib/ruby:." +#endif + +#define RUBY_LIB_SEP ':' + +static void +addpath(path) + char *path; +{ + char *p, *s; + + if (path == Qnil) return; + + p = s = path; + while (*p) { + while (*p == RUBY_LIB_SEP) p++; + if (s = index(p, RUBY_LIB_SEP)) { + Fary_push(rb_load_path, str_new(p, (int)(s-p))); + p = s + 1; + } + else { + Fary_push(rb_load_path, str_new2(p)); + break; + } + } +} + +Init_load() +{ + extern VALUE C_Kernel; + extern VALUE rb_check_str(); + char *path; + + rb_define_variable("$LOAD_PATH", &rb_load_path, Qnil, rb_check_str); + rb_load_path = ary_new(); + rb_define_variable("$LOAD_FILES", &rb_load_path, Qnil, rb_readonly_hook); + rb_loadfiles = ary_new(); + addpath(getenv("RUBYLIB")); + addpath(RUBY_LIB); + + rb_define_func(C_Kernel, "load", Fload, 1); + rb_define_func(C_Kernel, "require", Frequire, 1); +} |