練炭ブログ

萌え壁紙、Irvine、DMonkey、Proxomitron などの情報を扱ってます。

DMonkey: StringBuffer における NUL 文字の検索

コメントなし»

StringBuffer.indexOf において、String.fromCharCode (0) で NUL 文字を検索することが出来ます。

var sb = new StringBuffer ('abc');
sb [1] = 0;

var s = String.fromCharCode (0);
alert (sb.indexOf (s));   // 1

// これらの方法では検索できない
alert (sb.indexOf (0));   // -1
alert (sb.indexOf (""));   // -1
alert (sb.indexOf ("\x00"));   // -1

NUL の後ろに文字が続いていても考慮されます。

var sb = new StringBuffer ('a*b*c');
sb [1] = sb [3] = 0;

var s = String.fromCharCode (0) + 'c';
alert (sb.indexOf (s));   // 3

ちなみに StringBuffer を文字列で初期化した場合、データ末尾に NUL 文字は付きません。また length を増やした場合は 0 で埋められます。

var sb = new StringBuffer ('abc');
var s = String.fromCharCode (0);

alert (sb.indexOf (s));   // -1
++sb.length;
alert (sb.indexOf (s));   // 3

DMonkey: 数値文字列の大小比較は数値に変換されて比較される

コメントなし»

数値文字列同士を < > で比較すると、number 型に変換されてから数値として比較されます。

Array.sort() を関数を指定せずに呼び出した場合も内部で同じように比較されているようです。

alert ('5' < '10');   // true alert ('9e1' < '1e2');   // true alert ('10' < '0xf');   // true alert ('1E2' < '1e2');   // false alert ('1E2z' < '1e2z');   // true (辞書順) alert ([ '-1', '0', '+1' ].sort ()); // -1,0,+1

DMonkey: import の動作

コメントなし»
alert (1);

というコードを Irvine の scripts フォルダに test.dms として保存して、

var sl = new Strings ();
sl.add ('alert (2);');
sl.saveToFile ('(Irvineのパス)\scripts\test.dms');

alert (3);
import test.*;

を実行すると、3 → 1 と表示されます。

よって、import で指定されたファイルはスクリプトが実行されるより前(恐らくバイトコードへのコンパイル時)に読み込まれ、import が記述された位置で実行されるということになります。

なお、iffunction などのブロックの中に記述すると SyntaxError になるので、トップレベルでしか使用できません。

DMonkey: 関数は .apply() と .call() が使えて、arguments.callee もある

コメントなし»

実は関数に対しては .apply() と .call() が使えるようで、
DMonkey: インスタンス['メソッド名']() で this が Global になる
の例は

a ['p'].apply (a);   // this is Foo
a ['p'].call (a);   // this is Foo

とすれば期待通りの結果を得られました。

この例だと元のメソッドが引数を取らないので違いがありませんが、ECMAScript と同様に
.apply() は引数の配列を受け取り、.call() は最初の引数の後ろに , で並べた引数を受け取ります。

実行中の関数(自分自身の関数)を指す arguments.callee もあります。
typeof arguments.callee == 'function' です。

[6月16日 追記] 残念ながら、組み込みオブジェクトのメソッドには使えないようです。ENameError => member error apply/call になります。

DMonkey: インスタンス['メソッド名']() で this が Global になる

コメントなし»
Global.foo = 'this is Global';

function Foo () { }
Foo.prototype.foo = 'this is Foo';
Foo.prototype.p = function () {
  // alert (nameOf (this));
  alert (this.foo);
};

var a = new Foo ();
a.p ();   // this is Foo

a ['p'] ();   // this is Global

インスタンス.メソッド名() による呼び出しでは this がそのインスタンス自体を指しますが、インスタンス['メソッド名']() では this が Global を指してしまいます。

DMonkey: 配列を挟んでオブジェクトを循環参照するとエラーになる

コメントなし»
function Foo () {
  this.a = [ this ];
}

function OnStartThread () {
  var f = new Foo ();
}

上記スクリプトを実行すると、EInvalidPointer: Invalid pointer operation と表示されてダウンロードスレッドがエラー終了してしまいます。

常にエラーになるわけではなく、正常に終了する場合もあります。その場合は再度アイテムを登録したり、Irvine を再起動すると発症するようになったりします。厳密な発生条件は不明で、OS などの環境によっては全く発生しないかもしれません。

OnStartThread() 自体は正常に終了して、スクリプト環境の終了処理内(DMonkey のガベージコレクタとか)でエラーが起きているような感じですが、詳しい状況は不明です。

対処としては配列内に循環参照を入れないようにするしかないと思いますが、データの保存場所が配列でなくてもよいいなら、代わりにオブジェクトを使えば回避できるはずです。

  this.a = { length: 0 };
  this.a [this.a.length++] = this;

テスト用スクリプトファイル
添付ファイル:dmonkey_circref_test.zip

DMonkey: boolean と文字列の比較結果

