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

时间:2016-04-18 17:29:07

标签: php regex templates

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

您可以使用一些工作示例查看正则表达式here,然后查看正在尝试工作的示例。

这是正则表达式:

{%\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%}标记,并将其中的所有内容用作我之后可以递归解析的字符串。

另外,作为附注,将大部分问题的信息留在正则表达式测试人员的链接中是否合适?或者我是否也应该在这里复制大部分问题(在SO上)?

1 个答案:

答案 0 :(得分:2)

第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()时,您只需传入整个原始字符串,即可启动解析。

祝你好运!

Expanded

 # (?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" );