用 C 語言開發一門程式語言 異常處理

2021-10-04 20:33:47 字數 4509 閱讀 8720

《用 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 語言主要有兩種型別 編譯型和解釋型。技術上,任何語言都可以被編譯或解釋,但是一種或另一種語言通常對於特定語言更有意義。一般來說,解釋往往更加靈活,而編譯往往具有更高的效能。當我們希...