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