除了錯誤處理文件還沒有定義好以外,現(xiàn)在語法定義跟語法樹的數(shù)據(jù)結(jié)構(gòu)定義以及分析器都完成了!有了這兩個文件,我的工具就可以替你生成一個函數(shù)和一堆類,讓你使用這個函數(shù)就可以將一份代碼轉(zhuǎn)換為一顆語法樹啦。娃哈哈……
現(xiàn)在讓我們來看一個例子,這個例子展示了一個科學(xué)計算器的表達式的結(jié)構(gòu)定義以及語法定義。首先是數(shù)據(jù)結(jié)構(gòu)的定義:
enum BinopType
{
Plus
Minus
Mul
Div
}
enum SinopType
{
Negative
}
class Expression
{
}
class Number Expression
{
double Number
}
class Binop Expression
{
BinopType Operator
Expression^ LeftOp
Expression^ RightOp
}
class Sinop Expression
{
SinopType Operator
Expression^ Operand
}
class Invoke Expression
{
string Name;
list<Expression^> Parameters;
}
Type^的意思是autoptr<Type>。因為語義已經(jīng)決定了這玩意兒是生成樹的,所以我們不需要考慮循環(huán)引用的問題。如果你不改動這個樹的話,你可以不必刪除,autoptr自己會將這些問題處理掉的。內(nèi)存管理是一件多么麻煩的事情啊,于是我解決掉了。class后面的第一個名稱是類名,如果第二個名稱存在的話則代表父類的類名,可以用來表達繼承關(guān)系。
那么現(xiàn)在看看語法的定義:
num ="\d+(.\d+)?" ;
ident ="[a-zA-Z_]\w*" ;
plus ="\+" ;
minus ="\-" ;
mul ="\*" ;
div ="/" ;
leftbrace ="\(" ;
rightbrace ="\)" ;
comma ="," ;
discard ="\s+" ;
Number^ factor=num{Number};
Sinop^ factor=minus{Operator=Negative} factor{Operand};
Expression^ factor=leftbrace exp{#result} rightbrace;
Invoke^ factor=ident{Name}[leftbrace param_list{Parameters} rightbrace];
Expression^ term =factor{#result};
Binop^ term=term{LeftOp} (mul{Operator=Mul}|div{Operator=Div}) factor{RightOp};
Expression^ exp=term{#result};
Binop^ exp=exp{LeftOp} (plus{Operator=Plus}|minus{Operator=Minus}) term{RightOp};
list<Expression^> param_list=exp{#insert} [comma param_list{#result}];
Expression^ program=exp{#result};
我們來看其中的兩條文法。第一條文法:list<Expression^>param_list=exp{#insert}[comma param_list{#result}];。文法推導(dǎo)式子返回list<Expression^>類型的對象。如果param_list存在那么將param_list當成當前的列表,否則創(chuàng)建一個新列表。然后將exp使用insert方法插入列表的頭部。最后返回列表。
第二條文法:Binop^ exp=exp{LeftOp} (plus{Operator=Plus}|minus{Operator=Minus}) term{RightOp};。文法推導(dǎo)式子返回Binop^類型的對象。將exp放入成員LeftOp,將term放入成員RightOp,如果遇到plus則將Operator設(shè)置為BinopType::Plus,否則設(shè)置為BinopType::Minus。參考一下前面對于類Binop以及枚舉類型BinopType的定義就知道了。
文法從program開始,分析器檢查輸入的字符串,得到一個正確的推倒之后,就是用這些語義命令來構(gòu)造所需的數(shù)據(jù)結(jié)構(gòu)。當整個過程被生成為C++代碼之后,一切都變得非常的方便。等代碼生成部分完成之后(這一部分由于將產(chǎn)生基于我半年前做的Syngram庫的代碼,由于Syngram支持在C++里面直接寫文法,所以代碼生成是沒有什么難度的,而且做起來很快)就算不會處理字符串也可以寫文法分析器啦!