到目前为止,我们已经实现了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
符合我们的预期。