Reputation: 539
I'm trying to do conditional statements in php template system, but I have some problems with making it working.
My syntax is (condition? value to show if condition is true)
. Matching would be easily accomplished using this pattern: \((\w+)\?(.+?)\)
. The problem is I need it to work recursively.
I tried these patterns on the string (it should be (a?working(b? with nested conditions).))
:
\((\w+)\?(.+?|(?R))\)
but it matches (a?working(b? with nested conditions)
(skips .)
in the end)
\((\w+)\?(.+|(?R))\)
but it matches (a?working(b? with nested conditions).))
(everything until last )
).
Help me, I'm stuck.
Upvotes: 1
Views: 198
Reputation: 539
As @Sniffer said, I've made a parser. It's quite messy, but does the job. It is part of template system class. $this->rendered
is a string which is being parsed.
const COND_START = '((';
const COND_END = '))';
const COND_SEP = '?';
const COND_NOT = '!';
private function parseConditionals()
{
for (
$i=0,
$level=0,
$levelstart=array(0=>0),
$levelseparator=array(0=>0),
$levelname=array(0=>'__main__');
$i < strlen($this->rendered);
) {
$startpos = strpos($this->rendered, self::COND_START, $i);
$seppos = strpos($this->rendered, self::COND_SEP, $i);
$endpos = strpos($this->rendered, self::COND_END, $i);
if ( ($startpos === FALSE) && ($endpos === FALSE) ) {
$i = strlen($this->rendered);
} elseif ( ($startpos !== FALSE) && $startpos < $endpos ) {
if ( $seppos < $endpos ) {
$level++;
$levelstart[$level] = $startpos;
$levelseparator[$level] = $seppos;
$levelname[$level] = substr(
$this->rendered,
$startpos+strlen(self::COND_START),
$seppos-$startpos-strlen(self::COND_START)
);
$i = $seppos + strlen(self::COND_SEP);
} else {
$i = $startpos + strlen(self::COND_START);
}
} else {
$originallen = strlen($this->rendered);
if ( $level > 0 ) {
$not = false;
if ( strpos($levelname[$level], self::COND_NOT) === 0 ) {
$levelname[$level] = substr($levelname[$level], strlen(self::COND_SEP));
$not = true;
}
if (
!$this->get($levelname[$level]) == $not
) {
$this->rendered = substr_replace(
$this->rendered,
'',
$endpos,
strlen(self::COND_END)
);
$this->rendered = substr_replace(
$this->rendered,
'',
$levelstart[$level],
$levelseparator[$level]-$levelstart[$level]+strlen(self::COND_SEP)
);
} else {
$this->rendered = substr_replace(
$this->rendered,
'',
$levelstart[$level],
$endpos-$levelstart[$level]+strlen(self::COND_END)
);
}
$level--;
}
$i = $endpos + strlen(self::COND_END);
$i += strlen($this->rendered)-$originallen;
}
}
}
Upvotes: 0
Reputation: 19423
Try the following pattern:
\((\w+)\?([^()]|\((?!\w+\?)|(?R))+\)
Edit: OK try changing the pattern to the following one:
\((\w+)\?(.+|\((?!\w+\?)|(?R))+\)
^^
If this doesn't work as well, try changing .+
to .+?
. If all of this doesn't work for you then you probably (I think this is the better solution) need to use a parser instead of regular expressions.
Upvotes: 1