意味を表すオブジェクトの文字列表現




意味を表すオブジェクトを文字列表現を実装してみた
こんな感じ

{Instance={Name=this,Typ={Name=T102_Class1Func1P0}:Typ,VarKind=This}:Variable,Callee={Name=0}:Fctn,Args={},IsNewObj=False}:CallAction

文字列にはなったが、当初の目的の「テストコードの見通しがよい」とはとてもいえない

とりあえずインデントを付けたらどうか

{
    Instance={
        Name=this
        , Typ={
            Name=T102_Class1Func1P0
            }:Typ
        , VarKind=This
        }:Variable
    , Callee={
        Name=0
        }:Fctn
    , Args={}
    , IsNewObj=False
    }:CallAction

これでも見にくいなあ

ひとつの意味に対してここまでテストの期待値を記述していくのは辛い

幾つもの意味に分解されて、それらごとにこの文字列表現を記述していくというのを考えると、、、正直やりたくない

結局、末尾の":CallAction"だけ期待値として一致させればいいのではと思えてきた

そこらへんが現実的かしら

はてさて

意味解析器の単体テストを実装したい

今、実装出来ている単体テストは、コンパイラにソースを入力し、ILを出力させて、そのILを文字列として比較している

この方法は、字句解析器、構文解析器、意味解析器、中間表現生成器、IL生成器を通してテストをしていることになる


意味解析器の出力のテスト結果がILに変換された結果で確認されるので、期待値の表現がILの表現に依存してしまう

これは意味解析器の期待値の表現として回りくどいので嫌だ


意味解析器の出力を直接確認できないのはなぜか

意味解析器の出力は意味を表すオブジェクトだ

その意味を表すオブジェクトを文字列表現する方法がないからだ

意味を表すオブジェクトが持つ各フィールド変数値、プロパティ値を直接確認する方法をとればいいのだが、やっぱりまとめて文字列化してから期待値と比較したほうが、テストコードの見通しがよい

これは個人的な好みだろう


というわけで意味を表すオブジェクトを文字列表現する方法を模索しよう

すいか割り


色んな仕事の仕方があるからおもしろい

例えばある仕事の仕方を例えるとそれは「すいか割り」


目隠しで直感で閃いた方に真っ直ぐ進んで、えいっと棒を振り下ろす

そこにすいかが無ければ、また直感に従って進んで、えいっ


自分の直感を信じて疑わない



けどその失敗が続くと驚くべき新しい理論が展開される


「棒を振り下ろした所がすいかだ」



棒を振り下ろした所がすいかである確率は

チョコボールを3個買って、金のエンジェルマークが連続で3つ出るのと同じくらい低いだろう


仕方なく周りの人は棒を振り下ろしそうな所にすいかを置く



ここで確実にすいかを割る方法を考えよう


Step 1. 目隠しを外す

Step 2. すいかのある所を認める

Step 3. 棒を振り下ろしたらすいかに当たりそうな所まで行く

Step 4. すいかを目掛けて棒を振り下す



やっぱりすいか割りは難しい

ILAsmの配列

ILAsmの配列がよく理解できませんでした
そこでExpert .NET 2.0 IL Assemblerの内容を適当に訳してまとめした

種類

CLRには2種類の配列があります
vectorとarrayです

1次元配列です。下限値が0に決まっています

  • array

多次元配列です。1次元以上の配列を定義できます。下限値には0以外も指定できます


arrayとvectorは別なものとして扱われます
例えば、2次元のarray(実例: int32[..., ...])とvectorvector(実例: int32)は全く別なものです
int32[..., ...] は1度のインストラクションコールで生成されます
int32は2回のnewarrインストラクションコールで生成されます

ILAsm表記法

vector
[ ]
array
[ [,] ]
 ::= [] ... []
実例
//vector
int32[]


//array
int32[..., ...]
int32[2...5]
int32[0..., 0...]


上限値、下限値とも指定しないときは"..."は省略可能です
そのため2次元以上の配列で int32[..., ...] と int32[,] は同じ意味のarrayになります
しかし1次元配列では、"..."は省略時の意味が分かれます
int32[] はvectorを表します
int32[...] はarrayを表します

もし下限値が指定されなかったときは、0だと仮定されます(?:要確認)
また、2次元以上のarrayで2次元目以上の範囲が指定されたときは、
それ以下の次元の範囲も指定します
例えば5次元のarrayで、3次元目の範囲を指定したら、
1次元目、2次元目の範囲も指定します

