Qt学习之路2的bool树模型,根据豆子的文章整理为代码形式,便于阅读理解
来源:互联网 发布:淘宝网怎么代销 编辑:程序博客网 时间:2024/04/30 01:46
// booleanparser.hclass BooleanParser{public: Node *parse(const QString &expr);private: Node *parseOrExpression(); Node *parseAndExpression(); Node *parseNotExpression(); Node *parseAtom(); Node *parseIdentifier(); void addChild(Node *parent, Node *child); void addToken(Node *parent, const QString &str, Node::Type type); bool matchToken(const QString &str) const; QString in; //存储要处理的布尔表达式的字符串 int pos; //分析布尔表达式字符串时的当前字符位置,起始为0};// booleanparser.cppNode *BooleanParser::parse(const QString &expr){//首先将传入的布尔表达式的字符串保存下来,避免直接修改参数(这也是库的接口设计中常见的一个原则) in = expr; in.replace(" ", ""); //将其中的空格全部去掉 pos = 0;//创建Root节点——布尔表达式的树状表达,显然需要有一个根节点,所以我们在这里直接创建根节点,这个根节点就是一个完整的布尔表达式Node *node = new Node(Node::Root);//或运算的优先级最低,应该最后被处理。一旦或运算处理完毕,意味着整个布尔表达式已经处理完毕,所以在调用了addChild(node, parseOrExpression())之后,返回整个node。 addChild(node, parseOrExpression()); return node;}Node *BooleanParser::parseOrExpression(){//要想处理OR运算,首先要处理AND运算Node *childNode = parseAndExpression();//AND节点处理完毕,函数返回。如果&&后面是||,说明是OR节点if (matchToken("||")) {//生成一个OR节点,把刚刚处理过的AND节点添加为其子节点 Node *node = new Node(Node::OrExpression); addChild(node, childNode);//如果同级中有多个或,则将他们都提出,建立node的或运算符子节点,直到找到的不是||,OR节点处理完毕,返回这个node。 while (matchToken("||")) { addToken(node, "||", Node::Operator); addChild(node, parseAndExpression()); } return node;//如果AND节点后面不是||,说明不是OR节点,则直接返回刚刚处理过的AND节点。 } else { return childNode; }}Node *BooleanParser::parseAndExpression(){ //要想处理AND运算,首先要处理NOT运算Node *childNode = parseNotExpression();//NOT节点处理完毕,函数返回。如果NOT节点后面是&&,说明是AND节点。if (matchToken("&&")) {//生成一个AND节点,把刚刚处理过的NOT节点添加为其子节点 Node *node = new Node(Node::AndExpression); addChild(node, childNode);//如果同级中有多个与,则将他们都提出,建立node的与运算符子节点,直到找到的不是&&,AND节点处理完毕,返回这个node。 while (matchToken("&&")) { addToken(node, "&&", Node::Operator); addChild(node, parseNotExpression()); } return node;//如NOT节点后面不是&&,说明不是AND节点,则直接返回刚刚处理过的NOT节点。 } else { return childNode; }}Node *BooleanParser::parseNotExpression(){ if (matchToken("!")) { Node *node = new Node(Node::NotExpression);//如果同级中有多个非,则将他们都提出,建立node的非运算符子节点 while (matchToken("!")) addToken(node, "!", Node::Operator); addChild(node, parseAtom()); //调用原子解析函数处理"!"后的表达式或标识符 return node; } else { return parseAtom(); }}Node *BooleanParser::parseAtom(){ if (matchToken("(")) { //当目标位置处是括号(表达式)时 Node *node = new Node(Node::Atom); addToken(node, "(", Node::Punctuator); addChild(node, parseOrExpression()); //递归解析或运算 addToken(node, ")", Node::Punctuator); return node; } else { //当目标位置处是除 (,),!,&&,|| 以外的其他字符时 return parseIdentifier(); }}Node *BooleanParser::parseIdentifier(){int startPos = pos;//当pos没到输入字符串最尾,且pos处的字符是字母或数字时 while (pos < in.length() && in[pos].isLetterOrNumber()) ++pos;//此时pos - 1处的字符是最后一个标识符名称(变量名)if (pos > startPos) {//截取 startPos开始,pos – startPos长度的字符串作为标识符名称 return new Node(Node::Identifier, in.mid(startPos, pos - startPos)); } else { return 0; }}
在parseNotExpression()函数中,检查第一个字符是不是“!”,如果是,意味着这个表达式是一个NOT运算,生成NOT节点。NOT节点可能会有两种不同的情况:
1. 子表达式(也就是用括号包围起来的部分,由于这部分的优先级最高,所以看做是一个完整的子表达式)
子表达式是原子性的,需要一个独立的处理,也要生成一个节点,其分隔符是“(” 和 “)”。“(”和“)”之间又是一个完整的布尔表达式,一个完整的布尔表达式最后要处理的部分是OR运算,因此调用parseOrExpression()函数进行递归。
2. 标识符(如果“!”符号后面不是括号,则只能是一个标识符,这是布尔表达式文法决定的)我们使用 parseIdentifier()函数来获得这个标识符。
void BooleanParser::addChild(Node *parent, Node *child){ if (child) { //如果child不为NULL parent->children += child; //在节点指针列表(QList<Node*>)中增加子节点 parent->str += child->str; //将子节点的字符串数据加到父节点 child->parent = parent; //设置子节点的父指针 }}
parse()函数中新声明的节点开始都没有数据,其中的数据(包括字符和符号)都是调用addChild()函数时加上的。
void BooleanParser::addToken(Node *parent, const QString &str, Node::Type type){//在in中从pos位置开始截取长度为str.length()的字符串,判断是否等于strif (in.mid(pos, str.length()) == str) {//当上述位置处的字符串与输入的str一致时,创建一个符号节点(Node::Operator或Node::Punctuator)并作为parent的一个子节点 addChild(parent, new Node(type, str));//移动标记位置到下一个分析点,跳过一个完整符号 pos += str.length(); }}bool BooleanParser::matchToken(const QString &str) const{ return in.mid(pos, str.length()) == str;}
这个过程看起来非常复杂,实际非常清晰:一层一层按照布尔文法递归执行,从最顶层一直到最底层。这就是编译原理的词法分析中最重要的算法之一:递归下降算法。由于这个算法简洁明了,很多编译器的词法分析都是使用的这个算法。
1 0
- Qt学习之路2的bool树模型,根据豆子的文章整理为代码形式,便于阅读理解
- GRE阅读理解文章的主题句有两种形式
- 泛型的基本含义-便于阅读理解
- 学习笔记——bool值用printf的形式输出
- linux ls 显示便于阅读的文件大小
- 豆子Qt Object-C BLOG<2012 年的简单计划>
- VC目标代码的阅读理解2
- 豆子的梦想
- 吃豆子的小游戏
- Qt学习之六: Qt的对象模型
- 转载便于学习 android jni代码编写规则--整理总结
- Objective-C之NSLog和BOOL的整理。
- linux的用户管理---(根据他人博客的学习总结之路非技术性文章)
- 学习笔记之C++ 输出代码的几种形式
- Qt学习2之QSplitter类的理解
- 机器学习笔记(2)线性模型的基本形式
- 一张便于理解JMS的结构图
- 关于分治的小例子便于理解
- 面向对象
- 开源造轮子:一个简洁,高效,轻量级,酷炫的不要不要的canvas粒子运动插件库
- web前端-JavaScript 调试 -021
- 蓝牙Ble4.0开发
- 自定义拦截器
- Qt学习之路2的bool树模型,根据豆子的文章整理为代码形式,便于阅读理解
- swoole和redis异步任务
- 基于spark的ctr预估离线流程
- 车载adb常用到的脚本
- linux系统挂载NTFS移动硬盘
- Oracle触发器编译错误之ORA-04098: 触发器 无效且未通过重新验证
- 在Java中斜杠和反斜杠都有各自不同的意思
- Android 解决方法数 65536 (65k) 限制
- C++stl容器类使用