在野牛c ++中解析谓词逻辑

时间:2015-01-17 02:36:41

标签: c++ qt parsing bison

我正在开发一个软件来简化predicate logic符号中的公式,应用一些逻辑定律以合取范式输出公式。 我将尝试尽可能清楚地解释所有内容。我知道这不是一个数学网站,但我必须解释一些概念以获得具体的答案。


软件会做什么?

软件将有一个条目,用户可以在其中插入公式,程序将处理,直到达到其联合正常形式。所以我必须做一个可以处理输入的解析器,并将每个令牌放在某个结构中,然后根据需要进行转换。我将使用以下工具:

  • C ++。
  • Qt框架。
  • Bison and Flex。

如何获得公式的conjunctive normal form

  • F=!(a && b),然后输出为F[cnf]=!a || !b(应用De Morgan's law)。
  • F=(a && b),然后输出为F[cnf]=a && b(相同,因为输入已经是这种形式)。
  • F=a && (b || c),然后F[cnf]= (a || b) && (a || c)distributive law)。

所以,基本上是相同的公式,但用&&标记分隔。

施工状态

当我试图操作输入公式的部分时,我遇到的第一个问题是处理递归我的语法规则。

假设以下规则:

expr:
         SYMBOL                                  { cout << " 0 "; }
    |    LEFT_PARENTHESES expr RIGHT_PARENTESES  { cout << " 1 "; }
    |    expr AND expr                           { cout << " 2 "; }
;

然后输入(a && b)然后程序打印:0 - 0 - 1 - 2,这是:从最后执行的规则到第一个。我已经意识到使用queue并使用push_back()方法插入每个标记,然后我可以按照这些规则获取所有令牌。但我想用这些表达式操作,不仅要验证她的语法并打印令牌,所以我必须创建一个更复杂的结构(不那么复杂),binary tree来模拟一个abstract syntax tree然后转换它。现在假设公式a && b || c,在我的树中表示它将是:

      &&
     /  \
    a    ||
        /  \
       b    c

这就是,父对象始终是运算符,子对象是操作数或某些表达式。如果我有a && (b || c)之类的表达式,那么我必须包括括号:

      &&
     /  \
    a    (
          \
          ||
         /  \
        b    c

以这种方式,(右侧的所有标记将被包含在一个组中。所以,我可以使用简单公式(使用notand||()运算符进行二进制或多个操作;我可以解决嵌套公式并应用De Morgan定律)这非常有效:D。

那么,问题是什么?

好的,正如我上面所说,它只适用于简单的公式。但是如果输入具有运算符->(隐含)或<->(当且仅当),会发生什么? (...)该计划不起作用。我所要做的就是应用逻辑运算符的定义:

  • P->Q!P || Q(1)
  • P<->Q(P->Q) && (Q->P)(!P || Q) && (!Q || P)(2)

因此,我要做的第一件事是使用规则(1)和(2)转换-><->操作。一旦我拥有了元素运算符(&&||!())的所有表达式,我就必须使用De Morgan分发!运算符法律和驱逐||以返回其联合正常形式。这听起来很简单,但事实并非如此(至少不适合我)。

你还记得当我提到规则没有以令牌的到达顺序执行时吗?在这种情况下,我可以应用->运算符:让a->b在左表达式(!)中添加a并在这两个表达式之间插入||ab)。我可以在我的树(!),右孩a||父母和另一个孩子b中添加父母:

      ||
     /  \
    !    b
     \   
      a  

所以,在这种情况下有效,但我不能对<->运算符做同样的事情!

所以,我的问题是:我如何解决这些问题?你建议使用其他结构来存储令牌吗?你知道有什么技巧吗?


代码

parser.y

%{
    #include <iostream>
    #include "parsingtree.h"

    using namespace std;

    #define YYSTYPE string

    int yylex(void);
    void yyerror(char *);

    ParsingTree tree;
%}

%token  LEFT_PAR
        RIGHT_PAR
        SYMBOL
        END
        NOT
        DIST

%left   AND
        OR
        THEN
        IFF

%start input

%%

input:
        /* empty */
    |   input expr
;

expr:
        SYMBOL {
            $$ = $1;
            cout << "ACA ENTRO A LA REGLA 0" << endl;
            tree.pushSymbol($1);
        }
    |   LEFT_PAR expr RIGHT_PAR {
            $$ = $2;
            tree.pushLogicOperator("()");
        }
    |   NOT expr {
            $$ = $2;
            tree.pushLogicOperator("!");
        }
    |   DIST expr RIGHT_PAR {
            $$ = $2;
            tree.pushLogicOperator("!");
        }
    |   expr AND expr {
            tree.pushLogicOperator("&&");
        }
    |   expr OR expr {
            tree.pushLogicOperator("||");
        }
    |   expr THEN expr {
            tree.pushLogicOperator("||");
            tree.pushLogicOperator("!");
        }
;

            tree.pushLogicOperator("||");
            tree.pushLogicOperator("!");

        }
    |   SYMBOL THEN expr {
            tree.pushSymbol($1);
            tree.pushLogicOperator("||");
            tree.pushLogicOperator("!");
        }
