C編譯器入門~想懂低階系統從自幹編譯器開始~
  • 譯者序
  • 前言
    • 符號與規範
    • 本書的開發環境
    • 關於作者
    • 結束前言之前
  • 機械語言與組譯器
    • CPU 與記憶體
    • 什麼是組譯器
    • C程式和所對應的組合語言
      • 簡單的範例
      • 包含呼叫函式的範例
    • 本章小結
  • 創造計算機等級的語言
    • 第1步:創造能編譯1個整數的語言
    • 第2步:製作可以算加減法的編譯器
    • 第3步:加入標記解析器(tokenizer)
    • 第4步:改良錯誤訊息
    • 文法的記法與遞迴下降分析法
      • 將文法結構表示為樹(tree)
      • 以生成規則定義文法
      • 以 BNF 描述生成規則
      • 簡單的生成規則
      • 以生成規則描述運算子的優先順序
      • 包含遞迴的生成規則
      • 遞迴下降語法分析
    • 堆疊機
      • 堆疊機的概念
      • 編譯成堆疊機指令
      • 以x86-64實作堆疊機的方法
    • 第5步:製作可進行四則運算的編譯器
    • 第6步:單項加與單項減
    • 第7步:比較運算子
      • 修改標記解析器
      • 新的文法
      • 產生組合語言指令
  • 分離編譯與連結
    • 分離編譯
      • 分離編譯與其必要性
      • 標頭檔的必要性與其內容
      • 連結錯誤
      • 全域變數的宣告與定義
    • 第8步:分割檔案與修改 Makefile
      • 分割檔案
      • 修改 Makefile
  • 函式與區域變數
    • 第9步:1個字的區域變數
      • 堆疊上的變數空間
      • 修改標記解析器
      • 修改分析器
      • 左邊值與右邊值
      • 從任意的記憶體位址取得其值
      • 修改指令產生器
      • 修改主函式
    • 第10步:複數文字的區域變數
    • 第11步:return
    • 1973年的C編譯器
Powered by GitBook
On this page

Was this helpful?

  1. 創造計算機等級的語言
  2. 第7步:比較運算子

新的文法

我們來考慮一下加入了比較運算子的文法會變成什麼樣子吧。至今為止出現的運算子的優先順序由低至高表列如下:

  1. == !=

  2. < <= > >=

  3. + -

  4. * /

  5. 單項+ 單項-

  6. ()

把優先順序分別對應到不同的終端符號,就可以用生成文法來表現優先順序。像expr和mul一樣的方法來思考,加上比較運算子的文法如下所示:

expr       = equality
equality   = relational ("==" relational | "!=" relational)*
relational = add ("<" add | "<=" add | ">" add | ">=" add)*
add        = mul ("+" mul | "-" mul)*
mul        = unary ("*" unary | "/" unary)*
unary      = ("+" | "-")? term
term       = num | "(" expr ")"

equality代表 ==和 !=,relational代表<、<=、>、>=。這類非終端符號運用左結合運算子的分析模式,可以直接對應成函式。

除此之外,為了以equality表示算式整體,把expr和equality分離。也可以把equality的右邊直接寫在expr,但是上述的寫法應該比較好讀。

小知識:單純但冗長的程式碼和進階的簡潔程式碼

遞迴下降分析法基本上是照搬生成規則寫成程式碼,所以根據相同規則分析的函式看起來會一模一樣。至今為止寫的relational、equality、add、mul應該就是長得一樣的函式。

看到像這樣在不同函式出現同樣的模式,應該很自然會想要利用C的巨集(macro)或C++的模板(template)、高階函式(higher-order function)或程式碼產生(code generation)等超程式設計(meta-programming,也譯作元程式設計)手法來進行抽象化吧。事實上是可以這樣做的,但本書刻意不這樣處理。其理由如下:

單純的程式碼就算有些冗長,也還是很好理解。就算事後要在這些很像的函式重複加上一樣的修改,其實也不用花太大的功夫。此外,高度抽象化的程式碼,要先知道其抽象化的原理,再進而搞懂該怎麼使用他們,就會變得較難以理解。舉例來說,本輸如果用超程式設計的手法來產生遞迴下降分析的程式碼開始講解,這本書應該會變得更難讀吧。

不需要隨時都以寫出簡潔寫技巧精練的程式碼為目標。如果這樣做,連本不該變難的程式都會變得很難寫。

自己讀自己寫的程式,和他人讀同樣的程式的感受是差很多的。寫程式的本人因為是這份程式碼的專家,會以專家的視角認為簡潔沒有多餘的程式碼才是好的程式碼。但是大部分的讀者並無法共感,而且說實話也不需要精通到那個程度,所以作為作者,要懷疑作為該部分專家的自己的感性。

就像文章是為了寫給讀者,程式碼也為了讀者而寫吧!不要忘了作為讀者讀程式碼的感受,在必要時去寫「應該有更好的寫法但單純的程式碼」,是做出易讀易維護的程式的重要技巧。

Previous修改標記解析器Next產生組合語言指令

Last updated 5 years ago

Was this helpful?