php7's ast
TRANSCRIPT
php7’s ast
2015/11/22 第六回闇 PHP勉強会do_aki
@do_aki
@do_aki
http://do-aki.net/
agenda
1.PHP コンパイラの概要
2.PHP AST の概要
3.PHP AST の視覚化 (DEMO)
コンパイルプロセス(教科書的 )
字句解析
構文解析 最適化 コード
生成
字句解析
• ソースコードをトークンに分解• トークン := 意味を持つ最小単位
<?php
function hello ( ) {
echo ‘HELLO WORLD’ ;
}
字句解析
• ソースコードをトークンに分解• トークン := 意味を持つ最小単位
<?php
function hello ( ) {
echo ‘HELLO WORLD’ ;
}
T_OPEN_TAG
T_FUNCTION T_STRING ( ) {
}
T_ECHO T_CONSTANT_ENCAPSED_STRING ;
構文解析
• トークン列から規則(文法)を導く
function hello ( ) {
echo ‘HELLO WORLD’ ;
}
T_FUNCTION T_STRING ( ) {
}
T_ECHO T_CONSTANT_ENCAPSED_STRING ;inner_statement_list
関数定義
php5 の コンパイルプロセス
字句解析 構文解析 + コード生成
php7 の コンパイルプロセス
字句解析 構文解析 コード生成
zendlex -> TOKEN zendparse -> OPCODE
zendlex -> TOKEN zendparse -> AST zend_compile_stmt -> OPCODE
php5 の コンパイルプロセス
字句解析 構文解析 + コード生成
php7 の コンパイルプロセス
字句解析 構文解析 コード生成
zendlex -> TOKEN zendparse -> OPCODE
zendlex -> TOKEN zendparse -> AST zend_compile_stmt -> OPCODE
構文解析で AST を構築し、AST から OPCODE を生成
導入理由
コンパイラおよびパーサのメンテナンス性向上 (技巧的なコードの排除)
技術的な問題により、導入が困難であった文法を扱えるようになる
PHP の AST
zend_ast ( 基本形 )• Zend/zend_ast.h / Zend/zend_ast.c
typedef uint16_t zend_ast_kind;typedef uint16_t zend_ast_attr;
struct _zend_ast { zend_ast_kind kind;
/* Type of the node (ZEND_AST_* enum constant) */ zend_ast_attr attr;
/* Additional attribute, use depending on node type */ uint32_t lineno;
/* Line number */ zend_ast *child[1];
/* Array of children (using struct hack) */};
typedef struct _zend_ast zend_ast; // <- Zend/zend_types.h
Zend/zend_ast.h より 一部見やすさのために改変
zend_ast_kind• ZEND_AST_*• 全 98 種 (php7.0.0 RC7 時点 )
• 大まかに分類して 4 系統–特殊ノード ZEND_AST_ZVAL / ZEND_AST_ZNODE–定義ノード ZEND_AST_CLASS など–リストノード ZEND_AST_STMT_LIST など–通常ノード ZEND_AST_VAR, ZEND_AST_FOREACH な
ど
特殊ノード (special nodes)
• ZEND_AST_ZVAL– zval を包含するノード–リテラル や 変数名、呼び出し関数名等–常にリーフ
• ZEND_AST_ZNODE–構文解析の過程では一切利用されない–コンパイル時に一時的に使われる–あまり詳しく追ってない
定義ノード (declaration nodes)
• doc_comment を保持
• 常に4つの子ノードを確保 (NULL の場合もある)
• ZEND_AST_FUNC_DECL 関数定義
• ZEND_AST_CLOSURE 無名関数定義
• ZEND_AST_METHOD メソッド定義
• ZEND_AST_CLASS クラス、無名クラス、 インタ
フェース、トレイト定義
リストノード (list nodes)• 配下に複数のノードを格納するノード
• ZEND_AST_STMT ステートメントを格納 (ASTの ROOT)
• ZEND_AST_IF if文の各条件とブロックを格納
• ZEND_AST_ARRAY 配列
• その他、 ZEND_AST_ARG_LIST 等 “ ,” を使って列挙するところは大体ここに分類
通常ノード (standard nodes)
• 0 child nodes– ZEND_AST_MAGIC_CONST / ZEND_AST_TYPE– __LINE__ とか / callable とか
• 1 child nodes– 変数、定数、キャスト、単項演算子 等
• 2 child nodes– 配列の要素参照、関数呼び出し、代入、2項演算子、 new など
• 3 child nodes– メソッド呼び出し、プロパティ定義、 3項演算子 など
• 4 child nodes– ZEND_AST_FOR / ZEND_AST_FOREACH– for (1, 2, 3) { 4 } / foreach (1 as 2 => 3) {4}
専用のメモリ領域
• CG(ast_arena)• 初期サイズは 32MB
• 構文解析前に確保(zend_arena_create)
• OPCODE 生成後に破棄(zend_arena_destroy)
zend_ast_create_*
zend_ast_alloc(size)
zend_arena_alloc(&CG(ast_arena),
size)
AST Hook Function
• AST構築直後 (OPCODE 生成前 )に呼ばれる• AST そのものが渡される -> 改変可能
• 将来的には、このフックポイントを利用した、最適化やフィルタを行う拡張が生まれるのでは
• https://wiki.php.net/rfc/parser-extension-api
typedef void (*zend_ast_process_t)(zend_ast *ast);extern ZEND_API zend_ast_process_t zend_ast_process;
nikic/php-asthttps://github.com/nikic/php-ast
do-aki/phpasthttps://github.com/do-aki/phpast
DEMO
Hello world
function hello() { echo 'hello wporld';}
FizzBuzz
for ($i = 1; $i <= 100; $i++) { if ($i % 3 == 0 && $i % 5 == 0) { echo 'FizzBuzz'; } elseif ($i % 5 == 0) { echo 'Buzz'; } elseif ($i % 3 == 0) { echo 'Fizz'; } else { echo $i; } echo PHP_EOL;}
any question?
(blank)
字句解析器の状態 (全 10状態 )• 初期状態 (INITIAL)• PHP Script (ST_IN_SCRIPTING)• 文字列解析のための状態
– ST_DOUBLE_QUOTES– ST_BACKQUOTE– ST_HEREDOC– ST_END_HEREDOC– ST_NOWDOC– ST_LOOKING_FOR_VARNAME (${)
• プロパティ参照解析のための状態– ST_LOOKING_FOR_PROPERTY ($xxx->)
• 配列要素参照解析のための状態– ST_VAR_OFFSET