简易逻辑表达式词法分析器

待分析的逻辑表达式如下:

                EXISTS(x)(Missile(x) AND Owns(Father(Nono),Part_of(x))) => Sells(West,x,Nono)

为了处理逻辑表达式,必须首先提取出逻辑表达式的各个部分:关键字(AND, OR, NOT, FORALL, EXISTS, =>)、谓词和参数,同时必须对于表达式中多余的空格必须删除。这项工作由一个词法分析器完成。

该词法分析器最基础的部分是一个自定义节点Node,定义如下:

class Node
{
public:
    
// VARIABLEs
    enum NodeType {KEYWORD, PREDICATE, PARAM};    // Types of the node.
    static wstring KeyWord[];
    NodeType type;
    wstring content;    
// content of the node.
    int level;    // Record the level of the node,for example: 
                
// Owns(Nono,Sub(x))---"Owns" in level 0,"Nono""Sub" in level 1 and x in level 2.

    
// FUNCTIONs
    Node();
    Node(wstring con,NodeType type_
=KEYWORD,int lev=0);
    
~Node();
    
virtual Node& operator=(Node&); // Overrides of operator=

protected:

}
;

Node主要有三个部分:该节点内容、节点类型、节点的等级(用以对提取出的表达式进行进一步的分析)。

同时定义了一个链表:

typedef list<Node> NodeList; // Define List.

该链表用于存储整个逻辑表达式。

同时定义了一个类Parser,封装了NodeList用于对逻辑表达式进行操作。

class Parser
{
public:
    
// VARIABLEs
    NodeList list;
    Node
* node;
    
    
// FUNCTIONs
    Parser(){ node=NULL;};
    
~Parser();
    Node
* Parse(wstring str,int cur_pos=0); // function to parse a string.

private:

}
;

词法分析器的核心函数就是Node* Parser::Parse(wstring str,int cur_pos)该函数主要针对逻辑表达式中的'('' '','以及')'进行分析,流程图如下:

运行结果如下:

词法分析器运行结果

 
分析结果以树的形式表示:同一级节点表示具有相同的等级,参数比它的谓词的等级要高,比如Missile(x)中Missile等级为0级,x为1级。分
析之后各个节点存储在Parser.list中,可以根据该链表的节点的level属性将各节点插入树种的相应位置。
     分析以上逻辑表达式可知,各个节点的等级如下:
      Missile  x  AND  Owns  Nono  Sub  x  =>  Sells  West  x  Nono
         0         1   0         0           1         1    2  0      0          1      1      1
    
可见,利用堆栈来辅助将节点插入树中是最理想的方法。思路如下:将树的TVI_ROOT压栈;若当前节点的等级大于其前一个节点,将前一个节点压栈;若当
前节点等级小于前一个节点,出栈前一节点等级 和 
当前节点等级之差次;若当前节点等级等于前一节点等级,既不压栈也不出栈。每个当前节点的父节点为栈顶元素。


源代码如下:

//////////////////////////////////////////////////
//    
//    Function Name: Parse
//    Input:    a wstring(must), 
//            the start position of being parsed(choiced).
//    Output: a parsed list's head.
//
//////////////////////////////////////////////////
Node* Parser::Parse(wstring str,int cur_pos)
{
//    int start=cur_pos;
    Node* tmpNode=NULL;
    wchar_t pstr[
40]=L"";    // temp var to record words.
    int k=0// used together with pstr.
    int level=0;
    
int space_pos=0;
    
    
// Deal with the first keywords of a propositional fuction.
    
// Such as NOT, EXISTS(x), FORALL(y) and so on.
    
// EXAMPLE: EXISTS(x)(Missile(x) AND Owns(Nono,x)) => Sells(West,x,Nono)
    
// EXAMPLE: NOT(Missile(x)) AND Owns(Nono,Sub(x)) => Sells(West,x,Nono)
    
// EXAMPLE: Missile(x) AND Owns(Nono,Sub(x)) => Sells(West,x,Nono)
    while(cur_pos!=str.length())
    
{
        
if(str[cur_pos]==L'(')    // Words before '(' only can be KEYWORD or PREDICATE.
        {
            
bool isKeyWord=false;
            
for(int i=0;i<sizeof(Node::KEYWORD);++i)
            
{
                
if(!wcscmp(pstr,Node::KeyWord[i].c_str())) //pstr==Node::KeyWord[i]
                {
                    isKeyWord
=true;
                    tmpNode
=new Node(pstr,Node::KEYWORD,level);
                    list.push_back(
*tmpNode);    // Insert keyword.
                    break;
                }

            }

            
if(!isKeyWord && wcscmp(pstr,L"")) //pstr is not keyword or NULL
            {
                tmpNode
=new Node(pstr,Node::PREDICATE,level);
                list.push_back(
*tmpNode);    // Insert PREDICATE.
            }

            
++level;
            
++cur_pos;
            k
=0;
        }

        
else if(str[cur_pos]==L' ')
        
{
            
if(str[cur_pos-1]!=L')'// if a ' ' follows ')',then compare the following to or three with keyword.
            {
                
if(wcscmp(pstr,L"")) // pstr is not empty 
                {
                    wstring strAND(str,cur_pos
-3,3);
                    wstring strOR(str,cur_pos
-2,2);
                    
if(!strAND.compare(L"AND"|| !strOR.compare(L"OR"|| !strOR.compare(L"=>"))
                    
{
                        tmpNode
=new Node(pstr,Node::KEYWORD,level);
                        list.push_back(
*tmpNode);    // Insert keyword.
                        ++cur_pos;
                        k
=0;

                        memset(pstr,L
'',sizeof(pstr));
                        
continue;
                    }

                    
else 
                        exit(
0);    // Error!
                }

            }

                
            
++cur_pos;    // Ignore whitespace.
            continue;
        }

        
else if(str[cur_pos]==L','// pstr must be a param
        {
            
if(!wcscmp(pstr,L""&& str[cur_pos-1]==L')'// In a case like: Owns(Father(Nono),Sub(x))
            {
                
++cur_pos;
            }

            
else if(wcscmp(pstr,L"")) // pstr is not empty 
            {
                
if(str[cur_pos-1]==L')'// Something between a ')' and a ',', Error!
                    exit(1);
                
else
                
{
                    tmpNode
=new Node(pstr,Node::PARAM,level);
                    list.push_back(
*tmpNode);    // Insert PARAM.
                    ++cur_pos;
                }
    
            }
        

            k
=0;
        }

        
else if(str[cur_pos]==L')')    
        
{
            
if(!wcscmp(pstr,L""&& str[cur_pos-1]==L')'// In a case like: Owns(Father(Nono),Sub(x))
            {
                
++cur_pos;
            }

            
else if(wcscmp(pstr,L"")) //if pstr is not empty
            {
                
if(str[cur_pos-1]==L')')    // Error!
                    exit(1);
                
else
                
{
                    tmpNode
=new Node(pstr,Node::PARAM,level);
                    list.push_back(
*tmpNode);    // Insert PARAM.
                    ++cur_pos;
                }

            }


            
--level;
            k
=0;
        }

        
else
        
{
            pstr[k
++]=str[cur_pos++];
            
continue;
        }


        memset(pstr,L
'',sizeof(pstr));
    }

    
return &list.front();
}