<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國際加速解決方案。 廣告
                # 手把手教你做一個 C 語言編譯器(5):變量定義 本章中我們用 EBNF 來大致描述我們實現的 C 語言的文法,并實現其中解析變量定義部分。 由于語法分析本身比較復雜,所以我們將它拆分成 3 個部分進行講解,分別是:變量定義、函數定義、表達式。 **本系列:** 1. [手把手教你做一個 C 語言編譯器(0):前言](http://blog.jobbole.com/97332/) 2. [手把手教你做一個 C 語言編譯器(1):設計](http://blog.jobbole.com/97350/) 3. [手把手教你做一個 C 語言編譯器(2):虛擬機](http://blog.jobbole.com/97359/) 4. [手把手教你做一個 C 語言編譯器(3):詞法分析器](http://blog.jobbole.com/97375/) 5. [手把手教你做一個 C 語言編譯器(4):遞歸下降](http://blog.jobbole.com/97382/) ## EBNF 表示 EBNF 是對前一章提到的 BNF 的擴展,它的語法更容易理解,實現起來也更直觀。但真正看起來還是很煩,如果不想看可以跳過。 ``` program ::= {global_declaration}+ global_declaration ::= enum_decl | variable_decl | function_decl enum_decl ::= 'enum' [id] '{' id ['=' 'num'] {',' id ['=' 'num'} '}' variable_decl ::= type {'*'} id { ',' {'*'} id } ';' function_decl ::= type {'*'} id '(' parameter_decl ')' '{' body_decl '}' parameter_decl ::= type {'*'} id {',' type {'*'} id} body_decl ::= {variable_decl}, {statement} statement ::= non_empty_statement | empty_statement non_empty_statement ::= if_statement | while_statement | '{' statement '}' | 'return' expression | expression ';' if_statement ::= 'if' '(' expression ')' statement ['else' non_empty_statement] while_statement ::= 'while' '(' expression ')' non_empty_statement ``` 其中 `expression` 相關的內容我們放到后面解釋,主要原因是我們的語言不支持跨函數遞歸,而為了實現自舉,實際上我們也不能使用遞歸(虧我們說了一章的遞歸下降)。 P.S. 我是先寫程序再總結上面的文法,所以實際上它們間的對應關系并不是特別明顯。 ## 解析變量的定義 本章要講解的就是上節文法中的 `enum_decl` 和 `variable_decl` 部分。 ### program() 首先是之前定義過的 `program` 函數,將它改成: ``` void program() { // get next token next(); while (token > 0) { global_declaration(); } } ``` 我知道 `global_declaration` 函數還沒有出現過,但沒有關系,采用自頂向下的編寫方法就是要不斷地實現我們需要的內容。下面是 `global_declaration` 函數的內容: ### global_declaration() 即全局的定義語句,包括變量定義,類型定義(只支持枚舉)及函數定義。代碼如下: ``` int basetype; // the type of a declaration, make it global for convenience int expr_type; // the type of an expression void global_declaration() { // global_declaration ::= enum_decl | variable_decl | function_decl // // enum_decl ::= 'enum' [id] '{' id ['=' 'num'] {',' id ['=' 'num'} '}' // // variable_decl ::= type {'*'} id { ',' {'*'} id } ';' // // function_decl ::= type {'*'} id '(' parameter_decl ')' '{' body_decl '}' int type; // tmp, actual type for variable int i; // tmp basetype = INT; // parse enum, this should be treated alone. if (token == Enum) { // enum [id] { a = 10, b = 20, ... } match(Enum); if (token != '{') { match(Id); // skip the [id] part } if (token == '{') { // parse the assign part match('{'); enum_declaration(); match('}'); } match(';'); return; } // parse type information if (token == Int) { match(Int); } else if (token == Char) { match(Char); basetype = CHAR; } // parse the comma seperated variable declaration. while (token != ';' && token != '}') { type = basetype; // parse pointer type, note that there may exist `int ****x;` while (token == Mul) { match(Mul); type = type + PTR; } if (token != Id) { // invalid declaration printf("%d: bad global declaration\n", line); exit(-1); } if (current_id[Class]) { // identifier exists printf("%d: duplicate global declaration\n", line); exit(-1); } match(Id); current_id[Type] = type; if (token == '(') { current_id[Class] = Fun; current_id[Value] = (int)(text + 1); // the memory address of function function_declaration(); } else { // variable declaration current_id[Class] = Glo; // global variable current_id[Value] = (int)data; // assign memory address data = data + sizeof(int); } if (token == ',') { match(','); } } next(); } ``` 看了上面的代碼,能大概理解嗎?這里我們講解其中的一些細節。 **向前看標記** :其中的 `if (token == xxx)` 語句就是用來向前查看標記以確定使用哪一個產生式,例如只要遇到 `enum` 我們就知道是需要解析枚舉類型。而如果只解析到類型,如 `int identifier` 時我們并不能確定 `identifier` 是一個普通的變量還是一個函數,所以還需要繼續查看后續的標記,如果遇到 `(` 則可以斷定是函數了,反之則是變量。 **變量類型的表示** :我們的編譯器支持指針類型,那意味著也支持指針的指針,如 `int **data;`。那么我們如何表示指針類型呢?前文中我們定義了支持的類型: ``` // types of variable/function enum { CHAR, INT, PTR }; ``` 所以一個類型首先有基本類型,如 `CHAR` 或 `INT`,當它是一個指向基本類型的指針時,如`int *data`,我們就將它的類型加上 `PTR` 即代碼中的:`type = type + PTR;`。同理,如果是指針的指針,則再加上 `PTR`。 ### enum_declaration() 用于解析枚舉類型的定義。主要的邏輯用于解析用逗號(`,`)分隔的變量,值得注意的是在編譯器中如何保存枚舉變量的信息。 即我們將該變量的類別設置成了 `Num`,這樣它就成了全局的常量了,而注意到上節中,正常的全局變量的類別則是 `Glo`,類別信息在后面章節中解析 `expression` 會使用到。 ``` void enum_declaration() { // parse enum [id] { a = 1, b = 3, ...} int i; i = 0; while (token != '}') { if (token != Id) { printf("%d: bad enum identifier %d\n", line, token); exit(-1); } next(); if (token == Assign) { // like {a=10} next(); if (token != Num) { printf("%d: bad enum initializer\n", line); exit(-1); } i = token_val; next(); } current_id[Class] = Num; current_id[Type] = INT; current_id[Value] = i++; if (token == ',') { next(); } } } ``` ### 其它 其中的 `function_declaration` 函數我們將放到下一章中講解。`match` 函數是一個輔助函數: ``` void match(int tk) { if (token == tk) { next(); } else { printf("%d: expected token: %d\n", line, tk); exit(-1); } } ``` 它將 `next` 函數包裝起來,如果不是預期的標記則報錯并退出。 ## 代碼 本章的代碼可以在 [Github](https://github.com/lotabout/write-a-C-interpreter/tree/step-3) 上下載,也可以直接 clone ``` git clone -b step-3 https://github.com/lotabout/write-a-C-interpreter ``` 本章的代碼還無法正常運行,因為還有許多功能沒有實現,但如果有興趣的話,可以自己先試著去實現它。 ## 小結 本章的內容應該不難,除了開頭的 EBNF 表達式可能相對不好理解一些,但如果你查看了 EBNF 的具體表示方法后就不難理解了。 剩下的內容就是按部就班地將 EBNF 的產生式轉換成函數的過程,如果你理解了上一章中的內容,相信這部分也不難理解。 下一章中我們將介紹如何解析函數的定義,敬請期待。
                  <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>

                              哎呀哎呀视频在线观看