B演習(言語処理系演習)第7回 データの表現 田浦 最終段階: 評価器 構文木 “fib” def n if < n … 評価器 (実行器) 2 そのプログラムが起こすべき副作用 (例: print) 評価器の仕事 print 1 “1”を表示する print x + 3 x + 3の「値」(= 変数xに格納されている値 + 3)を 表示する “x + 3”と表示するのではないことに注意 一般に, print <式> <式>の値(式を「評価した値」という)を表示する 評価器は任意の式の「評価」ができなくてはなら ない もちろん文も def f(x): s = 0.0 for i in range(0, x): s = s + i return s print f(x) 式f(x)の評価 つまり,fの中身の実行 文(の並び)の実行 評価器は任意の文の実行ができなくてはならな い 式を「評価する」とはどういうことか? 評価器内部で何を起こせば式を「評価した」と いえるのか? • 入力は式を表す構文木(実はこれだけでは足りな いが, それは後述) • 出力はあるPythonの値(整数,リスト,辞書,etc.) 以降ではPython値と呼ぶ つまり大雑把に言えば, py_val_t eval_expr(expr_t e, …) という,「式eを評価したPython値を返す」C関 数を作ることが目標 文を「評価する」とはどういうことか 文はPython値を生み出すわけではない • continue, break • 代入文: a = 10 • etc. 文を評価した結果 • 何らかの副作用(例: 変数の値が変わる)が実行される • 「次の文の選択」に影響を与える(continue/break/return) 目標: 以下の関数 eval_stmt(stmt_t s, …) 返り値は「次の文の選択」に必要な情報(continueが 実行された,など) 今日の話 Python値とは何かの定義 • 大部分は復習 データ表現の決定 すべてのPython値の列挙 整数 None 浮動小数点数 Python関数 (defで定義された関数) Native関数 (処理系内部でCで定義された関数) 文字(1文字の文字列) 文字列 タプル リスト 辞書 sub-Python (最小限)仕様 注: Native関数 defで定義されているわけではない 処理系内部のCの関数に対応している mini-Pythonプログラムからは通常の関数と同じ ように呼び出せる なぜ必要か? • C関数を呼び出さないと書けない機能の追加 • 組み込み関数(足し算などを含め)を作るときの要領 • Cとmini-Pythonの組み合わせで作るため (次週以降)組み込み関数の作り方 例: a + b ひとつとっても実は複雑 • a, bがともに整数なら整数の足し算 • a, bがともに数で,どちらかが浮動小数点数なら浮動小数 点数の足し算 • a, b がともに文字列,リスト,またはともにタプルなら連結 全部処理系内部(C)で実装するのは大変で要領が 悪い • Cで必要最小限の単純なもののみを作る • mini-Pythonで書けるものはmini-Pythonで +の場合のC/Pythonの切り分け例 C: • • • • • • 整数同士の足し算 浮動小数点同士の足し算 文字列の連結 タプルの連結 リストのappend 整数 浮動小数点の変換 mini-Pythonプログラム : • 場合わけをしながら適切な関数を呼ぶ本体 • def add(x, y): … 処理系内部: • a + b add(a, b)の呼び出しに変換 評価器を作る前に決めなくてはいけな いこと: データ表現 様々な種類のPython値(整数,リスト,辞書, etc.)がある 式を評価すると,結果に対応するPython値が, 評価器(それはCのプログラム)によって作ら れる あらゆるPython値がCプログラム中でどう表 現されるかの「取り決め」(データ表現)が必要 Python値 (我々の頭の中に のみある) 写像(データ表現) Cプログラムのあるデータ データ表現(例) Python値の [ 1, 2, 3 ]は評価器中でどういうC のデータとして表現されるのか? • おそらく適当な構造体を定義し,それへのポイン タとするのであろう Python値の “hello”はどう評価器中でCの データとして表現されるのか? Python値の5は? Python値の3.28は? データ表現に当たって重要なこと (実行時に)データの種類がきちんと区別でき ること • 不法な演算(例: aが整数でa[i]を実行した)を検出 する • 同じ構文でもデータが異なれば動作が異なる(例: a[x]) 基本枠組み 構造体 py_val, それへのポインタ型 py_val_t を定義する typedef struct py_val { … } py_val, * py_val_t; すべてのPython値は評価器内部では py_val_t型 (py_valへのポインタ)のCデータと して表す 可能なpy_val抜粋 typedef struct py_val { py_type_t type; /* 型を示す */ union { int i; /* 整数 */ double f; /* 浮動小数点数 */ py_string s; /* 文字列 */ py_ifun i; /* Python関数 */ py_nfun n; /* Native関数 */ … /* タプル,リスト,辞書 (省略) */ } u; } py_val; 例: (浮動小数点数の足し算) py_val_t fadd(py_val_t x, py_val_t y, …) { if (x->type != py_type_float) runtime_error(…); if (y->type != py_type_float) runtime_error(…); z = (py_val_t)malloc(…); z->type = py_type_float; z->u.f = x->u.f + y->u.f; return z; } 整数くらいは速くしたい ポインタ型(py_val_t)の変数に直接整数を代 入する つまり(後で若干修正するが), Python整数5の表現 = ((py_val_t)5) C言語における整数ポインタ間の代 入 int a[10]; int * p = a; /* 普通 */ int x = a; /* 整数 ポインタ */ p = x; /* ポインタ ポインタ */ 合法: 警告くらいでOKが出る 機械語レベルでの動作は,まるで特別なこと はない(bit列を「そのまま」moveしているだけ) もちろんこれも「合法な」Cのプログラ ム /* 注: 以下の構造体の定義はあくまで例 */ typedef struct py_val { … } * py_val_t; main() { py_val_t p = 10; /* 10をポインタに代入 */ } つまり… py_val_t (ポインタ)もint (整数)も所詮はコン ピュータ内部では整数(32個のbit列) C言語がそれを表面上区別しているに過ぎな い その区別を「わざと」無視することは常に可能 整数値をpy_val_t型の記憶領域(変数や フィールド)に格納することに驚きはない(要す るに32bit使うといっているに過ぎない) 各データ型の表現 もちろんこのとおりにやることを強制するわけではな い • • • • • • • • • • 整数 文字(1文字の文字列) None 浮動小数点数 文字列 Python関数 Native関数 タプル リスト 辞書 ポインタ型の32bitに必要なデー タをすべておしこむ(ポインタで あってポインタでない) 32bit値 即値,unboxed value 本体(構造体)が別の場所にあり, py_val_tの値はそれへのポインタ boxed value 32bit値 整数 mini-Pythonにおける整数iの,インタプリタ内 部での表現(py_val_t型の値) i の整数の2進表現 ※暗黙の仮定:通常のポインタ(メモリ 割り当ての結果返されるアドレス)の最 下位bitは0 1 タグbit 整数である印 なぜこれが正しく整数のタグになる か? もちろん保証されているわけではない しかしmallocで返される値はほとんどの計算 機で4または8の倍数(32 bit/64 bit) スタックの変数のアドレスなども同様 心配しないでそう仮定してよい(ためしに mallocで返された値を表示してみるとよい) 実際にmini-Pythonの「5」を表すCの 式が書けますか? 答え: ((py_val_t)((5 << 1) + 1)) Cの整数から対応するmini-Pythonの整数を作る関 数 • py_val_t mk_py_int(int i) { return ((py_val_t)((5 << 1) + 1); } mini-Pythonの値から,それが表す整数をCの整数と して取り出す関数 • int py_val_int(py_val_t v) { int x = (int)v; if (x & 1) return (x >> 1); else { error(); /* これは整数ではない! */ } } 1文字 文字c (Cではcharだが要するに整数)例えば 以下のようにする あき c の2進表現(8bit) 010 ((py_val_t)((c << 3) + 2)) 最下位bit = 0に注意(整数と区別可能) None 0 0110 これまでは32bitにすべてが収まって いた 以下は収まらない • • • • • • • 浮動小数点数(64bit) 文字列 Python関数 Native関数 タプル リスト 辞書 py_val構造体の中身を定義(ソースダウンロード) それらのデータはその構造体を確保し,そのアドレ スで表現する py_val抜粋 typedef struct py_val { py_type_t type; /* 型を示す */ union { double f; /* 浮動小数点数 */ py_string s; /* 文字列 */ py_ifun i; /* Python関数 */ py_nfun n; /* Native関数 */ … /* タプル,リスト,辞書 (省略) */ } u; } py_val; 浮動小数点数 つまり, 32bitポインタ a a番地: type=py_type_float u.f=32.343 文字列 typedef struct py_string { int n; /* 文字数 */ char * a; /* C文字列 */ } py_string, * py_string_t; 32bitポインタ a a番地: type=py_type_string u.s.n=5 u.s.a=b b番地: ‘h’‘e’‘l’‘l’‘o’ 0 Python関数 typedef struct py_ifun { char * name; /* defされた名前 */ str_vec_t ps; /* 引数のベクタ (文字列のベクタ) */ stmt_vec_t b; /* 本体(文のベクタ) */ } py_ifun, * py_ifun_t; Native関数 typedef py_val_t (*C_fun_t)(); /* C関数 */ typedef struct py_nfun { char * name; /* 名前 */ int arity; /* 引数の数 */ C_fun_t f; /* C関数 */ } py_nfun, * py_nfun_t; タプル,リスト,辞書 各自(資料を見ながら)工夫してみてください • データをどう格納するか? • それらに対する操作(a[i], a[i] = x, len(a)など)をき ちんと実現できるか? 今週の課題 データ表現の理解 pyvalues.h, pyvalues.cの完成(雛形提供) 各データ型に対するプリミティブな演算を関数 としてたくさんつくってみよ • それらは後に評価器やmini-Pythonプログラムか ら呼ばれる • 何が必要か自分で整理してみよ
© Copyright 2024 ExpyDoc