正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——4 构造DFA

前端之家收集整理的这篇文章主要介绍了正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——4 构造DFA前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

整个引擎代码在github上,地址为:https://github.com/sun2043430/RegularExpression_Engine.git

接上篇《正则表达式引擎的构建——基于编译原理DFA(龙书第三章)——3 计算4个函数

本章将介绍如何使用followpos集合来构建DFA。相关算法和例子在龙书中文第二版的3.9.5节(根据正则表达式构建DFA),算法3.36和例3.37。


添加结尾END符号表示接受状态的语法树和函数

我们把上一篇文章中所举的例子——正则表达式(a|b)*abb,其对应的语法树和计算得到的4个函数列表再展示一下,我们需要借助followpos集合来构建DFA。下面展示的图形和表格中,我们在正则表达式的末尾添加表示接受状态的END符号

正则表达式(a|b)*abb对应的抽象语法树(注意最后的END标志):


正则表达式(a|b)*abb对应的nullable值、firstpos集合、lastpos集合和followpos集合(在结尾添加了END标志)



根据followpos集合创建DFA的实例说明

在构建DFA时,我们需要followpos集合。按照龙书的算法3.36,首先我们从根节点的firstpos集合入手。也就是cat11的firstpos集合{a1,b2,a5},将其放入队列Q中(此时Q中只有这一个集合)。

接下来我们的工作将转入处理队列Q。具体处理方法如下:

从头开始遍历队列Q,对其中的每一个集合S考察每一个输入字符(例如a)可匹配的节点p,所有p的followpos集合再求并集U,如果集合U在队列Q中存在则不添加到队列中,如果集合U在队列Q中不存在,则将其添加到队列Q中,最后不管U是否存在于队列Q中,我们都要记录对应的转换——集合S遇到输入字符a到达集合U。


上面讲的很抽象,我们结合例子来看,队列Q中首先放入了集合{a1,a5},对于此集合存在输入字符a和b上的转换,我们逐一来处理,首先处理输入字符为a的情况,a匹配的节点是a1和a5,所以将followpos(a1)和followpos(a5)求并集,也就是{a1,a5}并上{b7},得到的集合是{a1,a5,b7},此集合不存在于队列Q中,所以我们需要添加此集合。这样队列Q变成下图所示:

(队列图1:2个集合)


同时我们需要记录转换关系:

队列Q中的第1个集合在输入为a时,会转换到队列Q的第2个集合。

接下来看输入字符为b的情况,字符b匹配的节点在集合{a1,a5}中只有b2,所以看b2的followpos集合,followpos(b2)为{a1,a5},此集合在Q中已经存在(队列Q的第一个对象),所以无需加入到队列Q中,但转换关系还是要记录的:

队列Q中的第1个集合在输入为b时,会转换到队列Q的第1个集合。


现在队列Q中的第1个集合处理完了,接下来处理第2个集合{a1,b7},还是一样此集合存在输入字符a,b。所以逐一处理,先看a,a匹配的节点是a1,a5,所以对followpos(a1)和followpos(a5)求并集,得到的集合是{a1,b7}。在队列Q中存在。只记录转换关系:

队列Q中的第2个集合在输入为a时,会转换到队列Q的第2个集合。

再来看字符b,匹配的是b2,b7两个节点,followpos(b2)和followpos(b7)求并集,得到的集合是{a1,b9},此集合在Q中没有,所以需要添加,这样队列变成下图所示:

