<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ### 3.1.1 詞法解析、語法解析 這一節我們分析下PHP的解析階段,即 __PHP代碼->抽象語法樹(AST)__ 的過程。 PHP使用re2c、bison完成這個階段的工作: * __re2c:__ 詞法分析器,將輸入分割為一個個有意義的詞塊,稱為token * __bison:__ 語法分析器,確定詞法分析器分割出的token是如何彼此關聯的 例如: ```php $a = 2 + 3; ``` 詞法分析器將上面的語句分解為這些token:$a、=、2、+、3,接著語法分析器確定了`2+3`是一個表達式,而這個表達式被賦值給了`a`,我們可以這樣定義詞法解析規則: ```c /*!re2c LABEL [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* LNUM [0-9]+ //規則 "$"{LABEL} {return T_VAR;} {LNUM} {return T_NUM;} */ ``` 然后定義語法解析規則: ```c //token定義 %token T_VAR %token T_NUM //語法規則 statement: T_VAR '=' T_NUM '+' T_NUM {ret = str2int($3) + str2int($5);printf("%d",ret);} ; ``` 上面的語法規則只能識別兩個數值相加,假如我們希望支持更復雜的運算,比如: ```php $a = 3 + 4 - 6; ``` 則可以配置遞歸規則: ```c //語法規則 statement: T_VAR '=' expr {} ; expr: T_NUM {...} |expr '?' T_NUM {} ; ``` 這樣將支持若干表達式,用語法分析樹表示: ![](https://box.kancloud.cn/16bf76a3daca3fe633e9f71216cb4290_479x293.png) 接下來我們看下PHP具體的解析過程,PHP編譯階段流程: ![](https://box.kancloud.cn/d9c4996f3723b6cde73658ed06145ccd_863x151.png) 其中 __zendparse()__ 就是詞法、語法解析過程,這個函數實際就是bison中提供的語法解析函數 __yyparse()__ : ```c #define yyparse zendparse ``` __yyparse()__ 不斷調用 __yylex()__ 得到token,然后根據token匹配語法規則: ![](https://box.kancloud.cn/1c22b1b50c758679d4873ee0a721441c_532x230.png) ```c #define yylex zendlex //zend_compile.c int zendlex(zend_parser_stack_elem *elem) { zval zv; int retval; ... again: ZVAL_UNDEF(&zv); retval = lex_scan(&zv); if (EG(exception)) { //語法錯誤 return T_ERROR; } ... if (Z_TYPE(zv) != IS_UNDEF) { //如果在分割token中有zval生成則將其值復制到zend_ast_zval結構中 elem->ast = zend_ast_create_zval(&zv); } return retval; } ``` 這里兩個關鍵點需要注意: __(1) token值__:詞法解析器解析到的token值內容就是token值,這些值統一通過 __zval__ 存儲,上面的過程中可以看到調用lex_scan參數是是個zval*,在具體的命中規則總會將解析到的token保存到這個值,從而傳遞給語法解析器使用,比如PHP中的解析變量的規則:`$a;`,其詞法解析規則為: ```c <ST_IN_SCRIPTING,ST_DOUBLE_QUOTES,ST_HEREDOC,ST_BACKQUOTE,ST_VAR_OFFSET>"$"{LABEL} { //將匹配到的token值保存在zval中 zend_copy_value(zendlval, (yytext+1), (yyleng-1)); //只保存{LABEL}內容,不包括$,所以是yytext+1 RETURN_TOKEN(T_VARIABLE); } ``` zendlval就是我們傳入的zval*,yytext指向命中的token值起始位置,yyleng為token值的長度。 __(2) 語義值類型__:bison調用re2c分割token有兩個含義,第一個是token類型,另一個是token值,token類型一般以yylex的返回值告訴bison,而token值就是語義值,這個值一般定義為固定的類型,這個類型就是語義值類型,默認為int,可以通過 __YYSTYPE__ 定義,而PHP中這個類型是 __zend_parser_stack_elem__ ,這就是為什么zendlex的參數為`zend_parser_stack_elem`的原因。 ```c #define YYSTYPE zend_parser_stack_elem typedef union _zend_parser_stack_elem { zend_ast *ast; //抽象語法樹主要結構 zend_string *str; zend_ulong num; } zend_parser_stack_elem; ``` 實際這是個union,ast類型用的比較多(其它兩種類型暫時沒發現有地方在用),這樣可以通過%token、%type將對應的值修改為elem.ast,所以在zend_language_parser.y中使用的$$、$1、$2......多數都是 __zend_parser_stack_elem.ast__ : ```c %token <ast> T_LNUMBER "integer number (T_LNUMBER)" %token <ast> T_DNUMBER "floating-point number (T_DNUMBER)" %token <ast> T_STRING "identifier (T_STRING)" %token <ast> T_VARIABLE "variable (T_VARIABLE)" %type <ast> top_statement namespace_name name statement function_declaration_statement %type <ast> class_declaration_statement trait_declaration_statement %type <ast> interface_declaration_statement interface_extends_list ``` 語法解析器從start開始調用,然后層層匹配各個規則,語法解析器根據命中的語法規則創建AST節點,最后將生成的AST根節點賦到 __CG(ast)__ : ```c %% /* Rules */ start: top_statement_list { CG(ast) = $1; } ; top_statement_list: top_statement_list top_statement { $$ = zend_ast_list_add($1, $2); } | /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_STMT_LIST); } ; ``` 首先會創建一個根節點list,然后將后面不斷命中top_statement生成的ast加到這個list中,zend_ast具體結構: ```c enum _zend_ast_kind { ZEND_AST_ZVAL = 1 << ZEND_AST_SPECIAL_SHIFT, ZEND_AST_ZNODE, /* list nodes */ ZEND_AST_ARG_LIST = 1 << ZEND_AST_IS_LIST_SHIFT, ... }; 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_list { zend_ast_kind kind; zend_ast_attr attr; uint32_t lineno; uint32_t children; zend_ast *child[1]; } zend_ast_list; ``` 根節點實際為zend_ast_list,每條語句對應的ast保存在child中,使用中zend_ast_list、zend_ast可以相互轉化,kind標識的是ast節點類型,后面會根據這個值生成具體的opcode,另外函數、類還會用到另外一種ast節點結構: ```c typedef struct _zend_ast_decl { zend_ast_kind kind; zend_ast_attr attr; /* Unused - for structure compatibility */ uint32_t start_lineno; //開始行號 uint32_t end_lineno; //結束行號 uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; zend_string *name; zend_ast *child[4]; //類中會將繼承的父類、實現的接口以及類中的語句解析保存在child中 } zend_ast_decl; ``` 這么看比較難理解,接下來我們從一個簡單的例子看下最終生成的語法樹。 ```php $a = 123; $b = "hi~"; echo $a,$b; ``` 具體解析過程這里不再解釋,有興趣的可以翻下zend_language_parse.y中,這個過程不太容易理解,需要多領悟幾遍,最后生成的ast如下圖: ![](https://box.kancloud.cn/3989dc29734e977a9e49740f78384647_1098x493.png) __總結:__ 這一節我們主要介紹了PHP詞法、語法解析生成抽象語法樹(AST)的過程,此過程是PHP語法實現的基礎,也是zend引擎非常關鍵的一部分,后續介紹的內容都是基于此過程的產出結果展開的。這部分內容關鍵在于對re2c、bison的應用上,如果是初次接觸它們可能不太容易理解,這里不再對re2c、bison作更多解釋,想要了解更多的推薦看下 __《flex與bison》__ 這本書。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看