【问题标题】:Trouble with regular expression matching in lexer词法分析器中的正则表达式匹配问题
【发布时间】:2013-12-15 18:18:51
【问题描述】:

我正在制作一个非常复杂的模板引擎,因为它将具有编程语言中的典型结构,例如 if 语句和循环。

目前,我正在研究词法分析器,我相信它处理将字符流转换为标记的工作。我想要做的是捕获 HTML 文档中的某些结构,稍后可以由解析器处理。

这是一个语法示例:

<head>

    <title>Template</title>
    <meta charset="utf-8">

</head>

<body>

    <h1>{{title}}</h1>

    <p>This is also being matched.</p>

    {{#myName}}
        <p>My name is {{myName}}</p>
    {{/}}

    <p>This content too.</p>

    {{^myName}}
        <p>I have on name.</p>
    {{/}}

    <p>No matching here...</p>

</body>

我正在尝试仅扫描开始 '{{' 字符和结束 '}}' 字符之间的所有内容。因此,{{title}} 将是一个匹配项,与 {{#myName}}、指向 {{/}} 的文本和内容一起,这应该是第二个匹配项。

我并不是特别擅长正则表达式,我很确定这是我设计的模式的问题,即:

({{([#\^]?)([a-zA-Z0-9\._]+)}}([\w\W]+){{\/?}})

我认为这是匹配两个 { 字符,然后是 # 或 ^ 任何包含大写或小写字母的单词,以及任何数字、点或下划线。匹配结束 }} 字符之后的任何内容,直到遇到 {{/}} 字符,但 /}} 部分是可选的。

问题在下面的链接中可见。它匹配不在 {{ 和 }} 块内的文本。我想知道它与 \w 和 \W 的使用有关,因为如果我具体指定要在集合中匹配哪些字符,它似乎就可以工作了。

正则表达式测试是here。我确实看过正则表达式是用于捕获所有非 HTML 文本的共享列表,我注意到它使用了我无法掌握的前瞻,或者理解它们为什么会帮助我。

有人可以通过指出正则表达式的问题来帮助我,或者我在创建词法分析器方面是否走错了路?

希望我提供了足够的信息,感谢您的帮助!

【问题讨论】:

  • 你匹配的太多了。您应该简单地将个人 {{things}} 转换为令牌。一方面,您的示例包含嵌套的{{things}} - 当然不能是单个标记(并且肯定正则表达式不足以捕获那种结构)。事实上,任何有结构的事情都应该发生在语法中,而不是词法分析器中。
  • @tripleee 我打算这样做,但我想先捕获它们,然后进一步分解它们,因为我对其他内容不感兴趣。

标签: php regex lexer


【解决方案1】:

您的模式不起作用,因为[\w\W]+ 会占用所有可能的字符,直到字符串的最后一个{{/}}。量词(即+*{1,3}?)默认是贪婪的。要获得惰性量词,您必须在其后添加?[\w\W]+?

一种处理嵌套结构的模式:

$pattern = <<<'LOD'
~
{{
(?|                  # branch reset group: the interest of this feature is that
                     # capturing group numbers are the same in all alternatives
    ([\w.]++)}}      # self-closing tag: capturing group 1: tag name
  |                  # OR
    ([#^][\w.]++)}}  # opening tag:      capturing group 1: tag name
    (                # capturing group 2: content
        (?>          # atomic group: three possible content type
            [^{]++   # all characters except { 
          |          # OR
            {(?!{)   # { not followed by another {
          |          # OR
            (?R)     # an other tag is met, attempt the whole pattern again
        )*           # repeat the atomic group 0 or more times
    )                # close the second capturing group
    {{/}}            # closing tag
)                    # close the branch reset group
~x
LOD;

preg_match_all($pattern, $html, $matches);

var_dump($matches);

要获得所有嵌套级别,您可以使用此模式:

$pattern = <<<'LOD'
~
(?=(                            # open a lookahead and the 1st capturing group
    {{
    (?|
        ([\w.]++)}}
      |
        ([#^][\w.]++)}}
        (                       # ?R was changed to ?1 because I don't want to
        (?>[^{]++|{(?!{)|(?1))* # repeat the whole pattern but only the
        )                       # subpattern in the first capturing group
        {{/}}
    )
)                               # close the 1st capturing group 
)                               # and the lookahead
~x
LOD;

preg_match_all($pattern, $html, $matches);

var_dump($matches);

此模式只是包含在前瞻和捕获组中的第一个模式。此构造允许捕获重叠的子字符串。

有关这两种模式中使用的正则表达式功能的更多信息:

  • possessive quantifiers++
  • atomic groups(?&gt;..)
  • lookahead(?=..)(?!..)
  • branch reset group(?|..|..)
  • recursion(?R),(?1)
  • 【讨论】:

    • 谢谢。我一定会看看这些资源。正则表达式有很多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 2013-01-04
    • 1970-01-01
    相关资源
    最近更新 更多