使用正则表达式解析嵌套的IF语句


Using regex to parse nested IF statements

我正在以学习和爱好的名义开发自己的模板引擎。我有一个正则表达式,它使用与TWIG几乎相同的语法来查找if语句。

您可以在这里查看regex,其中包含一些工作示例,然后是我正在尝试使其工作的示例。

这是正则表达式:

{%'s*if's+(?<var>(?:[a-zA-Z_'x7f-'xff][a-zA-Z0-9_'x7f-'xff]*)(?:'.(?:[a-zA-Z0-9_'x7f-'xff]*))*)'s(?:(?<operation>=|!=|<=|<|>=|>)'s(?<var2>(?:(?:(?1)*)(?:'.(?:(?2)*))*)|(?:[0-9]+))'s?)?%}(?<if>(?:(?!{% e(?:lse|nd if) %}).)*)(?:{%'h?else'h?%}(?<else>['s'S]*?))?{%'h?end if's?%}

这是它正在处理的数据:

THESE WORK
        {% if thing %}
        stuff
        {% end if %}
        {% if thing %}
        stuff
        {% else %}
        other stuff
        {% end if %}
        {%if thing = thingy %}
        stuff
        {% else %}
        other stuff
        {% end if %}
THIS DOESN'T
        Problem starts here:
        {% if this = that %}
        {% if item.currency = 0 %}
        selected="selected"
        {% else %}
        you
        {% end if %}
        {% end if %}

基本上,我希望正则表达式搜索最后一个{%end if%}标记,并将其间的所有内容用作字符串,以便稍后递归解析。

此外,作为附带说明,将问题的大部分信息保留在regex-tester的链接中是否合适?或者我应该把问题的大部分也复制到这里(SO上)吗?

修订版1

既然你是在试验,经过一段时间的闲逛,我为你想出了一个通用的正则表达式。

这可能会增加你目前的知识,并提供一些可以借鉴的东西

剧情简介:

在纯正则表达式解决方案中,平衡文本的概念远至
正则表达式引擎已经失效。它不会填补细节
为此,你必须自己做。

与下降语法分析器或类似语法分析器相比,这是一种缓慢的方法
不同的是,它不需要放松就能知道自己在哪里。
因此,这将允许您在遇到错误时继续进行解析
从过去的错误中获得更多的意义,以帮助调试。

在做这种事情时,您应该解析每一个字符
因此,我们解析内容、分隔符开始、核心、结束和错误。

在这种情况下,我们在外部范围上留出了7个捕获组来浏览信息。

Content-它由if/else/end if以外的任何内容组成。

Else-这是else语句

Begin-这是if块的开始

If_Content-这是if block content

Core-这是all between外部开始和结束。还包含嵌套内容。

End-这是外部end if

Error-这是不平衡误差,它是ifend if

用法:

在主机程序中,定义一个名为ParseCore()
的函数此函数需要传递(或知道)当前核心字符串
如果它是c++,它将被传递开始和结束字符串迭代器
无论如何,字符串必须是函数的本地字符串。

在这个函数中,在while循环中解析字符串
在每一场比赛中,做一个if/else,看看上面的哪一组匹配
只能是这些组合

Content

Else

Begin, If_Content, Core, End

Error

只有一组对递归很重要。这就是Core
当该组匹配时,您对
进行递归函数调用ParseCore()Core字符串传递给它。

重复此操作,直到不再匹配为止
错误报告、创建结构树以及其他任何操作都可以完成
在该功能内
您甚至可以在任何时候设置一个全局标志来解除递归调用
然后退出。例如,如果出现错误或类似情况,您希望停止

注意:在对ParseCore()的初始调用中,只需传入整个原始字符串即可开始解析

祝你好运!

扩展

 # (?s)(?:(?<Content>(?&_content))|(?<Else>(?&_else))|(?<Begin>{%'s*if's+(?<If_Content>(?&_ifbody))'s*%})(?<Core>(?&_core)|)(?<End>{%'s*end's+if's*%})|(?<Error>(?&_keyword)))(?(DEFINE)(?<_ifbody>(?>(?!%}).)+)(?<_core>(?>(?<_content>(?>(?!(?&_keyword)).)+)|(?(<_else>)(?!))(?<_else>(?>{%'s*else's*%}))|(?>{%'s*if's+(?&_ifbody)'s*%})(?:(?=.)(?&_core)|){%'s*end's+if's*%})+)(?<_keyword>(?>{%'s*(?:if's+(?&_ifbody)|end's+if|else)'s*%})))
 (?s)                               # Dot-all modifier
 # =====================
 # Outter Scope
 # ---------------
 (?:
      (?<Content>                        # (1), Non-keyword CONTENT
           (?&_content) 
      )
   |                                   # OR,
      # --------------
      (?<Else>                           # (2), ELSE
           (?&_else) 
      )
   |                                   # OR
      # --------------
      (?<Begin>                          # (3), IF
           {% 's* if 's+ 
           (?<If_Content>                     # (4), if content
                (?&_ifbody) 
           )
           's* %}
      )
      (?<Core>                           # (5), The CORE
           (?&_core) 
        |  
      )
      (?<End>                            # (6)
           {% 's* end 's+ if 's* %}           # END IF
      )
   |                                   # OR
      # --------------
      (?<Error>                          # (7), Unbalanced IF or END IF
           (?&_keyword) 
      )
 )
 # =====================
 #  Subroutines
 # ---------------
 (?(DEFINE)
      # __ If Body ----------------------
      (?<_ifbody>                        # (8)
           (?>
                (?! %} )
                . 
           )+
      )
      # __ Core -------------------------
      (?<_core>                          # (9)
           (?>
                #
                # __ Content ( non-keywords )
                (?<_content>                       # (10)
                     (?>
                          (?! (?&_keyword) )
                          . 
                     )+
                )
             |  
                #
                # __ Else
                # Guard:  Only 1 'else'
                # allowed in this core !!
                (?(<_else>)
                     (?!)
                )
                (?<_else>                          # (11)
                     (?> {% 's* else 's* %} )
                )
             |  
                #
                # IF  (block start)
                (?>
                     {% 's* if 's+ 
                     (?&_ifbody) 
                     's* %}
                )
                # Recurse core
                (?:
                     (?= . )
                     (?&_core) 
                  |  
                )
                # END IF  (block end)
                {% 's* end 's+ if 's* %}
           )+
      )
      # __ Keyword ----------------------
      (?<_keyword>                       # (12)
           (?>
                {% 's* 
                (?:
                     if 's+ (?&_ifbody) 
                  |  end 's+ if
                  |  else
                )
                's* %}
           )
      )
 )

示例输入(已删除)
选定输出(已删除)

伪代码使用示例

bool bStopOnError = false;
regex RxCore(".....");
bool ParseCore( string sCore, int nLevel )
{
    // Locals
    bool bFoundError = false; 
    bool bBeforeElse = true;
    match _matcher;
    while ( search ( core, RxCore, _matcher ) )
    {
      // Content
        if ( _matcher["Content"].matched == true )
          // Print non-keyword content
          print ( _matcher["Content"].str() );
          // OR, Analyze content.
          // If this 'content' has error's and wish to return.
          // if ( bStopOnError )
          //   bFoundError = true;
        else
      // Else 
        if ( _matcher["Else"].matched == true )
        {
            // Check if we are not in a recursion
            if ( nLevel <= 0 )
            {
               // Report error, this 'else' is outside an 'if/end if' block
               // ( note - will only occur when nLevel == 0 )
               print ("'n>> Error, 'else' not in block " + _matcher["Else"].str() + "'n";
               // If this 'else' error will stop the process.
               if ( bStopOnError == true )
                  bFoundError = true;
            }
            else
            {
                // Here, we are inside a core recursion.
                // That means there can only be 1 'else'.
                // Print 'else'.
                print ( _matcher["Else"].str() );
                // Set the state of 'else'. 
                bBeforeElse == false;   
            }
        }
        else
      // Error ( will only occur when nLevel == 0 )
        if ( _matcher["Error"].matched == true )
        {
            // Report error 
            print ("'n>> Error, unbalanced " + _matcher["Error"].str() + "'n";
            // // If this unbalanced 'if/end if' error will stop the process.
            if ( bStopOnError == true )
                bFoundError = true;
        }
        else
      // IF/END IF block
        if ( _matcher["Begin"].matched == true )
        {
            // Analyze 'if content' for error and wish to return.
            string sIfContent = _matcher["If_Content"].str();
            // if ( bStopOnError )
            //   bFoundError = true;
            // else
            // {            
                 // Print 'begin' ( includes 'if content' )
                 print ( _matcher["Begin"].str() );
                 //////////////////////////////
                 // Recurse a new 'core'
                 bool bResult = ParseCore( _matcher["Core"].str(), nLevel+1 );
                 //////////////////////////////
                 // Check recursion result. See if we should unwind.
                 if ( bResult == false && bStopOnError == true )
                     bFoundError = true;
                 else
                     // Print 'end'
                     print ( _matcher["End"].str() );
            // }
        }
        else 
        {
           // Reserved placeholder, won't get here at this time.
        }
      // Error-Return Check
         if ( bFoundError == true && bStopOnError == true )
             return false;
    }
    // Finished this core!! Return true.
    return true;
}
///////////////////////////////
// Main
string strInitial = "...";
bool bResult = ParseCore( strInitial, 0 );
if ( bResult == false )
   print ( "Parse terminated abnormally, check messages!'n" );