用c語言手搓一個500+行的類c語言解釋器: 給編程初學者的編譯器教程(1)- 目標和前言
項目github地址及源碼:
https://github.com/yunwei37/tryC
一個小目標
這一系列教程希望面向初學者,使用c語言手工實現一個簡單的解釋器來玩,不需要您掌握除了c語言以外的其他前置知識,也不需要您學習過編譯原理的相關知識(當然如果能對簡單的數據結構有所瞭解的話會更好,比如樹、棧等)。
寫一個能執行代碼的解釋器不僅是一件很有(zhuang)趣(bi)的事情,大概也可以作為剛學習完c語言的一個練手的小項目啦
不同於大部分常見的其他只支持四則運算的所謂”手工解釋器“教程,我們希望在代碼結構儘量清晰的600行代碼中,手工(不借助lex/yacc等工具)完成一個腳本語言“try”,實現以下功能:
- 選擇和循環的流程控制語句
- 支持的數據類型:雙精度浮點數、字符型、字符串、浮點數數組
- 支持函數和變量的定義、函數的遞歸調用、嵌套作用域
(如果看不懂下面這段也沒關係,可以略過啦)
這個小玩意採用遞歸下降法進行語法分析,同時不顯式構建語法樹,不生成中間代碼或目標代碼,在語法分析的同時進行解釋執行;
解釋器可運行的代碼示例
遞歸計算文波那契數列 1 - 15,將結果存入數組中,並打印:
# Fibonacci sequence
func fun{
if(x <= 2){
return(1);
}
y = 0;
x = x - 1;
y = fun(x);
x = x - 1;
return(y + fun(x));
};
# save the Fibonacci sequence of 1 to 15 in an array
array arr(15);
x = 1;
while( x <= 15 ){
arr[x - 1] = fun(x);
x = x + 1;
}
puts("Fibonacci sequence:");
# print the Fibonacci sequence of 1 to 15
i = 0;
while(i < 15){
print(arr[i]);
i=i+1;
}
(起名困難x)這個小玩意我們就隨便叫它tryC吧,當做是一個小的嘗試。
本人水平有限,如有疏漏之處,還請多多指教。
部分語言規則:
- 註釋在一行內,以‘#’開頭;
- 語句以‘;’結尾
-
賦值語句類型:
x = 123.4; x = 'c'; x = "hello world!";
-
循環語句:
while( bool ){ statements }
-
選擇語句:
if( bool ){ statements } if( bool ){ statements }else{ statements }
-
定義函數:函數參數在定義中不出現,在調用中獲取;返回值為double
func function_name{ ... return(expression); }
-
定義數組:
array array_name(array_length);
-
輸入輸出:
puts(string); print(num); read(num);
寫在前面
關於寫這玩意的緣由(寫的很亂可以不看系列)
之前大一學c語言的時候,老師要求實現一個四則運算的計算器,於是我想...要是能給計算器加上函數和變量的定義就好啦...那大概能算一個簡單的解釋器?我應該怎樣去實現它呢?就去查了不少資料七拼八湊加上自己腦補搓了一個出來...雖然能跑起來但是代碼混亂不堪一塌糊塗,不過也挺好玩的。
這裡的部分是過了一年之後大二學編譯原理的時候,把當時的代碼用相對比較規範完善的方式重寫了一遍,也因此希望把它整理成一個簡單的教程,讓c語言的初學者也可以愉快地搓一個解釋器玩;或者讓學過編譯原理的同學,能夠把理論和實踐聯繫起來,(不要像我一樣被一大堆的理論迷惑住或嚇跑),對於如何構造一個解釋器有個直觀感性的認識,並且發現它並不像想象的那麼困難。
需要了解的前置知識
- c語言的指針、函數指針、結構體等
- 遞歸的思想
心理準備
- 寫一個600行的解釋器雖然不算什麼大工程,但相關的原理還是稍微有些複雜的,可能需要多花一些時間理解程序的運行過程;
- 代碼可能難以調試,尤其在沒有生成中間代碼的情況下;
參考資料
- 《編譯原理及其實踐》
-
用四個函數和很少的代碼就完成了功能相當完善的 C 語言編譯器, 並且能夠自舉;我自己寫作的時候也借鑑了c4的許多實現思想;
-
對c4的一個重寫版,附有詳細的中文教程
-
Let's Build a Compiler, by Jack Crenshaw
一個英文的初學者教程,講解如何實現一個編譯器