treeの作成に必要なpyparsingの機能を理解しました。今回は3番の抽象構文木(AST)生成を行います。パーサーでトークンに分解する。今ココASTノードを辿ってコードを生成する。ASTノードノードを定義するにあたっては次の点に注意します。ASTノードはpyparsingの実装から切り離す。ツリーの探索が簡単にできるようする。具体的には共通のベースクラスを持たせる。ノードがコード生成の最小単位であるから、一つのノードの役割を大きくしない。pl0_nodes.pyというファイルを新規に作成します。このファイルではpyparsingはインポートしません。ベースクラスはprintしたときに、クラス名とフィールド名と値を表示するメソッドだけです。このクラスはPythonのast.ASTノードを真似ています。)VARを具体例に実装します。変数の宣言をVariablesとし、変数そのものはVarとしました。idテストしてみます。variables結果は次のようにVariablesを頂点とした入れ子構造のASTです。Var(id=y)])ASTジェネレータ前回やったようにVariablesクラスをsetParseActionで直接起動しても良いのですが、それではコンストラクタの引数がpyparsingのトークンになってしまい密結合になってしまうのと、宣言された識別子の追跡も行いたいので、間にASTジェネレータを導入します。make_variablesがsetParseActionから呼ばれる関数で、トークンの2番目に入っている変数名のリストをループで処理しながらVarクラスのオブジェクトを作ります。同時にシンボルテーブルに識別子を格納していきます。シンボルテーブルがあれば変数の二重定義のエラーをチェックしたり、コード生成時にメモリを確保するなどの用途に使えます。Variables(variables)実際にパーサーから呼んでみましょう。先頭でインスタンスを作り、variables.setParseActionをセットします。SEMICOLONvariables.setParseAction(ast.make_variables)テストしたところ、ASTが正しく生成されています。Var(id=Name(id=x))}パース後にsymbol_tableをダンプするコードを追加します。v.__class__.__name__)サンプルのex1.pl0をパースしてみます。Varひたすら実装あとは順次最深部から実装して積み上げていきます。未実装の依存関係が最小限で済むのでテストがしやすいです。代入は右辺を左辺にセットするというだけです。right)識別子のテスト結果をみると':='が出ていましたが、パース時の残骸なのでSuppressしましょう。expressionassign_statement.setParseAction(ast.make_assign)テスト。手続き呼び出しcallは現状procedureしかとりませんが、callableに拡張すれば関数も実装できますね。Call(ident)pl0_parser.pycall_statement.setParseAction(ast.make_call)テスト結果。Varif文IF文は条件式と実行する本体をとります。bodytokens[2]は'THEN'が入っているので無視。予約語も全部Suppressしてもよかったですね。デバッグがしにくくなるかもしれませんが。body)pl0_parser.pyif_statement.setParseAction(ast.make_if)テスト...と思ったらex1.pl0にIF文がなかったので、適当にテストします。文IF文と全く同じです。複文BEGIN-ENDは複数の文をひとかたまりにしています。オリジナルのBNFから書き起こしたパーサーだとsetParseActionがセットできないので、括り出しました。MultiStatements(tokens)テスト結果。procedure同じパターンで以下を実装しました。VarblockブロックはOptionalのフィールドがあるので、トークンの位置だけでは何がセットされているのかわかりません。うまい方法を思いつかなかったので、isinstance()でクラスの型を判別することにしました。各statementクラスは親クラスをASTNodeからStatementクラスに変更します。statements)テスト結果。Program()ノードを頂点とした入れ子になっています。いや、式がトークンの羅列のままになっています。前回pyparsingのinfixNotationについてしらべましたが、引数を追加するとsetParseActionと同等のことができます。])コールバックを追加します。])make_binary_opは、演算子の記号によってインスタンス化するノードクラスを変えています。いうトークンを送ってくるので、二項演算に変換するためのconvert()という関数を書きました。3)というASTを生成することができます。upLoginユーザーは見つかりませんでした。