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

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

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();
}