#頭條創作挑戰賽#
我寫的scf編譯器,并沒有嚴格按照C語言的文法來寫。
我寫scf的目标是:
1,在C語言的基礎上,做一門更完善的、文法好看的程式設計語言。
文法不能像go語言那麼難看[捂臉]
2,給出一套完全自主知識産權的編譯器架構,
這個架構要盡可能簡單,并且可以作為開發其他程式設計語言的基礎架構。
類似llvm那樣,隻是我懶得去看llvm的文檔,我嫌它太麻煩了。
是以,scf前端的文法跟C語言幾乎一樣,但有點差别:
1,我還沒給它支援switch case語句[呲牙]
因為if else的使用範圍更廣,是以我就先支援了if else,把switch case暫時放到以後了。
2,支援while,但不支援do while。
因為根據我寫C代碼的經驗,do while用到的特别少。
3,for循環是肯定支援的。
4,編譯器不再區分頭檔案和源檔案,而是隻有一種檔案。
可以使用include關鍵字包含其他的代碼檔案。
可以把類型定義和函數聲明都寫在一個檔案裡,把它當作頭檔案去包含。
代碼在檔案裡怎麼分布,完全交給了程式員。
5,不需要考慮檔案的重複包含問題。
我在文法分析時做了去重處理,不需要像C語言那樣使用ifndef去防止重複包含。
6,沒有支援宏,
宏的展開還是比較麻煩的,不想把文法分析做得太複雜。
7,沒有格式限制,
回車、換行、Tab都一律當空格。
語句的結束以分号為标志,語句塊的結束以(右邊的)大括号為标志。
對于scf編譯器來說,換行符的作用隻是在debug資訊裡記錄行号[捂臉]
8,源檔案的擴充名暫時設定為.c,在scf的main()函數的第65行可以修改。
之是以設定為.c,是因為大多數的文本編輯器都對C語言有文法高亮功能,例如vim。
除了這裡之外,其他子產品都不會檢查擴充名,而是一律當成文本檔案處理。
編譯時輸入的檔案擴充名隻能是這4種:
.c表示源檔案,
.o表示目标檔案,
.a表示靜态庫,
.so表示動态庫,
隻會編譯源檔案.c,其他3種在連接配接時處理。
預設連接配接的庫檔案
預設連接配接的“庫檔案”如上圖:
1)_start.o,啟動main()函數用的,也是程式的真正入口。
2)scf_object.o,自動記憶體管理用的,
scf_atomic.o,自動記憶體管理用的,引用計數的原子操作。
3)最後那2個so庫,都是C語言的動态庫。
下面的3個例子都在scf/examples目錄:
1,hello.c,
它列印hello world。
hello world
因為要使用C标準庫的printf()函數,可以直接把它聲明在檔案的開頭。
沒有支援宏,沒法包含C語言的stdio.h,把用到的函數聲明一下就行了。
也可以把C庫的常用函數(例如printf, malloc, free)都聲明到一個檔案裡,以後隻包含那個檔案就行。
假設目前目錄是scf/parse,那麼編譯指令是:./scf ../examples/hello.c
2,qsort.c,
快速排序
因為最後要用printf()列印結果,是以也把它聲明在開頭。
快速排序的sort()函數有2層循環+2個遞歸,并且還有指針和數組的使用。
大多數情況下,我都是拿它來檢測scf編譯的代碼對不對。
編譯和運作結果
3,mat.c,
一個簡單的矩陣類。
mat類
因為使用了自動記憶體管理,先包含../lib/scf_capi.c,這個檔案裡有幾個自動記憶體管理的函數聲明,如下圖:
lib/scf_capi.c
include在scf裡作為關鍵字之一,用來包含其他的檔案。
然後聲明mat類,并且聲明它的成員變量。
scf對類的成員沒有權限控制,在類内和類外都可以通路。
構造函數__init()
構造函數:名字是__init(),傳回值是int,第一個參數是this指針,明确地寫在第一個位置。
構造函數的傳回值可以傳回錯誤碼,成功傳回0。
加号+的重載函數
加号的重載operator+()函數:
operator也是關鍵字,跟C++一樣。
它的2個參數都明确地寫出來:
第一個是this,位于加号左邊的操作數,
第2個是that,加号右邊的操作數。
傳回值有2個,mat*是計算結果,int是錯誤碼(成功為0)。
加号+的重載函數2
析構函數:名字是__release(),隻有1個參數this,明确地寫出來。
析構函數__release()
mat類的成員變量裡隻有uint8_t* data是指針,需要申請記憶體,是以在析構函數裡隻釋放它就可以。
它是需要自動記憶體管理的,使用scf__auto_freep()釋放:這樣如果它指向的記憶體被其他指針引用了的話,不會出現錯誤釋放。
例如這樣的代碼:
mat* a = create mat(); // 這時a->data的記憶體引用計數是1,
uint8_t* p = a->data; // 這時a->data的記憶體引用計數是2,因為指針p也指向了同一塊記憶體,
釋放a之後,p指向的記憶體應該還能用:這時a->data的引用計數隻是減1,并沒有真正free記憶體。
釋放p之後,這塊記憶體才被真正釋放,因為沒有指針指向它了。
自動記憶體管理的代碼都在scf/lib/scf_object.c裡,連接配接scf_object.o就行。
main()函數
最後是main()函數:
用3個double數組作為參數,建立了3個mat矩陣的對象。
然後進行了一個加法運算。
最後列印結果:
我沒有關閉自動記憶體管理子產品的日志,是以連記憶體申請和釋放的過程都打出來了。
紅框裡才是main()函數列印的運算結果。
編譯運作的結果