完整實現代碼及antlr語法規則文件在這里:https://github.com/czqasngit/DynamicDSL
上一節,我們已經實現了數據類型的定義,在此基礎上我們就可以來實現我們的數據節點了。
在實現某個具體節點(比如一無表達式節點)之前,我們需要抽象出一個基類SemaASTNode
。
它的定義如下:
namespace DynamicDSL {
/// 整理后可運算的AST
class SemaASTNode {
protected:
SemaContext *context;
public:
enum Type {
None = 1<<0,
Assign = 1<<1, /// 賦值表達式, 改變上下文環境中已存在變量的值
Declare = 1<<2, /// 申明變量,上下文環境中增加變量
Value = 1<<3 /// 求值表達式
};
/// 節點的類型
Type type;
SemaASTNodeObject object;
public:
SemaASTNode() {
this->type = None;
}
SemaASTNode(SemaContext *context, Type type) {
this->type = type;
/// 復制,單獨管理context的內存
this->context = SemaContext::copy(context);
}
virtual ~SemaASTNode() {
//cout << "SemaASTNode: release" << endl;
delete context;
}
/// 求節點的值
virtual void run() { }
/// 獲取節點的值
SemaASTNodeObject getResult() { return object; }
};
};
基類定義了共有的數據SemaContext(執行時的環境變量),Type(表達式類型),SemaASTNodeObject(表達式運算結果)。
同時還定義了一個虛函數,它抽象了節點結算的過程,每一種不同的節點都需要實現這個函數來完成具體節點的運算,這樣就很方便的只需要調用節點的run我們就能得到想要的結果了。
virtual void run() { assert(false); }
運算結果保存賦值給object,通過getResult()就可以取到節點的運算結果。
接下來我們來看最簡單的也是最重要的節點Primay節點:SemaPrimaryASTNode
。
這個節點需要完成兩個小功能,第一就是ID標識符的消解,我們需要將解析到的標識符解析成最終要獲得的值。
比如我們有一個變量是age,他的值是30,在SemaPrimaryASTNode
里面我們就需要將age替換成30。
而實現這個邏輯的代碼就在run()函數里面,當被調用的時候就替換成最終的值了。
void run() override {
if(idTokenText.empty() &&
stringTokenText.empty() &&
intTokenText.empty() &&
doubleTokenText.empty() &&
tfTokenText.empty()) {
object = SemaASTNodeObject(*context);
} else {
/// 這里對變量進行消解
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;
}
}
}
接下來是一元運算節點: SemaUnaryASTNode
他的實現也很簡單,因為我們現在只簡單的支持了++ --運算,所以我們要求他們的值類型一定是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 "一元表達式暫不支持: " + op + " 運算符";
}
} else {
cout << "[Warning] " << "++或--只能對Number類型有效, " << "當前的類型是: " << object.getTypeText() << endl;
}
}
但是這里需要注意的是,SemaUnaryASTNode
包含了一個節點,而這個節點求出來的值就是一個Number類型的數據。
它也許是一個簡單的SemaPrimaryASTNode節點,也許是一個更復雜的節點,但是我們只關心他這個節點運算的結果,在這個結果的基礎上再進行一元運算。
接下來是二元運算,二元運算也很簡單,它包含了兩個子節點:
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] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << endl;
}
} else if(op == "/") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number) {
object.setValue(left.getValue<number>() / right.getValue<number>());
} else {
cout << "[Warning] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << 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] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << 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] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << endl;
}
} else if(op == "-") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() - right.getValue<number>());
else {
cout << "[Warning] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << endl;
}
} else if(op == "<") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() < right.getValue<number>());
else {
cout << "[Warning] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << endl;
}
} else if(op == "<=") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() <= right.getValue<number>());
else {
cout << "[Warning] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << endl;
}
} else if(op == ">") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() > right.getValue<number>());
else {
cout << "[Warning] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << endl;
}
} else if(op == ">=") {
if(left.getType() == DynamicDSL::Number && right.getType() == DynamicDSL::Number)
object.setValue(left.getValue<number>() >= right.getValue<number>());
else {
cout << "[Warning] " << "二元表達式, 類型 " << left.getTypeText() << "與 " << right.getTypeText() << "不能進行 ' " + op + " ' 運算" << 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++完成對節點的運算求值,當需要支持更多的二元運算時我們就在這里對它進行擴展就可以了。
在求值之前我們需要先求出左右兩個子節點的值,通過這種模式可以無限擴展節點。
在說到三元運算符之前,需要先說一下小括號運算符,因為它會改變節點運算的優先級。
它的實現如下:
void run() override {
this->node->run();
this->object = node->getResult();
}
首先求出小括號內部節點的值,再把值賦值給節點自身,因為小括號只改變了運算優先級。
接下來就是三元運算了,它也很簡單:
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] " << "三元表達式條件語句的結果必須是Bool數據類型" << endl;
}
}
它有三個子節點,分別是:條件節點,條件為真時的first節點,條件為假時的second節點。它的運算規則就是判斷條件節點,再把結果設置給節點自身。
最后還需要實現的就是取值節點了,取值節點可能是一個或多個連續的聚會運算符,它的實現如下:
void run() override {
/// 如果node不為null,則表示當前取值是從上一個表達式的結果中取值
/// 上一個表達式結果必須是一個SemaContext
/// 如果是一個基礎類型,則不允許
if(this->node) {
/// 通過調用node的run,深度優先計算出左值
this->node->run();
SemaASTNodeObject tmp = this->node->getResult();
if(tmp.getType() == DynamicDSL::Object) {
try {
SemaContext tmpContext = tmp.getValue
如果它包含了一個子節點,這個子節點運算的結果是一個SemaContext,通過key獲取這個SemaContext中的數據。
如果它不包含子節點,則從上下文環境中的SemaContext中去獲取值。
本節需要實現的節點就是這些了,小結:
? 通過抽象節點,我們在運算的時候不關心節點本身是怎么實現運算的。
? 通過節點與節點之前的引用實現了節點樹的擴展。
? 最終我們只需要關心頂層節點返回的最終結果即可。
-
節點
+關注
關注
0文章
217瀏覽量
24386 -
數據類型
+關注
關注
0文章
236瀏覽量
13610 -
定義
+關注
關注
0文章
10瀏覽量
14336
發布評論請先 登錄
相關推薦
評論