(队列图2:3个集合


同时要记录转换关系:

队列Q中的第2个集合在输入为b时,会转换到队列Q的第3个集合。


第2个集合处理完之后,接下来处理第3个集合{a1,b9},和前面分析的一样,分别考虑输入字符a、b,对于a,匹配a1和a5节点,求这两个节点的followpos集合的并集为{a1,b7},此集合在队列Q中已经存在,所以只记录转换关系:

队列Q中的第3个集合在输入为a时,会转换到队列Q的第2个集合。

再来看字符b,匹配的是b2,b9两个节点,followpos(b2)和followpos(b9)求并集,得到的集合是{a1,END},此集合在Q中没有,所以需要添加,这样队列变成下图所示:

(队列图3:4个集合


同时要记录转换关系:

队列Q中的第3个集合在输入为b时,会转换到队列Q的第4个集合。


再处理第4个集合 {a1,END},当遇到的输入字符为a时,followpos(a1)和followpos(a5)求并集,得到的集合是{a1,b7}。在队列Q中已存在,所以只记录转换关系为:

队列Q中的第4个集合在输入为a时,会转换到队列Q的第2个集合。

当遇到的输入字符为b时,followpos(b2)到的集合是{a1,a5}。在队列Q中已存在,所以只记录转换关系为:

队列Q中的第4个集合在输入为b时,会转换到队列Q的第1个集合。


至此,队列Q中已经没有未处理的集合了。遍历队列Q的过程结束。


我们整理上面的转换关系(上面红色字体部分),将队列Q中的每个集合看成一个状态,则转换关系为下表:


状态1,2,3,4分别对应队列Q中的每一个集合,表中的状态4是接受状态,因为状态4对应的集合为{a1,END},其中包含了END标志。根据此表建立DFA,如下图:


算法总结

至此,根据根节点的firstpos集合和其他节点的followpos集合,构建DFA的过程就讲解完毕了。最后我们总结一下算法:

1 将根节点的firstpos集合放入队列Q中。

2 当队列Q中有未处理的对象S(也就是集合、状态)时,我们处理S中的每个输入符号(例如a),令U是S中和输入符号a匹配的所有节点的followpos集合的并集。

3 如果U在不在队列Q中,则将U加入到队列尾部,如果U在队列Q中已经存在,则无需添加

4 不管U是否已经出现在队列Q中,都需要记录转换关系:S在输入符号为a时转换到U。

5 如果队列Q中还有未处理的对象回到步骤2。否则处理结束。

6 如果队列Q中的某个对象(集合、状态)包含了END标志,则该对象(集合、状态)为接受状态。


下图为龙书中的算法介绍,我上面的算法步骤思想与之相同,只是在表述上有一些差异。



关键代码

下面是创建DFA的关键代码

BOOL CDFA::CreateDFA(CNodeInTree *pNode)
{
    int                                     nIdxCur = 0;
    int                                     nIdxNext= 0;
    BOOL                                    bRet    = FALSE;
    vector<CNodeInTree*>::iterator          itNode  = pNode->m_vecFirstPos.begin();
    set<CNodeInTree*>                       setNode;
    map<unsigned char,set<CNodeInTree*>>   mapSet;

    m_lstSet.clear();
    m_lstNodeRelation.clear();
    m_setAcceptingIdx.clear();

    try 
    {
        // prepare start state
        for ( ; itNode != pNode->m_vecFirstPos.end(); itNode++ )
        {
            setNode.insert(*itNode);
        }
        m_lstSet.push_back(setNode);
        if (IsContainAcceptingState(setNode))
        {
            m_setAcceptingIdx.insert(nIdxCur);// the set is an accepting state
        }
        // end prepare start state

        // Construct DFA set,translation relation.
        list<set<CNodeInTree*>>::iterator it = m_lstSet.begin();
        for ( ; it != m_lstSet.end(); it++,nIdxCur++)
        {
            mapSet.clear();
            setNode.clear();
            setNode = *it;
            // collect all translation char into mapSet
            set<CNodeInTree*>::iterator itSet = setNode.begin();
            for ( ; itSet != setNode.end(); itSet++ )
            {
                unsigned char ch = (*itSet)->m_pToken->GetChar();
                mapSet[ch].insert(*itSet);
            }
            // end collect

            // for each translation char,get the set that correspond to translation char
            map<unsigned char,set<CNodeInTree*>>::iterator itMap = mapSet.begin();
            for ( ; itMap != mapSet.end(); itMap++ )
            {
                unsigned char ch = itMap->first;
                set<CNodeInTree*> & setNodeTemp = itMap->second;
                set<CNodeInTree*> setNodeNext;
                // get the union of followpos(p) for all p in the setNodeTemp
                CHECK_BOOL ( GetNextSet(setNodeTemp,setNodeNext) );
                if (setNodeNext.empty())
                    continue;
                if (!IsNodeSetInList(setNodeNext,nIdxNext))
                {
                    //if the set not in list,add it into list
                    m_lstSet.push_back(setNodeNext);
                    if (IsContainAcceptingState(setNodeNext))
                    {
                        m_setAcceptingIdx.insert(nIdxNext);// the set is an accepting state
                    }
                }
                DFANodeRelation stNodeRelation(nIdxCur,ch,nIdxNext);
                m_lstNodeRelation.push_back(stNodeRelation);
            }
            // end for
        }
    }
    catch (...)
    {
        goto Exit0;
    }

    bRet = TRUE;
Exit0:
    return bRet;
}

BOOL CDFA::GetNextSet(set<CNodeInTree*> & setNodeTemp,set<CNodeInTree*> & setNodeNext)
{
    BOOL                        bRet    = FALSE;
    set<CNodeInTree*>::iterator it      = setNodeTemp.begin();

    for ( ; it != setNodeTemp.end(); it++ )
    {
        vector<CNodeInTree*> & vecFollowPos = (*it)->m_vecFollowPos;
        vector<CNodeInTree*>::iterator itVec = vecFollowPos.begin();
        for ( ; itVec != vecFollowPos.end(); itVec++ )
        {
            try 
            {
                setNodeNext.insert(*itVec);
            }
            catch (...)
            {
                goto Exit0;
            }
        }

    }
    bRet = TRUE;
Exit0:
    return bRet;
}

BOOL CDFA::IsNodeSetInList(set<CNodeInTree*> &setNodeNext,int &nIdx)
{
    list<set<CNodeInTree*>>::iterator it = m_lstSet.begin();
    for (nIdx = 0; it != m_lstSet.end(); it++,nIdx++)
    {
        if (*it == setNodeNext)
        {
            return TRUE;
        }
    }
    return FALSE;
}

BOOL CDFA::IsContainAcceptingState(set<CNodeInTree*> &setNode)
{
    for (set<CNodeInTree*>::iterator it = setNode.begin(); it != setNode.end(); it++)
    {
        if (eType_END == (*it)->m_pToken->GetType())
        {
            return TRUE;
        }
    }
    return FALSE;
}
原文链接:https://www.f2er.com/regex/362807.html

猜你在找的正则表达式相关文章