Regex Pattern with two equal but unknown parts

Viewed 80

I am currently working on a simple template engine. In a template, if-statements can be used. An if block looks like this

{% name IF: a EQUALS b %}

content

{% name ENDIF %}

I want to identify these blocks via regex. The Problem is I need a regex pattern which contains two unknown but equal parts. This is the pattern which matches to all blocks:

/{% +(.*) +IF: +(.*) +%}([\s\S]*){% +(.*) +ENDIF +%}/gm

To clarify which ENDIF marker belongs to which IF the first and the last capture group needs to be the same. Is there a way to do this?

2 Answers

You may use this regex:

{%\s+(\S+)\s+IF:.+?%}(?s)(.+?){%\s+\1\s+ENDIF\s+%}

RegEx Demo

RegEx Details:

  • {%: Match {%
  • \s+: Match 1+ whitespaces
  • (\S+):
  • \s+: Match 1+ whitespaces
  • IF:: Match IF:
  • .+?: Match 1+ of any character (non-greedy)
  • %}: Match %}
  • (?s): Enable DOTALL mode so that dot matches line break
  • (.+?): 1st capture group to match 1+ of any characters
  • {%: Match {%
  • \s+: Match 1+ whitespaces
  • \1: Match same value as what we captured in capture group #1
  • \s+: Match 1+ whitespaces
  • ENDIF: Match ENDIF
  • \s+: Match 1+ whitespaces
  • %}: Match closing %}

You can match the following regular expression (with general g, multiline m and case-indifferent i flags set).

{% +([a-z]+) +IF:.*? +%}\r?\n(?:^.*\r?\n)*?{% +\1 +ENDIF +%}$

Demo

The expression breaks down as follows (alternatively, hover the cursor of each element of the expression at the regex101.com link to obtain an explanation of its function).

{% +             # match `{%` followed by one or more spaces
([a-z]+) +       # match one or more letters followed by one or more spaces
IF:.*? +%}\r?\n  # match 'IF: followed by zero or more characters, matched
                 # lazily, followed by one or more spaces followed by '%}'
                 # followed by a line terminator (`\r?` to satisfy Windows)
(?:              # begin non-capture group
  ^              # match beginning of line
  .*\r?\n        # match one or more characters other than newlines followed
                 # by a line terminator
)*?              # end non-capture group and execute it zero or more times, lazily
{% +             # match `{%` followed by one or more spaces
\1 +             # match the content of capture group 1 followed by one or
                 # more spaces
ENDIF +          # match `ENDIF` followed by one or more spaces
%}$              # match `{%` at the end of a line

The link demonstates that if the block of text were:

{% Saffi IF: my { % dog } has fleas %}

content

{% Saffi ENDIF %}

it too would be matched.

Related