完整實(shí)現(xiàn)代碼及antlr語(yǔ)法規(guī)則文件在這里:https://github.com/czqasngit/DynamicDSL
上一節(jié),我們已經(jīng)實(shí)現(xiàn)了數(shù)據(jù)類型的定義,在此基礎(chǔ)上我們就可以來(lái)實(shí)現(xiàn)我們的數(shù)據(jù)節(jié)點(diǎn)了。
在實(shí)現(xiàn)某個(gè)具體節(jié)點(diǎn)(比如一無(wú)表達(dá)式節(jié)點(diǎn))之前,我們需要抽象出一個(gè)基類SemaASTNode
。
它的定義如下:
namespace DynamicDSL {
/// 整理后可運(yùn)算的AST
class SemaASTNode {
protected:
SemaContext *context;
public:
enum Type {
None = 1<<0,
Assign = 1<<1, /// 賦值表達(dá)式, 改變上下文環(huán)境中已存在變量的值
Declare = 1<<2, /// 申明變量,上下文環(huán)境中增加變量
Value = 1<<3 /// 求值表達(dá)式
};
/// 節(jié)點(diǎn)的類型
Type type;
SemaASTNodeObject object;
public:
SemaASTNode() {
this->type = None;
}
SemaASTNode(SemaContext *context, Type type) {
this->type = type;
/// 復(fù)制,單獨(dú)管理context的內(nèi)存
this->context = SemaContext::copy(context);
}
virtual ~SemaASTNode() {
//cout << "SemaASTNode: release" << endl;
delete context;
}
/// 求節(jié)點(diǎn)的值
virtual void run() { }
/// 獲取節(jié)點(diǎn)的值
SemaASTNodeObject getResult() { return object; }
};
};
基類定義了共有的數(shù)據(jù)SemaContext(執(zhí)行時(shí)的環(huán)境變量),Type(表達(dá)式類型),SemaASTNodeObject(表達(dá)式運(yùn)算結(jié)果)。
同時(shí)還定義了一個(gè)虛函數(shù),它抽象了節(jié)點(diǎn)結(jié)算的過(guò)程,每一種不同的節(jié)點(diǎn)都需要實(shí)現(xiàn)這個(gè)函數(shù)來(lái)完成具體節(jié)點(diǎn)的運(yùn)算,這樣就很方便的只需要調(diào)用節(jié)點(diǎn)的run我們就能得到想要的結(jié)果了。
virtual void run() { assert(false); }
運(yùn)算結(jié)果保存賦值給object,通過(guò)getResult()就可以取到節(jié)點(diǎn)的運(yùn)算結(jié)果。
接下來(lái)我們來(lái)看最簡(jiǎn)單的也是最重要的節(jié)點(diǎn)Primay節(jié)點(diǎn):SemaPrimaryASTNode
。
這個(gè)節(jié)點(diǎn)需要完成兩個(gè)小功能,第一就是ID標(biāo)識(shí)符的消解,我們需要將解析到的標(biāo)識(shí)符解析成最終要獲得的值。
比如我們有一個(gè)變量是age,他的值是30,在SemaPrimaryASTNode
里面我們就需要將age替換成30。
而實(shí)現(xiàn)這個(gè)邏輯的代碼就在run()函數(shù)里面,當(dāng)被調(diào)用的時(shí)候就替換成最終的值了。
void run() override {
if(idTokenText.empty() &&
stringTokenText.empty() &&
intTokenText.empty() &&
doubleTokenText.empty() &&
tfTokenText.empty()) {
object = SemaASTNodeObject(*context);
} else {
/// 這里對(duì)變量進(jìn)行消解
if(!idTokenText.empty()) {
object = this->context->getVariableValue(idTokenText);
} else if(!stringTokenText.empty()) {
object.setValue(stringTokenText.substr(1, stringTokenText.length() - 2));
} else if(!intTokenText.empty()) {
object.setValue(stod(intTokenText));
} else if(!doubleTokenText.empty()) {
object.setValue(stod(doubleTokenText));
} else if(!tfTokenText.empty()) {
if(tfTokenText == "true") {
object.setValue(true);
} else {
object.setValue(false);
}
} else {
cout << "[Warning] " << "未支持的類型" << endl;
}
}
}
接下來(lái)是一元運(yùn)算節(jié)點(diǎn): SemaUnaryASTNode
他的實(shí)現(xiàn)也很簡(jiǎn)單,因?yàn)槲覀儸F(xiàn)在只簡(jiǎn)單的支持了++ --運(yùn)算,所以我們要求他們的值類型一定是Number。
void run() override {
this->node->run();
this->object = node->getResult();
/// 僅Number支持
if(object.getType() == DynamicDSL::Number) {
if(op == "++") {
object.setValue(object.getValue<number>() + 1);
} else if(op == "--") {
object.setValue(object.getValue<number>() - 1);
} else {
throw "一元表達(dá)式暫不支持: " + op + " 運(yùn)算符";
}
} else {
cout << "[Warning] " << "++或--只能對(duì)Number類型有效, " << "當(dāng)前的類型是: " << object.getTypeText() << endl;
}
}
但是這里需要注意的是,SemaUnaryASTNode
包含了一個(gè)節(jié)點(diǎn),而這個(gè)節(jié)點(diǎn)求出來(lái)的值就是一個(gè)Number類型的數(shù)據(jù)。
它也許是一個(gè)簡(jiǎn)單的SemaPrimaryASTNode節(jié)點(diǎn),也許是一個(gè)更復(fù)雜的節(jié)點(diǎn),但是我們只關(guān)心他這個(gè)節(jié)點(diǎn)運(yùn)算的結(jié)果,在這個(gè)結(jié)果的基礎(chǔ)上再進(jìn)行一元運(yùn)算。
接下來(lái)是二元運(yùn)算,二元運(yùn)算也很簡(jiǎn)單,它包含了兩個(gè)子節(jié)點(diǎn):
void DynamicDSL::SemaBinaryASTNode::run() {
this->left->run();
this->right->run();
SemaASTNodeObject left = this->left->getResult();
SemaASTNodeObject right = this->right->getResult();
if(op == "*") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number) {
object.setValue(left.getValue<number>() * right.getValue<number>());
} else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == "/") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number) {
object.setValue(left.getValue<number>() / right.getValue<number>());
} else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == "%") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue((int)left.getValue<number>() % (int)right.getValue<number>());
else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == "+") {
if(left.getType() == DynamicDSL::String || right.getType() == DynamicDSL::String) {
object.setValue(left.getText() + right.getText());
} else if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number) {
object.setValue(left.getValue<number>() + right.getValue<number>());
} else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == "-") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() - right.getValue<number>());
else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == "<") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() < right.getValue<number>());
else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == "<=") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() <= right.getValue<number>());
else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == ">") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() > right.getValue<number>());
else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == ">=") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() >= right.getValue<number>());
else {
cout << "[Warning] " << "二元表達(dá)式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進(jìn)行 ' " + op + " ' 運(yùn)算" << endl;
}
} else if(op == "==") {
if(left.getType() != right.getType()) {
object.setValue(false);
} else {
object.setValue(left.getText() == right.getText());
}
} else if(op == "!=") {
if(left.getType() != right.getType()) {
object.setValue(true);
} else {
object.setValue(!(left.getText() == right.getText()));
}
} else if(op == "&&") {
if(left.getType() == DynamicDSL::Bool && right.getType() == DynamicDSL::Bool)
object.setValue(left.getValue
我們利用C++完成對(duì)節(jié)點(diǎn)的運(yùn)算求值,當(dāng)需要支持更多的二元運(yùn)算時(shí)我們就在這里對(duì)它進(jìn)行擴(kuò)展就可以了。
在求值之前我們需要先求出左右兩個(gè)子節(jié)點(diǎn)的值,通過(guò)這種模式可以無(wú)限擴(kuò)展節(jié)點(diǎn)。
在說(shuō)到三元運(yùn)算符之前,需要先說(shuō)一下小括號(hào)運(yùn)算符,因?yàn)樗鼤?huì)改變節(jié)點(diǎn)運(yùn)算的優(yōu)先級(jí)。
它的實(shí)現(xiàn)如下:
void run() override {
this->node->run();
this->object = node->getResult();
}
首先求出小括號(hào)內(nèi)部節(jié)點(diǎn)的值,再把值賦值給節(jié)點(diǎn)自身,因?yàn)樾±ㄌ?hào)只改變了運(yùn)算優(yōu)先級(jí)。
接下來(lái)就是三元運(yùn)算了,它也很簡(jiǎn)單:
void run() override {
this->condition->run();
this->first->run();
this->second->run();
SemaASTNodeObject condition = this->condition->getResult();
SemaASTNodeObject first = this->first->getResult();
SemaASTNodeObject second = this->second->getResult();
if(condition.getType() == DynamicDSL::Bool) {
if(condition.getValue<bool>()) {
object = first;
} else {
object = second;
}
} else {
cout << "[Warning] " << "三元表達(dá)式條件語(yǔ)句的結(jié)果必須是Bool數(shù)據(jù)類型" << endl;
}
}
它有三個(gè)子節(jié)點(diǎn),分別是:條件節(jié)點(diǎn),條件為真時(shí)的first節(jié)點(diǎn),條件為假時(shí)的second節(jié)點(diǎn)。它的運(yùn)算規(guī)則就是判斷條件節(jié)點(diǎn),再把結(jié)果設(shè)置給節(jié)點(diǎn)自身。
最后還需要實(shí)現(xiàn)的就是取值節(jié)點(diǎn)了,取值節(jié)點(diǎn)可能是一個(gè)或多個(gè)連續(xù)的聚會(huì)運(yùn)算符,它的實(shí)現(xiàn)如下:
void run() override {
/// 如果node不為null,則表示當(dāng)前取值是從上一個(gè)表達(dá)式的結(jié)果中取值
/// 上一個(gè)表達(dá)式結(jié)果必須是一個(gè)SemaContext
/// 如果是一個(gè)基礎(chǔ)類型,則不允許
if(this->node) {
/// 通過(guò)調(diào)用node的run,深度優(yōu)先計(jì)算出左值
this->node->run();
SemaASTNodeObject tmp = this->node->getResult();
if(tmp.getType() == DynamicDSL::Object) {
try {
SemaContext tmpContext = tmp.getValue
如果它包含了一個(gè)子節(jié)點(diǎn),這個(gè)子節(jié)點(diǎn)運(yùn)算的結(jié)果是一個(gè)SemaContext,通過(guò)key獲取這個(gè)SemaContext中的數(shù)據(jù)。
如果它不包含子節(jié)點(diǎn),則從上下文環(huán)境中的SemaContext中去獲取值。
本節(jié)需要實(shí)現(xiàn)的節(jié)點(diǎn)就是這些了,小結(jié):
? 通過(guò)抽象節(jié)點(diǎn),我們?cè)谶\(yùn)算的時(shí)候不關(guān)心節(jié)點(diǎn)本身是怎么實(shí)現(xiàn)運(yùn)算的。
? 通過(guò)節(jié)點(diǎn)與節(jié)點(diǎn)之前的引用實(shí)現(xiàn)了節(jié)點(diǎn)樹(shù)的擴(kuò)展。
? 最終我們只需要關(guān)心頂層節(jié)點(diǎn)返回的最終結(jié)果即可。
-
節(jié)點(diǎn)
+關(guān)注
關(guān)注
0文章
220瀏覽量
24527 -
數(shù)據(jù)類型
+關(guān)注
關(guān)注
0文章
236瀏覽量
13663 -
定義
+關(guān)注
關(guān)注
0文章
10瀏覽量
14356
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論