コンパイラ構成論ソース読み会 〜pypy〜 †インストール †
pypy-cというバイナリが出来上がる! lldbデバッグする場合 †
pypy interepreter をpdbデバッグする †
1日目 †どこでパースしているのか探そうという話 †$ lldb -- ./pypy-c tmp.py
pdbデバッグしていく †$ python -m pdb pypy/bin/pyinteractive.py tmp.py
/Users/e115747/Desktop/pypy/pypy/bin/pyinteractive.py
95行目 : spacce.setitem()
174行目 : main.run_toplevel()
pypy/pypy/interpreter/main.py
2日目 †python・pypyのコンパイラの仕組み。 †
スペースの切り替え部分を探す。 †pypy/pypy/interpreter/main.py
frame を作成して exec するところまでは追った。
(Pdb) bt /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/bdb.py(387)run() -> exec cmd in globals, locals <string>(1)<module>() pypy/pypy/bin/pyinteractive.py(204)<module>() -> sys.exit(main_(sys.argv)) pypy/pypy/bin/pyinteractive.py(175)main_() -> verbose=interactiveconfig.verbose): pypy/pypy/interpreter/main.py(103)run_toplevel() -> f() pypy/pypy/bin/pyinteractive.py(159)doit() -> main.run_file(args[0], space=space) pypy/pypy/interpreter/main.py(68)run_file() -> run_string(istring, filename, space) pypy/pypy/interpreter/main.py(59)run_string() -> _run_eval_string(source, filename, space, False) pypy/pypy/interpreter/main.py(48)_run_eval_string() -> retval = pycode.exec_code(space, w_globals, w_globals) pypy/pypy/interpreter/eval.py(33)exec_code() -> return frame.run() pypy/pypy/interpreter/pyframe.py(140)run() -> return self.execute_frame() > pypy/pypy/interpreter/pyframe.py(168)execute_frame() -> next_instr = r_uint(self.last_instr + 1) テストのソースを編集し、それを動かしながらデバッグしていく。 †
pypy/interpreter/pyparser/pyparse.py を読んだ
pypy/interpreter/pyparser/pytokenizer.py を読んでる。
バイトコードが生成された。 †pypy/interpreter/test/test_compiler.py
$ python -m pdb hogest.py tmp.py
バイトコードをディスパッチに食わせて、tmp.pyを実行できるかを試した †
pypy/pypy/interpreter/eval.py(33)
pypy/pypy/interpreter/main.py
3日目! †コンパイラは、与えられたソースコードをいろいろと変換し、 メモリ上に完全構文木(cst)にしたあと、それを抽象構文気(ast)に変換している。
test -> or_test -> and_test -> not_test -> comparison -> expr -> xor_expr -> and_expr -> shift_expr -> arith_expr -> term -> factor -> power hanldle_expr どんどんif文でchildrenを取っていっている children が 1 の場合は children[0] を取ってきて終了 power まできたら handle_power して handle_atom する おそらく、演算などがかかりそうな部分にすべて handler があって、 children が 1である(特に演算が無い場合)は次の演算をチェックする、という形で潜っていく様子 file_input pypy/interpreter/pyparser/data/Grammar2.7の中に、simple_stmtの構成等が書かれていた。 pypy/interpreter/pyparser/pytoken.pyの中に、演算子に対応する一覧が書かれている。
sym_tab = {} for attr in dir(astbuilder.syms): v = getattr(astbuilder.syms, attr) if isinstance(v, int): sym_tab[v] = attr def show_token(n): tokens = [] if n > 256: return sym_tab[n] else: tokens = pytoken.python_tokens.items() for k, v in tokens: if n == v: return k
p dir(syms) p syms.small_s とかも、覚えておくといいかも fileinputに潜る
type を識別する巨大な if-elif 文があり children[0] の type --> simple_stmt children[0] の type --> small_stmt children[0] の type --> expr_stmt 的な感じで分岐する様子
handle_expr の時点での btのログ /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/bdb.py(387)run() -> exec cmd in globals, locals <string>(1)<module>() /Users/e115763/files/build/pypy2/run_pypy.py(58)<module>() -> pycode = compile_with_astcompiler(source, "exec", StdObjSpace()) /Users/e115763/files/build/pypy2/run_pypy.py(37)compile_with_astcompiler() -> ast = astbuilder.ast_from_node(space, cst, info) /Users/e115763/files/build/pypy2/pypy/interpreter/astcompiler/astbuilder.py(12)ast_from_node() -> return ASTBuilder(space, node, compile_info).build_ast() /Users/e115763/files/build/pypy2/pypy/interpreter/astcompiler/astbuilder.py(63)build_ast() -> stmts.append(self.handle_stmt(stmt)) /Users/e115763/files/build/pypy2/pypy/interpreter/astcompiler/astbuilder.py(626)handle_stmt() -> return self.handle_expr_stmt(stmt) /Users/e115763/files/build/pypy2/pypy/interpreter/astcompiler/astbuilder.py(694)handle_expr_stmt() -> target_expr = self.handle_testlist(target_node) /Users/e115763/files/build/pypy2/pypy/interpreter/astcompiler/astbuilder.py(710)handle_testlist() -> return self.handle_expr(tests.children[0]) > /Users/e115763/files/build/pypy2/pypy/interpreter/astcompiler/astbuilder.py(721)handle_expr() -> if first_child.type in (syms.lambdef, syms.old_lambdef): children[1].type : 22 --> EQUAL
生成されたコード pypy/interpreter/astcompiler/ast.py ast.pyを生成するコード /pypy/interpreter/astcompiler/tools/asdl_py.py 生成の定義等が書かれているやつ← 超重要!! pypy/interpreter/astcompiler/tools/Python.asdl ソースをpypyの中で適当に書いてると環境が壊れることがあるので注意 最悪hg clone等で再構築 |