実例
int32 [ 0...3 , 0...3 , 0...3 , ... , ... ]   // OK
int32 [ ... , ... , 0...3 , ... , ... ]       // NG、1番目、2番目の範囲が未指定
int32 [ ... , 0...3 , 0...3 , ... , ... ]     // NG、1番目の範囲が未指定
int32 [ 0...3 , ... , 0...3 , ... , ... ]     // NG、2番目の範囲が未指定

インスタンス

vector、arrayとも[mscorlib]System.Arrayクラスから派生されています
vector、arrayともオブジェクト参照なので、使うときにはインスタンス化します
vectorインスタンス化は、newarrインストラクションを使います
arrayインスタンス化は、arrayのコンストラクタを使います
インスタンス化をするときは、そのサイズを指定します
そのため配列のサイズは、配列の型の属性ではなく、配列のインスタンスの属性になります

arrayのインストラクション

ILインストラクションセットにはvectorを扱うものはありますが、arrayを扱うものはありません
arrayやarrayの要素を扱うために、[mscorlib]System.Arrayのメソッドを呼ぶ必要があります
そのメソッドは、Get、Set、Addressです
Getメソッドはインデックスを表すN個の引数をとり、そのインデックスが指す値を返します
Addressメソッドも同様に引数をとり、インデックスが指す要素のマネージドポインタを返します
Setメソッドはインデックスを表すN個の引数と、インデックスが指す要素へ代入する値をとり、voidを返します

array生成

arrayコンストラクタは定義されていない下限値と大きさ(上限値)の数だけパラメータをとります
そのためnewobjインストラクションをコールする前に
同じ数の整数値をスタックに積んでおきます

実例
.locals init(int32[0..., 0...] iArr)
ldc.i4 5    // 1次元目の大きさ
ldc.i4 10   // 2次元目の大きさ
newobj instance void int32[0..., 0...]::.ctor(int32, int32)
stloc iArr

vector生成

newarr <要素の型>

vectorを生成します
このインストラクションはスタックからvectorの要素数を下ろして
生成したvectorの参照を積みます

実例
.locals init (int32[] arr)
ldc.i4 123
newarr int32
stloc.0

vector素数

ldlen

配列の要素数を取得します
このインストラクションはスタックからvectorインスタンスの参照を下ろして
その要素数を積みます

要素アドレス読み込み

ldelema <要素の型>

型のvector要素のアドレスを取得します
このインストラクションはスタックから要素のインデックスとvectorインスタンスの参照を下ろして
その要素へのマネージドポインタを積みます

素読み込み

ldelem.i4

int32型の要素をvectorから読み込みます

ldelem.ref

オブジェクト参照型の要素をvectorから読み込みます

他にも基本的な型の要素を読み込むインストラクションがあります
これらのインストラクションは共通してスタックから要素のインデックスとvectorインスタンスの参照を下ろして
その要素の値を積みます

ldelem (ldelem.any) <要素の型>

型の要素をvectorから読み込みます
このインストラクションは CLR version 2.0から導入され
vectorジェネリック型で定義されたarrayの変数を扱えます
このインストラクションの良いところは、型を自由に指定して読み込めるところです

要素格納

stelem.i4

int32型の要素をvectorに格納します

stelem.ref

オブジェクト参照型の要素をvectorに格納します
このインストラクションはキャストを伴います
そのためInvalidCast exceptionが投入されることがあります

stelem (stelem.any) <要素の型>

型の要素をvectorに格納します
このインストラクションは CLR version 2.0から導入され
vectorジェネリック型で定義されたarrayの変数を扱えます
このインストラクションの良いところは、型を自由に指定して読み込めるところです

サンプルプログラム