%%

void yyerror(char *s) {

}

scanner.l

%option noyywrap
%{
    #include <iostream>
    #include "parser.tab.c"
    using namespace std;
%}

%%

[a-zA-Z0-9<>=]+  {
    yylval = strdup(yytext);
    return SYMBOL;
}

"&&" {
    return AND;
}

"||" {
    return OR;
}

"!" {
    return NOT;
}

"!(" {
    return DIST;
}

[ \0\0] {
    return END;
}

"("     {
    return LEFT_PAR;
}

")"     {
    return RIGHT_PAR;
}

"->"    {
    return THEN;
}

"<->"   {
    return IFF;
}

%%

的main.cpp

#include "parsingtree.h"
#include "lex.yy.c"

typedef yy_buffer_state *YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_string(char *, size_t);

int main(int argc, char *argv[]) {
    yy_scan_string("(a&&(d->g))->(b&&c)\0\0");
    yyparse();

    tree.printTree();
}

parsingtree.h

#ifndef PARSINGTREE_H
#define PARSINGTREE_H
#include <QString>
#include <QList>
#include <QDebug>
#include <iostream>

using namespace std;

class ParsingTree
{
public:
    ParsingTree();
    void pushSymbol(string data);
    void pushLogicOperator(string data);
    void printTree();

private:
    struct treeNode
    {
        string data;
        treeNode* leftNode;
        treeNode* rightNode;
    };
    QList<treeNode*> auxList; //guarda el arbol en formación
    treeNode* tree; //pedir el ultimo elemento de la lista, ese es el arbol

    void printTree(treeNode* rTree);
    string latest(treeNode* rTree);
};

#endif // PARSINGTREE_H

parsingtree.cpp

#include "parsingtree.h"

ParsingTree::ParsingTree()
{
    tree= NULL;
}

void ParsingTree::pushSymbol(string data)
{
    treeNode* node = new treeNode;
    node->data = data;
    node->leftNode = NULL;
    node->rightNode= NULL;
    auxList.push_back(node);
}

void ParsingTree::pushLogicOperator(string data)
{
    //binarios
    if ((data == "||") || (data == "&&"))
    {
         treeNode* node = new treeNode;
         node->data = data;
         node->rightNode=auxList.last();
         auxList.removeLast();
         node->leftNode=auxList.last();
         auxList.removeLast();
         auxList.push_back(node);
    }
    //unarios
    if ((data == "!") || (data == "()"))
    {
        treeNode* node = new treeNode;
        node->data = data;
        node->rightNode=auxList.last();
        auxList.removeLast();
        node->leftNode= NULL;
        auxList.push_back(node);
    }
}

void ParsingTree::printTree()
{
    tree = auxList.last();
    printTree(tree);
}

void ParsingTree::printTree(ParsingTree::treeNode* rTree)
{
    if (rTree != NULL)
    {

        printTree(rTree->leftNode);
        if (rTree->data == "()")
        {
            cout << "$(";
            printTree(rTree->rightNode);
            cout << ")$";
        }
        else
        {
            cout << rTree->data + " ";
            printTree(rTree->rightNode);
        }
    }
}

谢谢! :d

PS:如果我写错了或有些东西你无法理解,请问我,这样我可以更好地表达自己;我的英语非常差(我来自阿根廷),我发现这个问题很难解释,所以我希望你能理解。

1 个答案:

答案 0 :(得分:1)

使用此伪代码:

P and Q are atomic OR expr between last ')' and corresponding '('

while '<=>' occurs
    P <=> Q |= (P => Q) && (Q => P)

while '=>' occurs
    P => Q |= !P || Q

while '!(P || Q)' || '!(P && Q)' occurs
    apply De Morgan

while 'P || (Q && R)' || '(Q & R) || P' occurs
    |= (P || Q) && (P || R)

apply these rules after each wile:
while 'P || (Q || R)' || '(P || Q) || R' occurs
    |= P || Q || R
while 'P && (Q && R)' || '(P && Q) && R' occurs
    |= P && Q && R

您可以使用正则表达式替换或使用c ++解析字符串来实现。务必以一致的方式从左到右或从右到左进行解析。这将确定逻辑是左还是右关联。

如果您发现此替换方案存在歧义,请与我们联系。