《手写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

符合我们的预期。

《手写PHP编译器》之实现echo_expr

上一篇:pandas 导出excel表,每个worksheet只能有65530条URL


下一篇:文件上传之后端黑白名单绕过