// コンパイル方法:
// ファイル名: tmp.il で保存して、SDK コマンド プロンプトから
// > ilasm tmp.il
.assembly extern mscorlib {.ver 2:0:0:0 .publickeytoken = (B7 7A 5C 56 19 34 E0 89)}
.assembly tmp{ }
.field static int32 a
.field static int32 b
.method static public void Main() {
    .entrypoint

    ldc.i4.s 9          // vectorの要素数を積む
    newarr int32        // vectorを生成
    stsfld int32 a    // 変数aに格納

    ldsfld int32 a    // 変数aを読み込み
    ldc.i4.0            // インデックスを積む
    ldc.i4.7            // 値を積む
    stelem.any int32    // 要素に格納

    ldsfld int32 a    // 変数aを読み込み
    ldc.i4.0            // インデックスを積む
    ldelem.any int32    // 要素を読み込む

    call void [mscorlib]System.Console::WriteLine(int32)   // -> 7


    ldc.i4.s 5          // vectorvectorの要素数を積む
    newarr int32      // vectorのvectorを生成
    stsfld int32  b // 変数bに格納

    ldsfld int32 b  // 変数bを読み込み
    ldc.i4.0            // インデックスを積む
    ldc.i4.s 3          // vectorの要素数を積む
    newarr int32        // vectorを生成
                        // 生成したvectorが値として積まれている
    stelem.any int32  // 要素に格納

    ldsfld int32 b  // 変数bを読み込み
    ldc.i4.0            // インデックスを積む
    ldelem.any int32  // 要素を読み込む
    ldc.i4.1            // インデックスを積む
    ldc.i4.s 11         // 値を積む
    stelem.any int32    // 要素を格納

    ldsfld int32 b  // 変数bを読み込み
    ldc.i4.0            // インデックスを積む
    ldelem.any int32  // 要素を読み込む
    ldc.i4.1            // インデックスを積む
    ldelem.any int32    // 要素を読み込む

    call void [mscorlib]System.Console::WriteLine(int32)    // -> 11

    ret
}


VC# ExpressでWindows Mobile開発 (2)に参加しました

先週(11/15)のことで遅ればせのレポートですが前回に引き続き、七誌さん主催の「VC# ExpressでWindows Mobile開発 (2)」に参加しました

第2回では第1回で作成途中だったものを引き続き作成しました
作っていたものは簡単なゲームです
0〜9までの数字が4回点滅するので、続けてその数字をタッチします
前回ではその数字をボタンコントロールで実装していましたが、今回はGraphicで描画するようにしました


できればそのときに配布された資料にあったダブルバッファリングで描画するようにしたかったのですが、
残念ながらそこもまではたどり着けませんでした


とりあえずボタンの色がポップな感じになったのでよしとしましょう



fslashtさんがDroid端末ゲームをWinMo端末をJoystickにして動かしていたのは面白かった
まともな速度でJoystickの入力に反応していて実用になっていました


それにしても携帯端末2台使って自作ゲームとは、なんとういう贅沢!!


fslashtのブログの方でも今回の勉強会のレポートされてて
写真がいっぱいで様子がよくわかってこっちよりいいですねw



また今回から参加された方もいて、質問を受けたりしていました
みんなで集まって作っていると、あーでもない、こーでもない言いながら開発できて楽しいですね
「勉強会というより合宿だ」と誰かが言っていた気がしますが、まさにそうでした



とりあえず作ったゲームのソースをまとめて↓に掲載しました
まずい、消すの忘れてる部分がある....orz

続きを読む

「みんなのPython」 を読みました

ちょっとPythonで書きたい処理があったので入門書として購入しました

よかったところ

日本語を文字列でどう扱うかの記述にはとても救われました
#メモとして自分なりの理解をBlogにのせたいです


リストや辞書などコレクション型説明が分類されていて分かりやすかった

わるかったところ

Amazonのレビューで指摘されているようなもの足りなさは確かにありました
#本の厚さをみると書きたくても書けなかったのかなという気もします
#それだけPythonのライブラリが充実しているとも言えるのでは?

Pythonを使ってみての感想

リストや辞書などコレクション型の機能が充実していて便利でした
ちょうどキーを使ってマッチングの処理を書きたかったのですが、listとsetを使って1行で書けてしました。ステキ☆
イメージ的には↓こんな感じです

dic1 = {"a": "AAA", "b": "BBB", "c": "CCC", "f": "FFF" }
dic2 = {"b": "BBB", "d": "DDD", "e": "EEE" }
 
keysunion = sorted(list(set(dic1.keys() + dic2.keys())))
print keysunion         # -> ['a', 'b', 'c', 'd', 'e', 'f']

CSC.exeが参照したいアセンブリを探す順序

CSC.exe が /reference コンパイラスイッチで指定されたアセンブリを探す順序

  • アセンブリのファイル名が完全パスならそのファイルをロードする
    1. 現在のワーキングディレクト
    2. CSC.exeがあるディレクトリ (CLRのDLLも含まれる ※ たぶん、mscorlib.dllがあるディレクトリも含まれるという意味?)
    3. /libコンパイラスイッチで指定された場所
    4. 環境変数LIBで指定されているディレクト

引用元:

プログラミングMicrosoft .NET Framework 第2版 (マイクロソフト公式解説書)

3.4 厳密名付きアセンブリを参照するアセンブリのビルド (P.89) から編集して抜粋しています