<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                在前面我們提到語言轉化的編譯過程一般分為詞法分析、語法分析、語義分析、中間代碼生成、代碼優化、目標代碼生成等六個階段。不管是編譯型語言還是解釋型語言,掃描(詞法分析)總是將程序轉化成目標語言的第一步。詞法分析的作用就是將整個源程序分解成一個一個的單詞,這樣做可以在一定程度上減少后面分析工作需要處理的個體數量,為語法分析等做準備。除了拆分工作,更多的時候它還承擔著清洗源程序的過程,比如清除空格,清除注釋等。詞法分析作為編譯過程的第一步,在業界已經有多種成熟工具,如PHP在開始使用的是Flex,之后改為re2c,MySQL的詞法分析使用的Flex,除此之外還有作為UNIX系統標準詞法分析器的Lex等。這些工具都會讀進一個代表詞法分析器規則的輸入字符串流,然后輸出以C語言實做的詞法分析器源代碼。這里我們只介紹PHP的現版詞法分析器,re2c。 [re2c](http://www.re2c.org/)是一個掃描器制作工具,可以創建非常快速靈活的掃描器。它可以產生高效代碼,基于C語言,可以支持C/C++代碼。與其它類似的掃描器不同,它偏重于為正則表達式產生高效代碼(和他的名字一樣)。因此,這比傳統的詞法分析器有更廣泛的應用范圍。你可以在[sourceforge.net](http://sourceforge.net/projects/re2c/)獲取源碼。 PHP在最開始的詞法解析器是使用的是Flex,后來改為使用re2c。在源碼目錄下的Zend/zend_language_scanner.l 文件是re2c的規則文件,如果需要修改該規則文件需要安裝re2c才能重新編譯,生成新的規則文件。 re2c調用方式: re2c [-bdefFghisuvVw1] [-o output] [-c [-t header]] file 我們通過一個簡單的例子來看下re2c。如下是一個簡單的掃描器,它的作用是判斷所給的字符串是數字/小寫字母/大小字母。當然,這里沒有做一些輸入錯誤判斷等異常操作處理。示例如下: #include <stdio.h> ? char *scan(char *p){ #define YYCTYPE char #define YYCURSOR p #define YYLIMIT p #define YYMARKER q #define YYFILL(n) /*!re2c [0-9]+ {return "number";} [a-z]+ {return "lower";} [A-Z]+ {return "upper";} [^] {return "unkown";} */ } ? int main(int argc, char* argv[]) { printf("%s\n", scan(argv[1])); ? return 0; } 如果你是在ubuntu環境下,可以執行下面的命令生成可執行文件。 re2c -o a.c a.l gcc a.c -o a chmod +x a ./a 1000 此時程序會輸出number。 我們解釋一下我們用到的幾個re2c約定的宏。 - YYCTYPE 用于保存輸入符號的類型,通常為char型和unsigned char型 - YYCURSOR 指向當前輸入標記, -當開始時,它指向當前標記的第一個字符,當結束時,它指向下一個標記的第一個字符 - YYFILL(n) 當生成的代碼需要重新加載緩存的標記時,則會調用YYFILL(n)。 - YYLIMIT 緩存的最后一個字符,生成的代碼會反復比較YYCURSOR和YYLIMIT,以確定是否需要重新填充緩沖區。 參照如上幾個標識的說明,可以較清楚的理解生成的a.c文件,當然,re2c不會僅僅只有上面代碼所顯示的標記,這只是一個簡單示例,更多的標識說明和幫助信息請移步 [re2c幫助文檔](http://re2c.org/manual.html):[http://re2c.org/manual.html](http://re2c.org/manual.html)。 我們回過頭來看PHP的詞法規則文件zend_language_scanner.l。你會發現前面的簡單示例與它最大的區別在于每個規則前面都會有一個條件表達式。 > NOTE re2c中條件表達式相關的宏為YYSETCONDITION和YYGETCONDITION,分別表示設置條件范圍和獲取條件范圍。 在PHP的詞法規則中共有10種,其全部在zend_language_scanner_def.h文件中。此文件并非手寫, 而是re2c自動生成的。如果需要生成和使用條件表達式,在編譯成c時需要添加-c 和-t參數。 在PHP的詞法解析中,它有一個全局變量:language_scanner_globals,此變量為一結構體,記錄當前re2c解析的狀態,文件信息,解析過程信息等。它在zend_language_scanner.l文件中直接定義如下: #ifdef ZTS ZEND_API ts_rsrc_id language_scanner_globals_id; #else ZEND_API zend_php_scanner_globals language_scanner_globals; #endif 在zend_language_scanner.l文件中寫的C代碼在使用re2c生成C代碼時會直接復制到新生成的C代碼文件中。這個變量貫穿了PHP詞法解析的全過程,并且一些re2c的實現也依賴于此,比如前面說到的條件表達式的存儲及獲取,就需要此變量的協助,我們看這兩個宏在PHP詞法中的定義: // 存在于zend_language_scanner.l文件中 #define YYGETCONDITION() SCNG(yy_state) #define YYSETCONDITION(s) SCNG(yy_state) = s #define SCNG LANG_SCNG ? // 存在于zend_globals_macros.h文件中 # define LANG_SCNG(v) (language_scanner_globals.v) 結合前面的全局變量和條件表達式宏的定義,我們可以知道PHP的詞法解析是通過全局變量在一次解析過程中存在。那么這個條件表達式具體是怎么使用的呢?我們看下面一個例子。這是一個可以識別為結束,識別字符,數字等的簡單字符串識別器。它使用了re2c的條件表達式,代碼如下: #include <stdio.h> #include "demo_def.h" #include "demo.h" ? Scanner scanner_globals; ? #define YYCTYPE char #define YYFILL(n) #define STATE(name) yyc##name #define BEGIN(state) YYSETCONDITION(STATE(state)) #define LANG_SCNG(v) (scanner_globals.v) #define SCNG LANG_SCNG ? #define YYGETCONDITION() SCNG(yy_state) #define YYSETCONDITION(s) SCNG(yy_state) = s #define YYCURSOR SCNG(yy_cursor) #define YYLIMIT SCNG(yy_limit) #define YYMARKER SCNG(yy_marker) ? int scan(){ /*!re2c ? <INITIAL>"<?php" {BEGIN(ST_IN_SCRIPTING); return T_BEGIN;} <ST_IN_SCRIPTING>[0-9]+ {return T_NUMBER;} <ST_IN_SCRIPTING>[ \n\t\r]+ {return T_WHITESPACE;} <ST_IN_SCRIPTING>"exit" { return T_EXIT; } <ST_IN_SCRIPTING>[a-z]+ {return T_LOWER_CHAR;} <ST_IN_SCRIPTING>[A-Z]+ {return T_UPPER_CHAR;} <ST_IN_SCRIPTING>"?>" {return T_END;} ? <ST_IN_SCRIPTING>[^] {return T_UNKNOWN;} <*>[^] {return T_INPUT_ERROR;} */ } ? void print_token(int token) { switch (token) { case T_BEGIN: printf("%s\n", "begin");break; case T_NUMBER: printf("%s\n", "number");break; case T_LOWER_CHAR: printf("%s\n", "lower char");break; case T_UPPER_CHAR: printf("%s\n", "upper char");break; case T_EXIT: printf("%s\n", "exit");break; case T_UNKNOWN: printf("%s\n", "unknown");break; case T_INPUT_ERROR: printf("%s\n", "input error");break; case T_END: printf("%s\n", "end");break; } } ? int main(int argc, char* argv[]) { int token; BEGIN(INITIAL); // 全局初始化,需要放在scan調用之前 scanner_globals.yy_cursor = argv[1]; //將輸入的第一個參數作為要解析的字符串 ? while(token = scan()) { if (token == T_INPUT_ERROR) { [printf](http://www.opengroup.org/onlinepubs/009695399/functions/printf.html)("%s\n", "input error"); break; } if (token == T_END) { [printf](http://www.opengroup.org/onlinepubs/009695399/functions/printf.html)("%s\n", "end"); break; } print_token(token); } ? return 0; } 和前面的簡單示例一樣,如果你是在linux環境下,可以使用如下命令生成可執行文件 re2c -o demo.c -c -t demo_def.h demo.l gcc demo.c -o demo -g chmod +x demo 在使用re2c生成C代碼時我們使用了-c -t demo_def.h參數,這表示我們使用了條件表達式模式,生成條件的定義頭文件。main函數中,在調用scan函數之前我們需要初始化條件狀態,將其設置為INITIAL狀態。然后在掃描過程中會直接識別出INITIAL狀態,然后匹配<?php字符串識別為開始,如果開始不為<?php,則輸出input error。在掃描的正常流程中,當掃描出<?php后,while循環繼續向下走,此時會再次調用scan函數,當前條件狀態為ST_IN_SCRIPTING,此時會跳過INITIAL狀態,直接匹配<ST_IN_SCRIPTING>狀態后的規則。如果所有的<ST_IN_SCRIPTING>后的規則都無法匹配,輸出unkwon。這只是一個簡單的識別示例,但是它是從PHP的詞法掃描器中抽離出來的,其實現過程和原理類似。 那么這種條件狀態是如何實現的呢?我們查看demo.c文件,發現在scan函數開始后有一個跳轉語句: int scan(){ ? #line 25 "demo.c" { YYCTYPE yych; switch (YYGETCONDITION()) { case yycINITIAL: goto yyc_INITIAL; case yycST_IN_SCRIPTING: goto yyc_ST_IN_SCRIPTING; } ... } 在zend_language_scanner.c文件的lex_scan函數中也有類型的跳轉過程,只是過程相對這里來說if語句多一些,復雜一些。這就是re2c條件表達式的實現原理。
                  <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>

                              哎呀哎呀视频在线观看