嵌套的PCRE正则表达式问题


Nested PCRE Regex Issue

我有一个自定义模板引擎。

它捕获了这个:

@function(argument1 argument2 ...)
@get(param:name)
@get(param:@get(sub:name))

和这个:

@function(argument1 argument2 ...)
    Some stuff @with(nested:tag)
    @foreach(arguments as value)
        More stuff : @get(value)
    @/foreach
    @function(other:args)
        Same function name (nested)
    @/function
@/function

使用这个模式(PCRE/PHP):

#
@ (['w]+) '(
( (?: [^@')] | (?R) )+ )
')
(?:
    ( (?> (?-2) ) )
    @/''1
)?
#xms

这个正则表达式捕获几乎所有的结果。但是当我有更多的嵌套(或没有)标签时,它什么也抓不到。例如,当我做2嵌套@foreach(var:name) ... @/foreach时,则regex将根据标签内容spaces失败。

使用命名子模式有时更清楚。我建议你这样做:

~
@(?<com>'w+)                 # command name
's*                          # possible white characters before args
(?: '( (?<args>[^)]*) ') )?+ # eventual parameters
(?:
    (?<content>(?:[^@]+|(?R))*+) # content (maybe empty)
    @/'g{com}                    # close the command
)?+                          # optional
~

如果你需要允许命令在参数中,你可以用(?<args>(?:[^@)]+|(?=@)(?R))*+)代替(?<args>[^)]*)

但是当你试图描述一门语言时,一个更好的方法是使用(?(DEFINE)...)语法首先描述元素,在主模式之前,例如:

$pattern = <<<'EOD'
~
(?(DEFINE)
    (?<command_name> 'w+ )
    (?<inline_command> @ 'g<command_name> 's* 'g<params>? )
    (?<multil_command> @ ('g<command_name>) 's* 'g<params>? 'g<content> @/ 'g{-1} )
    (?<command> 'g<multil_command> | 'g<inline_command> )
    (?<other> [^@()]+ ) 
    (?<param> 'g<other> | 'g<command> )
    (?<params> '( 's* 'g<param> (?: 's+ 'g<param> )* 's* ') )
    (?<content> (?: 'g<other> | 'g<command> )* )
)
# main pattern
'g<command>
~x
EOD;

使用这种语法,如果您想在底层提取元素,您只需要将主模式更改为:@(?<com> 'g<command_name> ) 's* (?<args>'g<params> )? (?: (?<con> 'g<content> ) @/ 'g{com} )?(注意:要获得其他级别,请将其放在forward中)