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

标签 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

由于您正在进行实验,经过一番闲逛后,为您想出了一个通用的正则表达式。

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

概要:

在纯正则表达式解决方案中,平衡文本的概念大约是
正则表达式引擎消失了。它不会填写详细信息。
为此你必须自己做。

与下降解析器等相比,这是一种缓慢的方法。
不同之处在于它不需要放松就知道它在哪里。
因此,这将允许您在遇到错误时继续解析。
有点从过去的错误中获得更多意义以帮助调试。

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

在本例中,我们在外部范围上预留了 7 个捕获组来浏览信息。

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

Else - 这是else语句

Begin - 这是开始 if block

If_Content - 这是 if block 内容

核心 - 这是外部开始和结束之间的所有内容。还包含嵌套内容。

End - 这是外部 end if block

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

用法:

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

在此函数中,处于 while 循环中解析字符串。
在每场比赛中,执行 if/else 查看上面的哪个组匹配。
只能是这些组合

内容

其他

开始、If_Content、核心、结束

错误

只有一组对于递归很重要。这是核心组。
当该组匹配时,您将递归函数调用
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" );

关于php - 使用正则表达式解析嵌套的 IF 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36700579/

相关文章:

php - 用php上传图片到数据库

python - 重复某个字符后删除单词?

java - 在 Android studio 中使用 Activity 模板时无法添加新的 Java 类吗?

python - 如何在字符串中查找正值或负值?

c++ - 在 C++ 中使用 [] 而不是函数调用

c++ - 模板重载优先级

php - 使用 LAMP Tasksel 安装 PHP-CGI

php - 删除 MySQL 中的 JSON 字段 [PHP] [CodeIgniter]

php - 确认设置提交按钮后提交 Jquery 表单

r - 以任何顺序(提取/分离/匹配)组