《用 c 語言開發一門程式語言 — 互動式解析器l》
《用 c 語言開發一門程式語言 — 跨平台的可移植性》
《用 c 語言開發一門程式語言 — 語法解析器》
《用 c 語言開發一門程式語言 — 抽象語法樹》
在開發過程中,程式崩潰是很正常的。但我們希望最後發布的產品能夠告訴使用者錯誤出在**,而不是簡單粗暴的退出。目前,我們的程式僅能列印出語法上的錯誤,但對於表示式求值過程中產生的錯誤卻無能為力。
c 語言有很多種錯誤處理方式,但針對當前的專案,我們考慮將錯誤也作為表示式求值的一種結果。也就是說,在 lispy 中,表示式求值的結果要麼是數字,要麼便是錯誤。舉例說,表示式+ 1 2
求值會得到數字 3,而表示式/ 10 0
求值則會得到乙個錯誤。
為了達到這個目的,我們需要能表示這兩種結果(成功 or 失敗)的資料結構。簡單起見,我們使用結構體來表示,並使用type
欄位來說明當前哪個欄位是有意義的。結構體名為 lval,取義 lisp value,定義如下:
/* declare new lval struct */
typedef
struct
lval;
lval 的 type 和 err 欄位的型別都是 int,這意味著它們皆由整數值來表示。之所以選用 int,是因為 「成功或失敗」 符合二元對立的情形。但 c 語言中,沒有 true or false 這樣的 boolean 資料型別,所以我們使用 0/1 代替:
並且,我們可以給這些數字起乙個有意義的名字,以提高**的可讀性。通過整型、別名這兩個特徵,我們很自然的會聯想到列舉資料型別:
/* create enumeration of possible lval types */
enum
;
另外,error 也必然是可以列舉的,所以同樣使用列舉資料型別:
/* create enumeration of possible error types */
enum
;
我們再定義兩個函式來完成 「lval 型別例項」 的初始化:
/* create a new number type lval
* 因為使用無名建立方式定義的 lval 結構體是自定義資料型別,
* 所以我們可以使用 lval 來宣告函式返回值型別。
*/lval lval_num
(long x)
/* create a new error type lval */
lval lval_err
(int x)
/* print an "lval" */
void
lval_print
(lval v)
if(v.err == lerr_bad_op)
if(v.err == lerr_bad_num)
break;}
}/* print an "lval" followed by a newline */
void
lval_println
(lval v)
最後,我們使用 lval 型別來替換掉之前使用的 long 型別,此外,我們還需要修改函式使其能正確處理數字或是錯誤作為輸入的情況:
#include
#include
#include
"mpc.h"
#ifdef _win32
#include
static
char buffer[
2048];
char
*readline
(char
*prompt)
void
add_history
(char
*unused)
#else
#ifdef __linux__
#include
#include
#endif
#ifdef __mach__
#include
#endif
#endif
/* create enumeration of possible lval types */
enum
;/* create enumeration of possible error types */
enum
;/* declare new lval struct
* 使用 lval 列舉型別來替換掉之前使用的 long 型別。
* 單存的 long 型別沒辦法攜帶成功或失敗、若失敗,是什麼失敗等資訊。
* 所以我們定義 lval 列舉型別來作為 「運算元」 及 「結果」。
*/typedef
struct
lval;
/* create a new number type lval */
lval lval_num
(long x)
/* create a new error type lval */
lval lval_err
(long x)
/* print an "lval"
* 通過對 lval 列舉型別變數的解析來完成對計算結果的解析。
*/void
lval_print
(lval v)
else
if(v.err == lerr_bad_op)
else
if(v.err == lerr_bad_num)
break;}
}/* print an "lval" followed by a newline */
void
lval_println
(lval v)
/* use operator string to see which operation to perform */
lval eval_op
(lval x,
char
*op, lval y)
if(y.type == lval_err)
/* otherwise do maths on the number values
* 如果 「運算元」 是 number,則取出運算元進行運算。
*/if(
strcmp
(op,
"+")==0
)if(strcmp
(op,
"-")==0
)if(strcmp
(op,
"*")==0
)if(strcmp
(op,
"/")==0
)}return
lval_err
(lerr_bad_op);}
lval eval
(mpc_ast_t *t)
/* the operator is always second child. */
char
*op = t->children[1]
->contents;
/* we store the third child in `x` */
lval x =
eval
(t->children[2]
);/* iterate the remaining children and combining. */
int i =3;
while
(strstr
(t->children[i]
->tag,
"expr"))
return x;
}int
main
(int argc,
char
*ar**)
else
free
(input);}
/* undefine and delete our parsers */
mpc_cleanup(4
, number, operator, expr, lispy)
;return0;
}
編譯:
gcc -g -std=c99 -wall parsing.c mpc.c -lreadline -lm -o parsing
執行:
$ ./parsing
lispy version 0.1
press ctrl+c to exit
lispy> / 10 0
error: division by zero!
lispy> / 10 2
5lispy>
:1:1: error: expected '+', '-', '*' or '/' at end of input
lispy> / 10 2
5
用 C 語言開發一門程式語言 抽象語法樹
抽象語法樹與行為樹 用 c 語言開發一門程式語言 互動式解析器l 用 c 語言開發一門程式語言 跨平台的可移植性 用 c 語言開發一門程式語言 語法解析器 lispy 5 2 2 regex operator char 1 1 expr number regex 1 3 5 expr char 1 ...
如何開發一門程式語言
首先,你要考慮這是動態語言還是靜態語言,然後去想它面向什麼,如web開發,物件導向的程式設計等。還有它的語法,下面列出了物件導向的程式語言所需要的語句 語句用途 if 表示式1 執行語句1 else 執行語句2 判斷如果表示式1,則執行語句1,否則,執行語句2 cout 輸出cin 輸入int 變數...
用 C 語言開發一門程式語言 互動式直譯器
通過開發一門類 lisp 的程式語言來理解程式語言的設計思想,本實踐來自著名的 build your own lisp 語言主要有兩種型別 編譯型和解釋型。技術上,任何語言都可以被編譯或解釋,但是一種或另一種語言通常對於特定語言更有意義。一般來說,解釋往往更加靈活,而編譯往往具有更高的效能。當我們希...