完整实现代码及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条评论
快来发表一下你的评论吧 !