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 /string.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 'string.c')
-rw-r--r-- | string.c | 1552 |
1 files changed, 1552 insertions, 0 deletions
diff --git a/string.c b/string.c new file mode 100644 index 0000000000..57198e4e87 --- /dev/null +++ b/string.c @@ -0,0 +1,1552 @@ +/************************************************ + + string.c - + + $Author: matz $ + $Date: 1994/06/27 15:48:44 $ + created at: Mon Aug 9 17:12:58 JST 1993 + + Copyright (C) 1994 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include "re.h" + +#include <stdio.h> +#include <ctype.h> + +VALUE C_String; + +#define STRLEN(s) RSTRING(s)->len + +VALUE +str_new(ptr, len) + char *ptr; + UINT len; +{ + NEWOBJ(str, struct RString); + OBJSETUP(str, C_String, T_STRING); + + str->len = len; + str->ptr = ALLOC_N(char,len+1); + if (ptr) { + memmove(str->ptr, ptr, len); + } + str->ptr[len] = '\0'; + str->orig = Qnil; + return (VALUE)str; +} + +VALUE +str_new2(ptr) + char *ptr; +{ + return str_new(ptr, strlen(ptr)); +} + +VALUE +str_new3(str) + struct RString *str; +{ + NEWOBJ(str2, struct RString); + OBJSETUP(str2, C_String, T_STRING); + + str2->len = str->len; + str2->ptr = str->ptr; + str2->orig = str; + + return (VALUE)str2; +} + +#define as_str(str) (struct RString*)obj_as_string(str) + +static ID pr_str = Qnil; + +VALUE +obj_as_string(obj) + VALUE obj; +{ + VALUE str; + + if (TYPE(obj) == T_STRING) { + return obj; + } + str = rb_funcall(obj, pr_str, 0); + if (TYPE(str) != T_STRING) + return Fkrn_to_s(obj); + return str; +} + +VALUE +Fstr_clone(str) + struct RString *str; +{ + VALUE obj; + + if (str->orig) + obj = str_new3(str->orig); + else + obj = str_new(str->ptr, str->len); + CLONESETUP(obj, str); + return obj; +} + +static VALUE +Fstr_new(class, str) + VALUE class; + struct RString *str; +{ + Check_Type(str, T_STRING); + { + NEWOBJ(str2, struct RString); + OBJSETUP(str2, class, T_STRING); + + str2->len = str->len; + str2->ptr = ALLOC_N(char, str->len+1); + if (str2->ptr) { + memmove(str2->ptr, str->ptr, str->len); + } + str2->ptr[str->len] = '\0'; + str2->orig = Qnil; + return (VALUE)str2; + } +} + +static VALUE +Fstr_length(str) + struct RString *str; +{ + return INT2FIX(str->len); +} + +VALUE +Fstr_plus(str1, str2) + struct RString *str1, *str2; +{ + struct RString *str3; + + GC_LINK; + GC_PRO3(str2, as_str(str2)); + str3 = (struct RString*)str_new(0, str1->len+str2->len); + memcpy(str3->ptr, str1->ptr, str1->len); + memcpy(str3->ptr+str1->len, str2->ptr, str2->len); + str3->ptr[str3->len] = '\0'; + GC_UNLINK; + + return (VALUE)str3; +} + +VALUE +Fstr_times(str, times) + struct RString *str; + VALUE times; +{ + struct RString *str2; + int i; + + times = NUM2INT(times); + + str2 = (struct RString*)str_new(0, str->len*times); + for (i=0; i<times; i++) { + memmove(str2->ptr+(i*str->len), str->ptr, str->len); + } + str2->ptr[str2->len] = '\0'; + + return (VALUE)str2; +} + +extern VALUE C_Range; + +static VALUE +Fstr_dot2(left, right) + VALUE left, right; +{ + extern VALUE C_Range; + VALUE str; + + Check_Type(right, T_STRING); + str = range_new(C_Range, left, right); + return str; +} + +VALUE +str_substr(str, start, len) + struct RString *str; + int start, len; +{ + struct RString *str2; + + if (start < 0) { + start = str->len + start; + } + if (str->len <= start) { + Fail("index %d out of range [0..%d]", start, str->len-1); + } + if (len < 0) { + Fail("Negative length %d", len); + } + + str2 = (struct RString*)str_new(str->ptr+start, len); + + return (VALUE)str2; +} + +VALUE +str_subseq(str, beg, end) + struct RString *str; + int beg, end; +{ + int len; + + if (beg < 0) { + beg = str->len + beg; + if (beg < 0) beg = 0; + } + if (end < 0) { + end = str->len + end; + if (end < 0) end = 0; + } + + if (beg > end) { + int tmp; + + if (verbose) { + Warning("start %d is bigger than end %d", beg, end); + } + tmp = beg; beg = end; end = tmp; + } + + if (beg >= str->len) { + return str_new(0, 0); + } + if (str->len < end) { + end = str->len; + } + + len = end - beg + 1; + if (len < 0) { + Fail("end %d too small(size %d)", end, str->len); + } + + return str_substr(str, beg, len); +} + +extern VALUE ignorecase; + +void +str_modify(str) + struct RString *str; +{ + if (str->orig == Qnil) return; + str->ptr = ALLOC_N(char, str->len+1); + if (str->ptr) { + memcpy(str->ptr, str->orig->ptr, str->len+1); + } + str->orig = Qnil; +} + +VALUE +str_grow(str, len) + struct RString *str; + UINT len; +{ + str_modify(str); + if (len > 0) { + REALLOC_N(str->ptr, char, len + 1); + str->len = len; + str->ptr[len] = '\0'; /* sentinel */ + } + return (VALUE)str; +} + +VALUE +str_cat(str, ptr, len) + struct RString *str; + char *ptr; + UINT len; +{ + str_modify(str); + + if (len > 0) { + REALLOC_N(str->ptr, char, str->len + len + 1); + if (ptr) + memmove(str->ptr + str->len, ptr, len); + str->len += len; + str->ptr[str->len] = '\0'; /* sentinel */ + } + return (VALUE)str; +} + +static VALUE +Fstr_concat(str1, str2) + struct RString *str1, *str2; +{ + str2 = as_str(str2); + str_cat(str1, str2->ptr, str2->len); + return (VALUE)str1; +} + +static char +str_next(s) + char *s; +{ + char c = *s; + + /* control code */ + if (c < ' ') return 0; + + /* numerics */ + if ('0' <= c && c < '9') (*s)++; + else if (c == '9') { + *s = '0'; + return '1'; + } + /* small alphabets */ + else if ('a' <= c && c < 'z') (*s)++; + else if (c == 'z') { + return *s = 'a'; + } + /* capital alphabets */ + else if ('A' <= c && c < 'Z') (*s)++; + else if (c == 'Z') { + return *s = 'A'; + } + return 0; +} + +static VALUE +Fstr_next(orig) + struct RString *orig; +{ + struct RString *str, *str2; + char *sbeg, *s; + char c = -1; + + GC_LINK; + GC_PRO3(str, (struct RString*)str_new(orig->ptr, orig->len)); + + sbeg = str->ptr; s = sbeg + str->len - 1; + + while (sbeg <= s) { + if (isalnum(*s) && (c = str_next(s)) == Qnil) break; + s--; + } + if (s < sbeg && c != -1) { + GC_PRO3(str2, (struct RString*)str_new(0, str->len+1)); + str2->ptr[0] = c; + memmove(str2->ptr+1, str->ptr, str->len); + obj_free(str); + str = str2; + } + GC_UNLINK; + + return (VALUE)str; +} + +static +str_hash(str) + struct RString *str; +{ + int len = str->len; + unsigned char *p = (unsigned char*)str->ptr; + int key = 0; + + if (ignorecase) { + while (len--) { + key = key*65599 + *p; + } + } + else { + while (len--) { + key = key*65599 + toupper(*p); + } + } + return key; +} + +static VALUE +Fstr_hash(str) + VALUE str; +{ + int key = str_hash(str); + return INT2FIX(key); +} + +#define min(a,b) (((a)>(b))?(b):(a)) + +int +str_cmp(str1, str2) + struct RString *str1, *str2; +{ + UINT len; + int retval; + + if (ignorecase != Qnil) { + return str_cicmp(str1, str2); + } + + len = min(str1->len, str2->len); + retval = memcmp(str1->ptr, str2->ptr, len); + if (retval == 0) { + return str1->ptr[len] - str2->ptr[len]; + } + return retval; +} + +static VALUE +Fstr_equal(str1, str2) + struct RString *str1, *str2; +{ + if (TYPE(str2) != T_STRING) + return FALSE; + + if (str1->len == str2->len + && str_cmp(str1, str2) == 0) { + return TRUE; + } + return FALSE; +} + +static VALUE +Fstr_cmp(str1, str2) + VALUE str1, str2; +{ + int result; + + Check_Type(str2, T_STRING); + result = str_cmp(str1, str2); + return INT2FIX(result); +} + +Regexp * make_regexp(); +VALUE Freg_match(); + +static VALUE +Fstr_match(this, other) + struct RString *this, *other; +{ + VALUE reg; + int start; + + switch (TYPE(other)) { + case T_REGEXP: + return Freg_match(other, this); + case T_STRING: + reg = re_regcomp(other); + start = research(reg, this, 0, ignorecase); + if (start == -1) { + return FALSE; + } + return INT2FIX(start); + default: + Fail("type mismatch"); + break; + } +} + +static VALUE +Fstr_match2(str) + struct RString *str; +{ + extern VALUE rb_lastline; + VALUE reg; + int start; + + if (TYPE(rb_lastline) != T_STRING) + Fail("$_ is not a string"); + + reg = re_regcomp(str); + start = research(reg, rb_lastline, 0, ignorecase); + if (start == -1) { + return Qnil; + } + return INT2FIX(start); +} + +static int +str_index(str, sub, offset) + struct RString *str, *sub; + int offset; +{ + char *s, *e, *p; + int len; + + if (str->len - offset < sub->len) return -1; + s = str->ptr+offset; + p = sub->ptr; + len = sub->len; + e = s + str->len - len + 1; + while (s < e) { + if (*s == *(sub->ptr) && memcmp(s, p, len) == 0) { + return (s-(str->ptr)); + } + s++; + } + return -1; +} + +static VALUE +Fstr_index(str, args) + struct RString *str; + VALUE args; +{ + struct RString *sub; + VALUE initpos; + int pos; + + if (rb_scan_args(args, "11", &sub, &initpos) == 2) { + pos = NUM2INT(initpos); + } + else { + pos = 0; + } + + switch (TYPE(sub)) { + case T_REGEXP: + pos = research(sub, str, pos, ignorecase); + break; + + case T_STRING: + pos = str_index(str, sub, pos); + break; + + default: + Fail("Type mismatch: %s given", rb_class2name(CLASS_OF(sub))); + } + + if (pos == -1) return Qnil; + return INT2FIX(pos); +} + +static VALUE +Fstr_rindex(str, args) + struct RString *str; + VALUE args; +{ + struct RString *sub; + VALUE initpos; + int pos, len; + char *s, *sbeg, *t; + + if (rb_scan_args(args, "11", &sub, &initpos) == 2) { + pos = NUM2INT(initpos); + if (pos >= str->len) pos = str->len; + } + else { + pos = str->len; + } + + Check_Type(sub, T_STRING); + if (pos > str->len) return Qnil; /* substring longer than string */ + sbeg = str->ptr; s = s + pos - sub->len; + t = sub->ptr; + len = sub->len; + while (sbeg <= s) { + if (*s == *t && memcmp(s, t, len) == 0) { + return INT2FIX(s - sbeg); + } + s--; + } + return Qnil; +} + +static VALUE +Fstr_aref_internal(str, indx) + struct RString *str; + VALUE indx; +{ + int idx; + + switch (TYPE(indx)) { + case T_FIXNUM: + idx = FIX2UINT(indx); + + if (idx < 0) { + idx = str->len + idx; + } + if (idx < 0 || str->len <= idx) { + Fail("index %d out of range [0..%d]", idx, str->len-1); + } + return (VALUE)INT2FIX(str->ptr[idx] & 0xff); + + case T_REGEXP: + if (Fstr_index(str, indx)) + return re_last_match(0); + return Qnil; + + case T_STRING: + if (str_index(str, indx, 0)) return indx; + return Qnil; + + default: + /* check if indx is Range */ + if (obj_is_kind_of(indx, C_Range)) { + int beg, end; + + beg = rb_iv_get(indx, "start"); beg = NUM2INT(beg); + end = rb_iv_get(indx, "end"); end = NUM2INT(end); + if (beg > end) { + int tmp; + + if (verbose) { + Warning("start %d is bigger than end %d", beg, end); + } + tmp = beg; beg = end; end = tmp; + } + + return str_subseq(str, beg, end); + } + Fail("Invalid index for string"); + } +} + +static VALUE +Fstr_aref(str, args) + struct RString *str; + VALUE args; +{ + VALUE arg1, arg2; + + if (rb_scan_args(args, "11", &arg1, &arg2) == 2) { + return str_substr(str, NUM2INT(arg1), NUM2INT(arg2)); + } + return Fstr_aref_internal(str, arg1); +} + +static void +str_replace(str, beg, len, val) + struct RString *str, *val; + int beg, len; +{ + if (len < val->len) { + /* expand string */ + REALLOC_N(str->ptr, char, str->len+val->len-len+1); + } + + memmove(str->ptr+beg+val->len, str->ptr+beg+len, str->len-(beg+len)); + memmove(str->ptr+beg, val->ptr, val->len); + str->len += val->len - len; + str->ptr[str->len] = '\0'; +} + +static void +str_replace2(str, beg, end, val) + struct RString *str, *val; + int beg, end; +{ + int len; + + if (beg < 0) { + beg = str->len + beg; + } + if (str->len <= beg) { + Fail("start %d too big", beg); + } + if (end < 0) { + end = str->len + end; + } + if (end < 0 || str->len <= end) { + Fail("end %d too big", end); + } + len = end - beg + 1; /* length of substring */ + if (len < 0) { + Fail("end %d too small", end); + } + + str_replace(str, beg, len, val); +} + +static VALUE +str_sub(str, pat, val, once) + struct RString *str; + struct RRegexp *pat; + VALUE val; + int once; +{ + VALUE sub; + int beg, end, offset, n; + + GC_LINK; + GC_PRO2(sub); + for (offset=0, n=0; + (beg=research(pat, str, offset, ignorecase)) >= 0; + offset=RREGEXP(pat)->ptr->regs.start[0]+STRLEN(val)) { + end = RREGEXP(pat)->ptr->regs.end[0]-1; + sub = re_regsub(val); + str_replace2(str, beg, end, sub); + n++; + if (once) break; + } + GC_UNLINK; + if (n == 0) return Qnil; + return INT2FIX(n); +} + +static VALUE +Fstr_aset_internal(str, indx, val) + struct RString *str; + VALUE indx, val; +{ + int idx, beg, end, offset; + + switch (TYPE(indx)) { + case T_FIXNUM: + idx = NUM2INT(indx); + if (idx < 0) { + idx = str->len + idx; + } + if (idx < 0 || str->len <= idx) { + Fail("index %d out of range [0..%d]", idx, str->len-1); + } + str->ptr[idx] = FIX2UINT(val) & 0xff; + return val; + + case T_REGEXP: + str_sub(str, indx, val, 0); + return val; + + case T_STRING: + for (offset=0; + (beg=str_index(str, indx, offset)) >= 0; + offset=beg+STRLEN(val)) { + end = beg + STRLEN(indx) - 1; + str_replace2(str, beg, end, val); + } + if (offset == 0) Fail("Not a substring"); + return val; + + default: + /* check if indx is Range */ + if (obj_is_kind_of(indx, C_Range)) { + Check_Type(val, T_STRING); + + beg = rb_iv_get(indx, "start"); beg = NUM2INT(beg); + end = rb_iv_get(indx, "end"); end = NUM2INT(end); + if (beg > end) { + int tmp; + + if (verbose) { + Warning("start %d is bigger than end %d", beg, end); + } + tmp = beg; beg = end; end = tmp; + } + + str_replace2(str, beg, end, val); + return val; + } + Fail("Invalid index for string"); + } +} + +static VALUE +Fstr_aset(str, args) + struct RString *str; + VALUE args; +{ + VALUE arg1, arg2, arg3; + + str_modify(str); + + if (rb_scan_args(args, "21", &arg1, &arg2, &arg3) == 3) { + int beg, len; + + Check_Type(arg3, T_STRING); + + beg = NUM2INT(arg1); + if (beg < 0) { + beg = str->len + beg; + if (beg < 0) Fail("start %d too small", beg); + } + len = NUM2INT(arg2); + if (len < 0) Fail("length %d too small", len); + if (beg + len > str->len) { + len = str->len - beg; + } + str_replace(str, beg, len, arg3); + return arg3; + } + return Fstr_aset_internal(str, arg1, arg2); +} + +static VALUE +Fstr_sub_internal(str, pat, val, once) + VALUE str, pat, val; + int once; +{ + VALUE reg, result; + + Check_Type(val, T_STRING); + str_modify(str); + + switch (TYPE(pat)) { + case T_REGEXP: + return str_sub(str, pat, val, once); + + case T_STRING: + reg = re_regcomp(pat); + result = str_sub(str, reg, val, once); + return result; + + default: + /* type failed */ + Check_Type(pat, T_REGEXP); + } + return Qnil; /* not reached */ +} + +static VALUE +Fstr_sub(str, pat, val) + VALUE str, pat, val; +{ + return Fstr_sub_internal(str, pat, val, 1); +} + +static VALUE +Fstr_gsub(str, pat, val) + VALUE str, pat, val; +{ + return Fstr_sub_internal(str, pat, val, 0); +} + +extern VALUE rb_lastline; + +static VALUE +Fsub(obj, pat, val) + VALUE obj, pat, val; +{ + Check_Type(rb_lastline, T_STRING); + return Fstr_sub_internal(rb_lastline, pat, val, 1); +} + +static VALUE +Fgsub(obj, pat, val) + VALUE obj, pat, val; +{ + Check_Type(rb_lastline, T_STRING); + return Fstr_sub_internal(rb_lastline, pat, val, 0); +} + +static VALUE +Fstr_reverse(str) + struct RString *str; +{ + VALUE obj = str_new(0, str->len); + char *s, *e, *p; + + s = str->ptr; e = s + str->len - 1; + p = RSTRING(obj)->ptr; + + while (e >= s) { + *p++ = *e--; + } + + return obj; +} + +static VALUE +Fstr_to_i(str) + struct RString *str; +{ + return str2inum(str->ptr, 10); +} + +static VALUE +Fstr_to_f(str) + struct RString *str; +{ + double atof(); + double f = atof(str->ptr); + + return float_new(f); +} + +static VALUE +Fstr_to_s(str) + VALUE str; +{ + return str; +} + +static VALUE +Fstr_inspect(str) + struct RString *str; +{ + char buf[160]; + char *p, *pend; + char *b, *bend; + +#define CHECK(n) if (b+n > bend) break; + + p = str->ptr; pend = p + str->len; + b = buf; bend = b + sizeof buf - (str->len>150?4:2); + *b++ = '"'; + while (p < pend) { + char c = *p++; + if (isprint(c)) { + CHECK(1); + *b++ = c; + } + else if (ismbchar(c)) { + CHECK(2); + *b++ = c; + *b++ = *p++; + } + else if (c == '\n') { + CHECK(2); + *b++ = '\\'; + *b++ = 'n'; + } + else if (c == '\r') { + CHECK(2); + *b++ = '\\'; + *b++ = 'r'; + } + else if (c == '\t') { + CHECK(2); + *b++ = '\\'; + *b++ = 't'; + } + else if (c == '\f') { + CHECK(2); + *b++ = '\\'; + *b++ = 'f'; + } + else if (c == '\13') { + CHECK(2); + *b++ = '\\'; + *b++ = 'v'; + } + else if (c == '\a') { + CHECK(2); + *b++ = '\\'; + *b++ = 'a'; + } + else if (c == 033) { + CHECK(2); + *b++ = '\\'; + *b++ = 'e'; + } + else if (iscntrl(c)) { + CHECK(2); + *b++ = '^'; + *b++ = c; + } + else { + CHECK(1); + *b++ = c; + } + } + *b++ = '"'; + if (p < pend) { + bend = buf + sizeof buf; + while (b < bend) { + *b++ = '.'; + } + } + return str_new(buf, b - buf); +} + +static VALUE +Fstr_toupper(str) + struct RString *str; +{ + char *s; + int i; + + str_modify(str); + s = str->ptr; + for (i=0; i < str->len; i++) { + if (islower(*s)) { + *s = toupper(*s); + } + *s++; + } + + return (VALUE)str; +} + +static VALUE +Fstr_tolower(str) + struct RString *str; +{ + char *s; + int i; + + str_modify(str); + s = str->ptr; + for (i=0; i < str->len; i++) { + if (isupper(*s)) { + *s = tolower(*s); + } + *s++; + } + + return (VALUE)str; +} + +static VALUE +Fstr_ucfirst(str) + struct RString *str; +{ + char *s, *send; + int i; + + str_modify(str); + s = str->ptr; send = s + str->len; + if (islower(*s)) + *s = toupper(*s); + return (VALUE)str; +} + +static VALUE +Fstr_lcfirst(str) + struct RString *str; +{ + char *s, *send; + int i; + + str_modify(str); + s = str->ptr; send = s + str->len; + if (isupper(*s)) + *s = tolower(*s); + return (VALUE)str; +} + +struct tr { + int last, max; + char *p, *pend; +} trsrc, trrepl; + +static +trnext(t) + struct tr *t; +{ + while (t->p < t->pend) { + if (t->max) { + if (++t->last < t->max) + return t->last; + t->last = t->max = 0; + } + else if (t->last && *t->p == '-') { + t->p++; + t->max = *t->p; + if (t->p == t->pend) { + t->p--; + return '-'; + } + else if (t->max < t->last) { + t->last = t->max - 1; + return '-'; + } + continue; + } + return t->last = *t->p++; + } + return -1; +} + +static VALUE +Fstr_tr(str, src, repl) + struct RString *str, *src, *repl; +{ + struct tr trsrc, trrepl; + char trans[256]; + int cflag = 0; + int i, c, save; + char *s, *send, *t; + + Check_Type(src, T_STRING); + trsrc.p = src->ptr; trsrc.pend = trsrc.p + src->len; + if (src->len > 2 && src->ptr[0] == '^') { + cflag++; + trsrc.p++; + } + Check_Type(repl, T_STRING); + trrepl.p = repl->ptr; trrepl.pend = trrepl.p + repl->len; + trsrc.last = trrepl.last = trsrc.max = trrepl.max = 0; + + for (i=0; i<256; i++) { + trans[i] = cflag ? 1 : 0; + } + + while ((c = trnext(&trsrc)) >= 0) { + trans[c & 0xff] = cflag ? 0 : 1; + } + + c = 0; + for (i=0; i<256; i++) { + if (trans[i] == 0) { + trans[i] = i; + } + else { + c = trnext(&trrepl); + if (c == -1) { + trans[i] = trrepl.last; + } + else { + trans[i] = c; + } + } + } + + str_modify(str); + + t = s = str->ptr; send = s + str->len; + while (s < send) { + c = *s++ & 0xff; + c = trans[c] & 0xff; + *t++ = c; + } + *t = '\0'; + str->len = t - str->ptr; + + return (VALUE)str; +} + +static void +tr_setup_table(str, table) + struct RString *str; + char table[256]; +{ + struct tr tr; + int i, cflag = 0; + char c; + + tr.p = str->ptr; tr.pend = tr.p + str->len; + tr.last = tr.max = 0; + if (str->len > 2 && str->ptr[0] == '^') { + cflag++; + tr.p++; + } + + for (i=0; i<256; i++) { + table[i] = cflag ? 1 : 0; + } + while ((c = trnext(&tr)) >= 0) { + table[c & 0xff] = cflag ? 0 : 1; + } +} + +static VALUE +Fstr_delete(str1, str2) + struct RString *str1, *str2; +{ + char *s, *send, *t; + char squeez[256]; + + Check_Type(str2, T_STRING); + tr_setup_table(str2, squeez); + + str_modify(str1); + + s = t = str1->ptr; + send = s + str1->len; + while (s < send) { + if (!squeez[*s & 0xff]) { + *t++ = *s; + } + s++; + } + *t = '\0'; + str1->len = t - str1->ptr; + + return (VALUE)str1; +} + +static VALUE +tr_squeeze(str1, str2) + struct RString *str1, *str2; +{ + char squeez[256]; + char *s, *send, *t; + char c, save; + + if (str2) { + tr_setup_table(str2, squeez); + } + else { + int i; + + for (i=0; i<256; i++) { + squeez[i] = 1; + } + } + + str_modify(str1); + + s = t = str1->ptr; + send = s + str1->len; + save = -1; + while (s < send) { + c = *s++ & 0xff; + if (c != save || !squeez[c & 0xff]) { + *t++ = save = c; + } + } + *t = '\0'; + str1->len = t - str1->ptr; + + return (VALUE)str1; +} + +static VALUE +Fstr_squeeze(str1, args) + VALUE str1; + VALUE *args; +{ + VALUE str2; + + rb_scan_args(args, "01", &str2); + if (str2) { + Check_Type(str2, T_STRING); + } + return tr_squeeze(str1, str2); +} + +static VALUE +Fstr_tr_s(str, src, repl) + VALUE str, src, repl; +{ + Check_Type(src, T_STRING); + Check_Type(repl, T_STRING); + Fstr_tr(str, src, repl); + tr_squeeze(str, repl); + return str; +} + +static VALUE +Fstr_split(str, args) + struct RString *str; + VALUE args; +{ + extern VALUE FS; + struct RRegexp *spat; + VALUE limit; + char char_sep = 0; + int beg, end, lim, i; + VALUE result, tmp; + + rb_scan_args(args, "02", &spat, &limit); + if (limit) { + lim = NUM2INT(limit); + i = 1; + } + + if (spat == Qnil) { + if (FS) { + spat = (struct RRegexp*)FS; + goto fs_set; + } + char_sep = ' '; + } + else { + switch (TYPE(spat)) { + case T_STRING: + fs_set: + if (STRLEN(spat) == 1) { + char_sep = RSTRING(spat)->ptr[0]; + } + else { + spat = (struct RRegexp*)re_regcomp(spat); + } + break; + case T_REGEXP: + break; + default: + Fail("split(): bad separator"); + } + } + + GC_LINK; + GC_PRO(spat); + GC_PRO3(result, ary_new()); + + beg = 0; + if (char_sep != 0) { + char *ptr = str->ptr; + int len = str->len; + char *eptr = ptr + len; + + if (char_sep == ' ') { /* AWK emulation */ + int skip = 1; + + for (end = beg = 0; ptr<eptr; ptr++) { + if (skip) { + if (isspace(*ptr)) { + beg++; + } + else { + end = beg+1; + skip = 0; + } + } + else { + if (isspace(*ptr)) { + Fary_push(result, str_substr(str, beg, end-beg)); + if (limit && lim <= ++i) break; + skip = 1; + beg = end + 1; + } + else { + end++; + } + } + } + } + else { + for (end = beg = 0; ptr<eptr; ptr++) { + if (*ptr == char_sep) { + Fary_push(result, str_substr(str, beg, end-beg)); + if (limit && lim <= ++i) break; + beg = end + 1; + } + end++; + } + } + } + else { + int start = beg; + int last_null = 0; + int idx; + +#define LMATCH spat->ptr->regs.start +#define RMATCH spat->ptr->regs.end + + while ((end = research(spat, str, start, ignorecase)) >= 0) { + if (start == end && LMATCH[0] == RMATCH[0]) { + if (last_null == 1) { + if (ismbchar(str->ptr[beg])) + Fary_push(result, str_substr(str, beg, 2)); + else + Fary_push(result, str_substr(str, beg, 1)); + beg = start; + if (limit && lim <= ++i) break; + } + else { + start += ismbchar(str->ptr[start])?2:1; + last_null = 1; + continue; + } + } + else { + Fary_push(result, str_substr(str, beg, end-beg)); + beg = start = RMATCH[0]; + if (limit && lim <= ++i) break; + } + last_null = 0; + + for (idx=1; idx < 10; idx++) { + if (LMATCH[idx] == -1) break; + if (LMATCH[idx] == RMATCH[idx]) + tmp = str_new(0, 0); + else + tmp = str_subseq(str, LMATCH[idx], RMATCH[idx]-1); + Fary_push(result, tmp); + if (limit && lim <= ++i) break; + } + + } + } + if (str->len > beg) { + Fary_push(result, str_subseq(str, beg, -1)); + } + else if (str->len == beg) { + Fary_push(result, str_new(0, 0)); + } + + GC_UNLINK; + return result; +} + +static VALUE +Fstr_each(str) + struct RString* str; +{ + extern VALUE RS; + int newline; + int rslen; + char *p = str->ptr, *pend = p + str->len, *s; + + if (RS == Qnil) { + rb_yield(str); + return (VALUE)str; + } + + rslen = RSTRING(RS)->len; + if (rslen == 0) { + newline = '\n'; + } + else { + newline = RSTRING(RS)->ptr[rslen-1]; + } + + for (s = p, p += rslen; p < pend; p++) { + if (rslen == 0 && *p == '\n') { + if (*(p+1) != '\n') continue; + while (*p == '\n') p++; + p--; + } + if (*p == newline && + (rslen <= 1 || + memcmp(RSTRING(RS)->ptr, p-rslen+1, rslen) == 0)) { + rb_lastline = str_new(s, p - s + 1); + rb_yield(rb_lastline); + s = p + 1; + } + } + + if (s != pend) { + rb_lastline = str_new(s, p - s); + rb_yield(rb_lastline); + } + + return (VALUE)str; +} + +static VALUE +Fstr_each_byte(str) + struct RString* str; +{ + int i; + + for (i=0; str->len; i++) { + rb_yield(str->ptr[i] & 0xff); + } + return (VALUE)str; +} + +static VALUE +Fstr_chop(str) + struct RString *str; +{ + int result; + + str_modify(str); + + str->len--; + str->ptr[str->len] = '\0'; + + return (VALUE)str; +} + +static VALUE +Fstr_strip(str) + struct RString *str; +{ + char *s, *t, *e; + + s = str->ptr; + e = t = s + str->len; + /* remove spaces at head */ + while (s < t && isspace(*s)) s++; + + /* remove trailing spaces */ + t--; + while (s <= t && isspace(*t)) t--; + t++; + + if (s > str->ptr || t < e) { + str_modify(str); + return str_new(s, t-s); + } + return (VALUE)str; +} + +static VALUE +Fstr_hex(str) + struct RString *str; +{ + return str2inum(str->ptr, 16); +} + +static VALUE +Fstr_oct(str) + struct RString *str; +{ + return str2inum(str->ptr, 8); +} + +static VALUE +Fstr_crypt(str, salt) + struct RString *str, *salt; +{ + Check_Type(salt, T_STRING); + if (salt->len < 2) + Fail("salt too short(need 2 byte)"); + return str_new2(crypt(str->ptr, salt->ptr)); +} + +static VALUE +Fstr_intern(str) + struct RString *str; +{ + if (strlen(str->ptr) != str->len) + Fail("string contains `\0'"); + + return rb_intern(str->ptr)|FIXNUM_FLAG; +} + +extern VALUE C_Kernel; +extern VALUE M_Comparable; +extern VALUE M_Enumerable; + +Init_String() +{ + C_String = rb_define_class("String", C_Object); + rb_include_module(C_String, M_Comparable); + rb_include_module(C_String, M_Enumerable); + rb_define_single_method(C_String, "new", Fstr_new, 1); + rb_define_method(C_String, "clone", Fstr_clone, 0); + rb_define_method(C_String, "<=>", Fstr_cmp, 1); + rb_define_method(C_String, "==", Fstr_equal, 1); + rb_define_method(C_String, "hash", Fstr_hash, 0); + rb_define_method(C_String, "+", Fstr_plus, 1); + rb_define_method(C_String, "*", Fstr_times, 1); + rb_define_method(C_String, "..", Fstr_dot2, 1); + rb_define_method(C_String, "[]", Fstr_aref, -2); + rb_define_method(C_String, "[]=", Fstr_aset, -2); + rb_define_method(C_String, "length", Fstr_length, 0); + rb_define_method(C_String, "=~", Fstr_match, 1); + rb_define_method(C_String, "~", Fstr_match2, 0); + rb_define_method(C_String, "next", Fstr_next, 0); + rb_define_method(C_String, "index", Fstr_index, -2); + rb_define_method(C_String, "rindex", Fstr_rindex, -2); + + rb_define_method(C_String, "to_i", Fstr_to_i, 0); + rb_define_method(C_String, "to_f", Fstr_to_f, 0); + rb_define_method(C_String, "to_s", Fstr_to_s, 0); + rb_define_method(C_String, "_inspect", Fstr_inspect, 0); + + rb_define_method(C_String, "toupper", Fstr_toupper, 0); + rb_define_alias(C_String, "uc", "toupper"); + rb_define_method(C_String, "tolower", Fstr_tolower, 0); + rb_define_alias(C_String, "lc", "tolower"); + rb_define_method(C_String, "ucfirst", Fstr_ucfirst, 0); + rb_define_method(C_String, "lcfirst", Fstr_lcfirst, 0); + rb_define_method(C_String, "hex", Fstr_hex, 0); + rb_define_method(C_String, "oct", Fstr_oct, 0); + rb_define_method(C_String, "split", Fstr_split, -2); + rb_define_method(C_String, "reverse", Fstr_reverse, 0); + rb_define_method(C_String, "concat", Fstr_concat, 1); + rb_define_method(C_String, "crypt", Fstr_crypt, 1); + rb_define_method(C_String, "intern", Fstr_intern, 0); + + rb_define_method(C_String, "sub", Fstr_sub, 2); + rb_define_method(C_String, "gsub", Fstr_gsub, 2); + rb_define_method(C_String, "chop", Fstr_chop, 0); + rb_define_method(C_String, "strip", Fstr_strip, 0); + + rb_define_method(C_String, "tr", Fstr_tr, 2); + rb_define_method(C_String, "tr_s", Fstr_tr_s, 2); + rb_define_method(C_String, "delete", Fstr_delete, 1); + rb_define_method(C_String, "squeeze", Fstr_squeeze, -2); + + rb_define_method(C_String, "each", Fstr_each, 0); + rb_define_method(C_String, "each_byte", Fstr_each_byte, 0); + + rb_define_func(C_Kernel, "sub", Fsub, 2); + rb_define_func(C_Kernel, "gsub", Fgsub, 2); + + pr_str = rb_intern("to_s"); +} |