动态模板语义分析-实现AST节点

描述

完整实现代码及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中去获取值。

本节需要实现的节点就是这些了,小结:

• 通过抽象节点,我们在运算的时候不关心节点本身是怎么实现运算的。

• 通过节点与节点之前的引用实现了节点树的扩展。

• 最终我们只需要关心顶层节点返回的最终结果即可。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分