コメントなし»

true == 'true'true == '1' が真、それ以外は偽('True' とか '2' も偽)。

false == 'false'false == '0' が真、それ以外は偽。

new Boolean ('string') の挙動も調べてマニュアルに書き加えておいた方がいいかも知れない。

DMonkey: String/RegExp.replace で0回のマッチをキャプチャすると number 0 になる

コメントなし»

[4月29日追記] 訂正: number 0 になるのではなく、キャプチャ結果が引数として渡されないため、マッチした位置(index)を参照していただけでした(0回のマッチと1回以上のマッチの場合で arguments.length が違う)。申し訳ありません。

  • String.replace() や RegExp.replace で、
  • 正規表現に (~)?(~)* を使い、
  • 置換文字列として関数を指定していると、
  • 最後のキャプチャ部分が0回の繰り返しにマッチした時、
  • キャプチャ結果として関数の引数に number 型の 0 が渡される。キャプチャ結果が関数の引数に渡されない。

通常は空文字列が渡されるのですが、上記のように最後のキャプチャが0回マッチだと 0 が渡されますキャプチャ結果が渡されません。他の文字列と + 演算子で繋げたりすると '0' という文字列として扱われてしまうので注意が必要です。

alert (
  'ab'.replace (/a(\d)?b/, function (s, p1) { return typeof p1; })
);
//number

alert (
  'ab'.replace (/a(\d)*b/, function (s, p1) { return typeof p1; })
);
//number

なお、各種ブラウザでは0回マッチでもキャプチャ結果が引数として渡されますが、その内容は Firefox 12 では空文字列(string)、Internet Explorer 8 と Google Chrome 18 では undefined になります。

常に文字列で渡される Firefox 形式が楽だと思うのですが、ECMAScript 的にはどの動作が正しいんでしょうか?

対処法

  • 正規表現の末尾に () を付ける。
  • (~)?((~)?) のように外側をキャプチャする。
  • 置換文字列として指定された関数内で、number 型か調べて空文字列に置き換える。

一つ目の方法の例。

alert(
  'ab'.replace(/a(\d)?b()/, function (s, p1) { return typeof p1; })
);
//string

alert(
  'ab'.replace(/a(\d)*b()/, function (s, p1) { return typeof p1; })
);
//string

DMonkey: URL オブジェクトの各プロパティ内容

コメントなし»

URL オブジェクトの各プロパティが実際にどのような内容になるか調べてみました。

空文字列以外はクオートを省略してあります。

var u = new URL ('http://example.com:8080/dir/index.html?q=1#frag');
alert ([
  u.url,       // http://example.com:8080/dir/index.html?q=1#frag
  u.protocol,  // http
  u.host,      // example.com:8080
  u.hostname,  // example.com
  u.port,      // 8080
  u.path,      // /dir/index.html?q=1#frag
  u.directory, // /dir/
  u.filename,  // index.html
  u.query      // ?q=1#frag
].join ("\n"));

最小限の構成要素だと以下のようになります。

var u = new URL ('https://example.com/');
alert ([
  u.url,       // https://example.com/
  u.protocol,  // https
  u.host,      // example.com
  u.hostname,  // example.com
  u.port,      // ''
  u.path,      // /
  u.directory, // ''
  u.filename,  // ''
  u.query      // ''
].join ("\n"));

DMonkey: Global.parseInt()

コメントなし»

dmonkeydoc.html より。

parseInt(値) ... 値を整数値に変換します。返値は整数値。

参考:parseInt - MDN

JavaScript では値はまず文字列化してから解析されますが、DMonkey では文字列化されず型ごとに変換アルゴリズムが適用されます。

基数を指定する第二引数は渡せません。

parseInt (整数) → そのまま

parseInt (小数) → 小数部を四捨五入 (round)
parseInt (-1.5) → -2
# JavaScript では parseInt (-1.5) → -1

parseInt (true) → 1
parseInt (false) → 0

parseInt () → undefined

parseInt (NaN) → NaN
parseInt (null) → NaN

var Infinity = Number.POSITIVE_INFINITY;
var undefined = (function () {}) ();

parseInt (Infinity) → NaN
parseInt (undefined) → NaN

文字列は、DMonkey の内部で Delphi の StrToIntDef 関数を呼び出しています(デフォルト値 0)。

/^ *\d+$/ → 10進数(0 または正数)
/^ *+\d+$/ → 10進数(0 または正数)
/^ *-\d+$/ → 10進数(0 または負数)

/^ *[-+]?(\d+\.?|\d*\.\d+) *$/ → 0

/^ *\d+(\.\d*)?e\d* *$/i → 0

/^ *$[0-9a-f]+$/i → 16進数
/^ *0x[0-9a-f]+$/i → 16進数

それ以外 → NaN

ややこしい動作をするので正規表現にしてみましたが、適当に入れてみたパターンから逆算しただけなので間違いがあるかも知れません。