0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

汽车电子技术 来源:程序猿搬砖 作者:坏人 2023-03-03 10:09 次阅读

完整实现代码及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

    文章

    208

    浏览量

    24078
  • 数据类型
    +关注

    关注

    0

    文章

    228

    浏览量

    13498
  • 定义
    +关注

    关注

    0

    文章

    10

    浏览量

    14232
收藏 人收藏

    评论

    相关推荐

    如何使用Linux系统下的mdev实现动态更新设备节点动态挂载U盘

    今天主要聊一聊,如何使用Linux系统下的mdev实现动态更新设备节点动态挂载U盘。
    发表于 10-17 09:22 3254次阅读

    NLPIR大数据语义智能分析平台先精准分词才语义分析

    特性,是对数据包含信息的更高层次的抽象。  灵玖软件NLPIR大数据语义智能分析平台针对中文数据挖掘的综合需求,融合了网络精准采集、自然语言理解、文本挖掘和语义搜索的研究成果,先后历时十八年,服务了
    发表于 08-27 10:53

    NLPIR语义分析是对自然语言处理的完美理解

    单位,词语具有特定的语义和内涵。在词语层次上,词语语义分析意味着词语的内涵分析、词语之间的语义相似度或相关度
    发表于 10-19 11:34

    九眼公共安全语义智能分析平台,实现文本分析的公共安全应用

    社会和科技的进步和现在行业对数据的利用率提高有很大关系,各行各业积累的数据量均在增加,公安领域也包括在内,有大量的案件信息数据需要进行文本分析。 现在的公安部门均使用信息管理系统管理数据,在实现
    发表于 10-08 15:56

    属性节点或调用节点动态引用问题

    预定逻辑使用属性节点和调用节点。这种办法麻烦的是先要生成引用常量数组,引用常量要一个个的手动在菜单里选择对应的变量。现在问:有没有一种办法,通过对控件名称的调用,实现动态地改变属性
    发表于 09-07 14:39

    动态ppt课件模板

    动态的ppt课件模板,带动画的细纹设计课件模版, 动态魔方课件模版
    发表于 12-03 19:10 0次下载
    <b class='flag-5'>动态</b>ppt课件<b class='flag-5'>模板</b>

    基于语义攻击树的安全事件建模与分析

             本文在安全事件的时间语义及上下文语义关系分析的基础上,建立语义攻击树模型,提出了增强非
    发表于 09-08 09:39 14次下载

    智能节点模板电路设计

    介绍了基于AT89C2051单片机的VCN2M IO智能节点模板结构电路设计。应用实践证明,该VCN2M IO智能节点模板的智能节点数大大增
    发表于 05-18 17:08 40次下载
    智能<b class='flag-5'>节点</b><b class='flag-5'>模板</b>电路设计

    智能IETM语义检索系统设计与实现

    通过对IETM智能化语义检索方法的探讨,提出了一种基于Jena推理的IETM智能化语义检索方法。分析了当前IETM系统检索方法存在的不足,提出了语义检索是
    发表于 09-12 16:14 0次下载

    基于语义的文本语义分析

    文本情感倾向性研究是人工智能的分支学科,涉及了计算语言学,数据挖掘,自然语言处理等多个学科。基于语义的情感倾向研究和基于机器学习的情感倾向研究是情感倾向性分析的两个方向。本文采用了基于语义的方法
    发表于 12-15 16:35 6次下载

    基于快照的大规模动态图相似节点查询算法

    的大规模动态图泛相似节点查询处理算法。具体包括:图动态演进过程的快照集表示,即演进动态图;图动态演进过程中的
    发表于 12-23 10:11 0次下载
    基于快照的大规模<b class='flag-5'>动态</b>图相似<b class='flag-5'>节点</b>查询算法

    自动驾驶语义高精地图的层级实现

    语义高精地图是高层级的高精地图,语义地图的实现需要以几何地图为基础。在这篇文章中,将要讲述自动驾驶语义高精地图的实现过程。
    的头像 发表于 05-23 15:13 3501次阅读

    工具SemExpr可实现gadget搜索与语义分析

    分析内容的基础上,总结 gadget语义分析应包含的要素,提出一种基于表达式树的 gadget语义分析方法。通过表达式树变体描述寄存器和内存
    发表于 03-12 14:52 20次下载
    工具SemExpr可<b class='flag-5'>实现</b>gadget搜索与<b class='flag-5'>语义</b><b class='flag-5'>分析</b>

    动态布局小结-Demo

    `Runtime`: 实现了脚本的语义分析,模板实现了一个栈帧。已经实现的运算有(一元表达式、二元表达式、三元表达式、链式取值、加减乘除、
    的头像 发表于 03-03 10:02 250次阅读
    <b class='flag-5'>动态</b>布局小结-Demo

    动态模板语义分析-动态绑定XML

    DynamicDSL将遵循以下的交互展示逻辑: 描述文件(XML,Widget) --->(绑定数据)--->渲染模板--->渲染--->事件驱动--->数据改变 这样循环下来的,最终我们看到的就是App上的界面与交互效果了。
    的头像 发表于 03-03 10:06 405次阅读
    <b class='flag-5'>动态</b><b class='flag-5'>模板</b><b class='flag-5'>语义</b><b class='flag-5'>分析</b>-<b class='flag-5'>动态</b>绑定XML