接着上节没有分析完的内容继续分析
?
编译流程以?echo?“test”;简单分析
(1)加载web server基本信息,通过调用RuntimeOption::Load(empty)方法进行加载
(2)初始化加载扩展基本内容:prepareOptions(po, argc, argv);初始化编译配置;BuiltinSymbols::LoadSuperGlobals()加载php?如_get,_session等函数的返回值类型等;BuiltinSymbols::Load和ar->loadBuiltins()加载扩展类、扩展方法、扩展常量、扩展变量
(3)parser:通过调用Package的parser方法,执行lex和bison进行词法和语法分析,生成语法分析树
(4) analyzeProgram:语义分析阶段,进行划分作用域和控制流等
(5)(7)代码优化
(6)类型推导
(8)生成代码
?
例:echo?“test”;
进入词法和语法分析的入口是Package::parse方法
词法分析规则文件是在src/Utile/Parse/hphp.x中,是用flex进行词法分析的
echo “test”
匹配了2个token分别为:
T_ECHO?和T_CONSTANT_ENCAPSED_STRING
Hphp.x中的匹配规则如下:
echo词法分析为T_ECHO
<ST_IN_SCRIPTING>"echo"???????????????? { SETTOKEN; return T_ECHO;}
“test”?解析TOKEN为T_CONSTANT_ENCAPSED_STRING
ANY_CHAR (.|[\n])
?
DOUBLE_QUOTES_LITERAL_DOLLAR("$"+([^a-zA-Z_\x7f-\xff$\"\\{]|("\\"{ANY_CHAR})))
?
DOUBLE_QUOTES_CHARS("{"*([^$\"\\{]|("\\"{ANY_CHAR}))|{DOUBLE_QUOTES_LITERAL_DOLLAR})
?
<ST_IN_SCRIPTING>(b?[\"]{DOUBLE_QUOTES_CHARS}*("{"*|"$"*)[\"]){
???????int bprefix = (yytext[0] != '"') ? 1 : 0;
???????std::string strval =
?????????_scanner->escape(yytext + bprefix + 1,
?????????????????????????? yyleng - bprefix -2, '"');
???????_scanner->setToken(yytext, yyleng, strval.c_str(), strval.length());
???????return T_CONSTANT_ENCAPSED_STRING;
}
?
Hiphop?定义的默认Token?结构是:
#define YYSTYPE HPHP::ScannerToken
调用流程:
(1)首先调用:hphp.y中的
static int yylex(YYSTYPE *token,HPHP::Location *loc, Parser *_p) {
?return _p->scan(token, loc);}调用扫描器;
(2)然后扫描器scan调用扫描器:Scanner::getNextToken ,获取token
(3)然后调用hphp.x中的:
int Scanner::scan() {
???return yylex(m_token, m_loc, m_yyscanner);
? }
(4)执行yylex对lex语法进行分析
?
TOKEN分词,目前echo “test”给拆分成了2个TOKEN
echo => T_ECHO
“test” =>T_CONSTANT_ENCAPSED_STRING
?
这里都是通过正则匹配进行词法分析的:
(1)??????echo
<ST_IN_SCRIPTING>"echo"???????????????? { SETTOKEN; return T_ECHO;}
当遇到echo?时则返回T_ECHO,其他依次类推,更多的分析,我们将在原理分析二中进行分析语句(statement)和表达式(expression)
(2)??????“test”
通过正则分析最终匹配T_CONSTANT_ENCAPSED_STRING,常量String的这个TOKEN
词法分析完成后,下一步就是进行语法分析了
之前lex?已经划分出了2个token: T_ECHO?和T_CONSTANT_ENCAPSED_STRING
根据token?查找语法规则:
Hphp的语法分析是用bison分析的,在src/Utile/Parse/hphp.y文件中
T_ECHO expr_list { _p->onEcho($$, $2,0);}
匹配上这里后,然后expr_list?又是一个合成规则,继续递归往下找,直到找到最终匹配规则:
common_scalar:
???T_LNUMBER?????????????????????????{ _p->onScalar($$, T_LNUMBER,?$1);}
? |T_DNUMBER????????????????? ????????{ _p->onScalar($$, T_DNUMBER,? $1);}
? |T_CONSTANT_ENCAPSED_STRING???????? {_p->onScalar($$,
????????????????????????????????????????T_CONSTANT_ENCAPSED_STRING,? $1);}
最终封装为一个语法树
?
具体规则内容:
start:
???top_statement_list????????????????{ _p->popLabelInfo();
????????????????????????????????????????_p->saveParseTree($$);}
;
?
top_statement_list:
???top_statement_list
???top_statement?????????????????????{ _p->addStatement($$,$1,$2);}
?|?????????? ?????????????????????????{_p->onStatementListStart($$);}
;
top_statement:
???statement?????????????????????????{ _p->nns($1.num() == T_DECLARE);
???????????????????????????????????????? $$ =$1;}
?............
;
?
statement:
……..
? |T_ECHO expr_list ';'?????????????? {_p->onEcho($$, $2, 0);}
/////////////////////////////////////
expr_list:
???expr_list ',' expr????????????????{ _p->onExprListElem($$, &$1, $3);}
? |expr?????????????????????????????? {_p->onExprListElem($$, NULL, $1);};
/////////////////////////////////
expr:
...............
? |scalar???????????????????????????? { $$ =$1;}
.............;
/////////////////////////////////////////
scalar:
.........
? |common_scalar????????????????????? { $$ =$1;}
............
;
?
?
common_scalar:
? ??T_LNUMBER????????????????????????? { _p->onScalar($$,T_LNUMBER,? $1);}
? |T_DNUMBER????????????????????????? {_p->onScalar($$, T_DNUMBER,? $1);}
? |T_CONSTANT_ENCAPSED_STRING???????? {_p->onScalar($$,
???????????????????????????????????????? T_CONSTANT_ENCAPSED_STRING,? $1);}
…………..
?
?
具体流程如下:
语法分析从start开始,类似top_statement:{…..}是一个语句;
如我们的echo,他会逐个的语句中去找,最终在
statement:
……..
? |T_ECHO expr_list ';'?????????????? {_p->onEcho($$, $2, 0);}
这个语句中找到了T_ECHO,然后{这里面的是调用的实现代码}
移进是一个自顶向下的过程,规约是一个自底向上的过程;
Statement=> T_ECHO=> expr_list=> expr=>scalar=>common_scalar=> T_CONSTANT_ENCAPSED_STRING
这就一个echo “test”?的语法分析:
首先找到T_ECHO?然后向下找expr_list;
expr_list?又有子集expr;
expr,这个表达式呢又找到是一个scalar的;
scalar表达式子集中common_scalar又有匹配;
?最终在common_scalar中找到匹配的TOKEN?(T_CONSTANT_ENCAPSED_STRING)
?
?
匹配的过程其实就是一个移进规约的过程:
Echo “test”
这里的.是游标
Echo .”test”?移进
EchoExpression “test”.?规约
EchoExpression ExpressionList?规约
EchoExpression. ScalarExpression移进
EchoExpression ExpressionList.?移进
EchoExpression ExpressionList规约
EchoExpression. ExpressionList移进
Statement?规约
Statement.?移进
top_statement?规约
top_statement.?移进
top_statement_list?规约
?
移进规约后生成一颗语法树;
?
?
Parser::onScalar?这个方法是获取T_CONSTANT_ENCAPSED_STRING这个token?后创建一个saclarExpression的实例
然后调用_p->onExprListElem($$, NULL, $1);是将saclarExpression封装到exprList中
然后调用onEcho?是将exprList封装到EchoStatment语句中,这样就生成了一个语法树
上面的$$类似于树的顶点,也就是每次处理后返回的封装节点,$1则是传入的token或者表达式等内容的信息,对其进行封装
?
生成的语法树:
?
?
?
?
?
hiphop Token?的数据结构:
???????? ?class Token : public ScannerToken?:
ExpressionPtr exp;
?StatementPtr stmt;
Class ScannerToken:
? int m_num; // internal token id
? std::string m_text;
? bool m_check;
?
下一节:hiphop原理分析2