如何使用正则表达式捕获嵌套的 {% if ... %}{% endif %} 语句
How to catch nested {% if ... %}{% endif %} statments with regex
这是我现在得到的:
/{% if(.+?) %}(.*?){% endif %}/gusi
它可以很好地捕获多个 if 语句等。
IMG:http://image.xesau.eu/2015-02-07_23-22-11.png
但是当我做嵌套的时候,所以 if 中的 if,它会在第一次出现 {% endif %} 时停止
IMG:http://image.xesau.eu/2015-02-08_09-29-43.png
有没有一种方法可以捕获与 {% if ... %} 语句一样多的 {% endif %} 语句,如果可以,怎么做?
将您的模式更改为 recursive pattern:
几乎是微不足道的
{% if(.+?) %}((?>(?R)|.)*?){% endif %}
工作示例:https://regex101.com/r/gX8rM0/1
但是, 这将是一个坏主意:该模式缺少许多情况,这确实是您的解析器中的错误。举几个常见的例子:
评论:
{% if aaa %}
123
<!-- {% endif %} -->
{% endif %}
字符串文字:
{% if aaa %}a = "{% endif %}"{% endif %}
{% if $x == "{% %}" %}...{% endif %}
转义字符(你确实需要转义字符,对吧?):
<p>To start a condition, use <code>\{% if aaa %}</code></p>
输入无效:
如果解析器能够在无效输入上工作得比较好,并指出错误的正确位置,那就太好了。
不要使用正则表达式,使用现有的 Twig 解析器。这是我编写的提取器示例,它解析自定义标签并提取它们:https://github.com/deceze/Twig-extensions/tree/master/lib/Twig/Extensions/Extension/Gettext
词法分析器的工作是将Twig源代码转化为对象;如果你需要连接到那个进程,你可以扩展它:
class My_Twig_Lexer extends Twig_Lexer {
...
/**
* Overrides lexComment by saving comment tokens into $this->commentTokens
* instead of just ignoring them.
*/
protected function lexComment() {
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename);
}
$value = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
$token = new Twig_Extensions_Extension_Gettext_Token(Twig_Extensions_Extension_Gettext_Token::COMMENT, $value, $this->lineno);
$this->commentTokens[] = $token;
$this->moveCursor($value . $match[0][0]);
}
...
}
通常 Twig 注释节点会被 Twig 丢弃,这个词法分析器会保存它们。
但是,您主要关心的是使用解析器:
$twig = new Twig_Environment(new Twig_Loader_String);
$lexer = new My_Twig_Lexer($twig);
$parser = new Twig_Parser($twig);
$source = file_get_contents($file);
$tokens = $lexer->tokenize($source);
$node = $parser->parse($tokens);
processNode($node);
$node
这里是节点树的根节点,以面向对象的方式表示 Twig 源,所有节点都已正确解析。您只需要处理这棵树,而不必担心用于生成它的确切语法:
processNode(Twig_NodeInterface $node) {
switch (true) {
case $node instanceof Twig_Node_Expression_Function :
processFunctionNode($node);
break;
case $node instanceof Twig_Node_Expression_Filter :
processFilterNode($node);
break;
}
foreach ($node as $child) {
if ($child instanceof Twig_NodeInterface) {
processNode($child);
}
}
}
只需要遍历它,直到找到您要查找的节点类型并获取其信息。稍微玩一下。这个示例代码可能有点过时也可能不会,无论如何你都必须深入研究 Twig 解析器源代码才能理解它。
这是我现在得到的:
/{% if(.+?) %}(.*?){% endif %}/gusi
它可以很好地捕获多个 if 语句等。
IMG:http://image.xesau.eu/2015-02-07_23-22-11.png
但是当我做嵌套的时候,所以 if 中的 if,它会在第一次出现 {% endif %} 时停止
IMG:http://image.xesau.eu/2015-02-08_09-29-43.png
有没有一种方法可以捕获与 {% if ... %} 语句一样多的 {% endif %} 语句,如果可以,怎么做?
将您的模式更改为 recursive pattern:
几乎是微不足道的{% if(.+?) %}((?>(?R)|.)*?){% endif %}
工作示例:https://regex101.com/r/gX8rM0/1
但是, 这将是一个坏主意:该模式缺少许多情况,这确实是您的解析器中的错误。举几个常见的例子:
评论:
{% if aaa %} 123 <!-- {% endif %} --> {% endif %}
字符串文字:
{% if aaa %}a = "{% endif %}"{% endif %} {% if $x == "{% %}" %}...{% endif %}
转义字符(你确实需要转义字符,对吧?):
<p>To start a condition, use <code>\{% if aaa %}</code></p>
输入无效:
如果解析器能够在无效输入上工作得比较好,并指出错误的正确位置,那就太好了。
不要使用正则表达式,使用现有的 Twig 解析器。这是我编写的提取器示例,它解析自定义标签并提取它们:https://github.com/deceze/Twig-extensions/tree/master/lib/Twig/Extensions/Extension/Gettext
词法分析器的工作是将Twig源代码转化为对象;如果你需要连接到那个进程,你可以扩展它:
class My_Twig_Lexer extends Twig_Lexer {
...
/**
* Overrides lexComment by saving comment tokens into $this->commentTokens
* instead of just ignoring them.
*/
protected function lexComment() {
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename);
}
$value = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
$token = new Twig_Extensions_Extension_Gettext_Token(Twig_Extensions_Extension_Gettext_Token::COMMENT, $value, $this->lineno);
$this->commentTokens[] = $token;
$this->moveCursor($value . $match[0][0]);
}
...
}
通常 Twig 注释节点会被 Twig 丢弃,这个词法分析器会保存它们。
但是,您主要关心的是使用解析器:
$twig = new Twig_Environment(new Twig_Loader_String);
$lexer = new My_Twig_Lexer($twig);
$parser = new Twig_Parser($twig);
$source = file_get_contents($file);
$tokens = $lexer->tokenize($source);
$node = $parser->parse($tokens);
processNode($node);
$node
这里是节点树的根节点,以面向对象的方式表示 Twig 源,所有节点都已正确解析。您只需要处理这棵树,而不必担心用于生成它的确切语法:
processNode(Twig_NodeInterface $node) {
switch (true) {
case $node instanceof Twig_Node_Expression_Function :
processFunctionNode($node);
break;
case $node instanceof Twig_Node_Expression_Filter :
processFilterNode($node);
break;
}
foreach ($node as $child) {
if ($child instanceof Twig_NodeInterface) {
processNode($child);
}
}
}
只需要遍历它,直到找到您要查找的节点类型并获取其信息。稍微玩一下。这个示例代码可能有点过时也可能不会,无论如何你都必须深入研究 Twig 解析器源代码才能理解它。