1. 程式人生 > 其它 >《手寫PHP編譯器》之實現echo_expr

《手寫PHP編譯器》之實現echo_expr

到目前為止,我們已經實現了yaphp原始碼到AST的生成工作。但是,我們對echo語句的實現還是簡單處理了,之前的實現如下:

statement:
    T_ECHO expr ';'     { $$ = $2; }
;

可以看到,這裡實際上我們沒有體現出T_ECHO的功能,我們僅僅處理了expr。所以,這裡我們需要去處理一下。

有了前面的基礎之後,我們可以非常輕鬆的來實現這個AST的生成。

我們修改後的規則如下:

statement:
    T_ECHO echo_expr ';'     { $$ = $2; }
;

echo_expr:
    expr {
        std::cout << "create echo zend_ast" << std::endl;
        $$ = zend_ast_create_1(ZEND_AST_ECHO, 0, $1);
    }
;

可以看到,我們加了一個echo_expr,用來建立一個ZEND_AST_ECHO型別的AST。這裡,我們的語法和php-src的有點不同,php-src的語法功能更加的豐富一點,它支援echo後面跟一個ZEND_AST_STMT_LIST,這個的實現也是比較簡單的,和我們之前建立top_statement_list的思路是一致的。這裡小夥伴們可以自己去實現一下,我們的yaphp就不支援這種可有可無的語法了。

其中,zend_ast_create_1的實現如下:

zend_ast *zend_ast_create_1(zend_ast_kind kind, zend_ast_attr attr, zend_ast *child) {
    zend_ast *ast;

    ast = (zend_ast *) malloc(zend_ast_size(1));
    ast->kind = kind;
    ast->attr = attr;
    ast->child[0] = child;

    return ast;
}

然後,我們需要在_zend_ast_kind中增加一個ZEND_AST_ECHO

/* 1 child node */
ZEND_AST_ECHO,

最後,我們再修改一下我們的dump_compiler_globals函式:

else if (ast->kind > ZEND_AST_0_NODE_END && ast->kind < ZEND_AST_1_NODE_END) {
    queue.push_back(ast->child[0]);
} else if (ast->kind > ZEND_AST_1_NODE_END && ast->kind < ZEND_AST_2_NODE_END) {
    queue.push_back(ast->child[0]);
    queue.push_back(ast->child[1]);
}

其中,ZEND_AST_0_NODE_END, ZEND_AST_1_NODE_END, ZEND_AST_2_NODE_END這三個zend_ast節點型別php-src是沒有的,這裡是為了方便除錯給加上的,否則,我們每次增加一個zend_ast節點型別,就需要寫一個if語句。

現在,讓我們來編譯一下yaphp,並且執行,結果如下:

create * zend_ast
create + zend_ast
create echo zend_ast
kind: 129, attr: 0
kind: 131, attr: 0
kind: 515, attr: 1
kind: 65, attr: 0, value: 1
kind: 515, attr: 3
kind: 65, attr: 0, value: 2
kind: 65, attr: 0, value: 3

符合我們的預期。