完整實(shí)現(xiàn)代碼及antlr語法規(guī)則文件在這里:https://github.com/czqasngit/DynamicDSL
上一節(jié),我們已經(jīng)實(shí)現(xiàn)了數(shù)據(jù)類型的定義,在此基礎(chǔ)上我們就可以來實(shí)現(xiàn)我們的數(shù)據(jù)節(jié)點(diǎn)了。
在實(shí)現(xiàn)某個(gè)具體節(jié)點(diǎn)(比如一無表達(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é)算的過程,每一種不同的節(jié)點(diǎn)都需要實(shí)現(xiàn)這個(gè)函數(shù)來完成具體節(jié)點(diǎn)的運(yùn)算,這樣就很方便的只需要調(diào)用節(jié)點(diǎn)的run我們就能得到想要的結(jié)果了。
virtual void run() { assert(false); }
運(yùn)算結(jié)果保存賦值給object,通過getResult()就可以取到節(jié)點(diǎn)的運(yùn)算結(jié)果。
接下來我們來看最簡單的也是最重要的節(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 {
/// 這里對變量進(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;
}
}
}
接下來是一元運(yùn)算節(jié)點(diǎn): SemaUnaryASTNode
他的實(shí)現(xiàn)也很簡單,因?yàn)槲覀儸F(xià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] " << "++或--只能對Number類型有效, " << "當(dāng)前的類型是: " << object.getTypeText() << endl;
}
}
但是這里需要注意的是,SemaUnaryASTNode
包含了一個(gè)節(jié)點(diǎn),而這個(gè)節(jié)點(diǎn)求出來的值就是一個(gè)Number類型的數(shù)據(jù)。
它也許是一個(gè)簡單的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)算。
接下來是二元運(yùn)算,二元運(yù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++完成對節(jié)點(diǎn)的運(yùn)算求值,當(dāng)需要支持更多的二元運(yùn)算時(shí)我們就在這里對它進(jìn)行擴(kuò)展就可以了。
在求值之前我們需要先求出左右兩個(gè)子節(jié)點(diǎn)的值,通過這種模式可以無限擴(kuò)展節(jié)點(diǎn)。
在說到三元運(yùn)算符之前,需要先說一下小括號(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í)。
接下來就是三元運(yù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á)式條件語句的結(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) {
/// 通過調(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,通過key獲取這個(gè)SemaContext中的數(shù)據(jù)。
如果它不包含子節(jié)點(diǎn),則從上下文環(huán)境中的SemaContext中去獲取值。
本節(jié)需要實(shí)現(xiàn)的節(jié)點(diǎn)就是這些了,小結(jié):
? 通過抽象節(jié)點(diǎn),我們在運(yùn)算的時(shí)候不關(guān)心節(jié)點(diǎn)本身是怎么實(shí)現(xiàn)運(yùn)算的。
? 通過節(jié)點(diǎn)與節(jié)點(diǎn)之前的引用實(shí)現(xiàn)了節(jié)點(diǎn)樹的擴(kuò)展。
? 最終我們只需要關(guān)心頂層節(jié)點(diǎn)返回的最終結(jié)果即可。
-
節(jié)點(diǎn)
+關(guān)注
關(guān)注
0文章
220瀏覽量
24540 -
數(shù)據(jù)類型
+關(guān)注
關(guān)注
0文章
236瀏覽量
13669 -
定義
+關(guān)注
關(guān)注
0文章
10瀏覽量
14358
發(fā)布評(píng)論請先 登錄
相關(guān)推薦
如何使用Linux系統(tǒng)下的mdev實(shí)現(xiàn)動(dòng)態(tài)更新設(shè)備節(jié)點(diǎn)及動(dòng)態(tài)掛載U盤
NLPIR大數(shù)據(jù)語義智能分析平臺(tái)先精準(zhǔn)分詞才語義分析
NLPIR語義分析是對自然語言處理的完美理解
九眼公共安全語義智能分析平臺(tái),實(shí)現(xiàn)文本分析的公共安全應(yīng)用
屬性節(jié)點(diǎn)或調(diào)用節(jié)點(diǎn)的動(dòng)態(tài)引用問題
動(dòng)態(tài)ppt課件模板
![<b class='flag-5'>動(dòng)態(tài)</b>ppt課件<b class='flag-5'>模板</b>](https://file.elecfans.com/web2/M00/48/8A/pYYBAGKhtAyAQkt8AAAhFSEwxBA489.jpg)
智能節(jié)點(diǎn)模板電路設(shè)計(jì)
![智能<b class='flag-5'>節(jié)點(diǎn)</b><b class='flag-5'>模板</b>電路設(shè)計(jì)](https://file.elecfans.com/web2/M00/48/F0/pYYBAGKhtC6ATMNZAAAMaufLOPc769.jpg)
智能IETM語義檢索系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)
基于語義的文本語義分析
基于快照的大規(guī)模動(dòng)態(tài)圖相似節(jié)點(diǎn)查詢算法
![基于快照的大規(guī)模<b class='flag-5'>動(dòng)態(tài)</b>圖相似<b class='flag-5'>節(jié)點(diǎn)</b>查詢算法](https://file.elecfans.com/web2/M00/49/83/poYBAGKhwMCAaxKAAAAhwsLj5XE104.jpg)
基于能量均衡與動(dòng)態(tài)調(diào)節(jié)的節(jié)點(diǎn)篩選算法
![基于能量均衡與<b class='flag-5'>動(dòng)態(tài)</b>調(diào)節(jié)的<b class='flag-5'>節(jié)點(diǎn)</b>篩選算法](https://file.elecfans.com/web1/M00/45/26/o4YBAFpkDF-AOUk5AABKAUOfZ-g556.jpg)
工具SemExpr可實(shí)現(xiàn)gadget搜索與語義分析
![工具SemExpr可<b class='flag-5'>實(shí)現(xiàn)</b>gadget搜索與<b class='flag-5'>語義</b><b class='flag-5'>分析</b>](https://file.elecfans.com/web1/M00/E5/1C/pIYBAGBLEPqAOfz2AACJ-fLgRWo277.png)
動(dòng)態(tài)模板語義分析-動(dòng)態(tài)綁定XML
![<b class='flag-5'>動(dòng)態(tài)</b><b class='flag-5'>模板</b><b class='flag-5'>語義</b><b class='flag-5'>分析</b>-<b class='flag-5'>動(dòng)態(tài)</b>綁定XML](https://file1.elecfans.com/web2/M00/81/BF/wKgaomQBVh6AWyPXAACpcDxfRBw190.jpg)
一個(gè)動(dòng)態(tài)環(huán)境下的實(shí)時(shí)語義RGB-D SLAM系統(tǒng)
![一個(gè)<b class='flag-5'>動(dòng)態(tài)</b>環(huán)境下的實(shí)時(shí)<b class='flag-5'>語義</b>RGB-D SLAM系統(tǒng)](https://file1.elecfans.com/web2/M00/A1/54/wKgaomToa9iAIHC7AAAXI4FRhcY470.png)
評(píng)論