Skip to content

Commit 93b74be

Browse files
committed
fix nested formulas
1 parent 332a11b commit 93b74be

File tree

3 files changed

+76
-18
lines changed

3 files changed

+76
-18
lines changed

Expression.lib.php

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ function nfx($expr)
211211
return $this->trigger("illegal character '{$matches[0]}'");
212212
}
213213
*/
214-
$first_argument = false;
214+
$begin_argument = false;
215215
while (1) { // 1 Infinite Loop ;)
216216
$op = substr($expr, $index, 2); // get the first two characters at the current index
217217
if (preg_match("/^[+\-*\/^_\"<>=%(){\[!~,](?!=|~)/", $op) || preg_match("/\w/", $op)) {
@@ -297,19 +297,20 @@ function nfx($expr)
297297
// make sure there was a function
298298
if (!preg_match("/^([a-z]\w*)\($/", $stack->last(2), $matches))
299299
return $this->trigger("unexpected ','");
300-
if ($first_argument) {
301-
$first_argument = false;
302-
} else {
303-
$stack->push($stack->pop() + 1); // increment the argument count
304-
}
305300
$stack->push('('); // put the ( back on, we'll need to pop back to it again
306301
$index++;
307302
$expecting_op = false;
303+
$begin_argument = true;
308304
//===============
309305
} elseif ($op == '(' and !$expecting_op) {
306+
if ($begin_argument) {
307+
$begin_argument = false;
308+
if (!$stack->incrementArgument()) {
309+
$this->trigger("unexpected '('");
310+
}
311+
}
310312
$stack->push('('); // that was easy
311313
$index++;
312-
// $allow_neg = true;
313314
//===============
314315
} elseif ($ex and !$expecting_op) { // do we now have a function/variable/number?
315316
$expecting_op = true;
@@ -321,9 +322,15 @@ function nfx($expr)
321322
array_key_exists($matches[1], $this->f) or
322323
array_key_exists($matches[1], $this->functions)
323324
) { // it's a func
325+
if ($begin_argument) {
326+
if (!$stack->incrementArgument()) {
327+
$this->trigger("unexpected '('");
328+
}
329+
}
324330
$stack->push($val);
325331
$stack->push(0);
326332
$stack->push('(');
333+
$begin_argument = true;
327334
$expecting_op = false;
328335
} else { // it's a var w/ implicit multiplication
329336
$val = $matches[1];
@@ -332,17 +339,13 @@ function nfx($expr)
332339
} else { // it's a plain old var or num
333340
$output[] = $val;
334341
if (preg_match("/^([a-z]\w*)\($/", $stack->last(3))) {
335-
$first_argument = true;
336-
while (($o2 = $stack->pop()) != '(') {
337-
if (is_null($o2)) return $this->trigger("unexpected error"); // oops, never had a (
338-
else $output[] = $o2; // pop the argument expression stuff and push onto the output
339-
}
340-
// make sure there was a function
341-
if (!preg_match("/^([a-z]\w*)\($/", $stack->last(2), $matches))
342-
return $this->trigger("unexpected error");
342+
if ($begin_argument) {
343+
$begin_argument = false;
343344

344-
$stack->push($stack->pop() + 1); // increment the argument count
345-
$stack->push('('); // put the ( back on, we'll need to pop back to it again
345+
if (!$stack->incrementArgument()) {
346+
$this->trigger("unexpected error");
347+
}
348+
}
346349
}
347350
}
348351
$index += strlen($val);
@@ -574,6 +577,26 @@ function pop()
574577
return null;
575578
}
576579

580+
function incrementArgument()
581+
{
582+
while (($o2 = $this->pop()) != '(') {
583+
if (is_null($o2)) {
584+
return false; // oops, never had a (
585+
} else {
586+
$output[] = $o2; // pop the argument expression stuff and push onto the output
587+
}
588+
}
589+
// make sure there was a function
590+
if (!preg_match("/^([a-z]\w*)\($/", $this->last(2), $matches)) {
591+
return false;
592+
}
593+
594+
$this->push($this->pop() + 1); // increment the argument count
595+
$this->push('('); // put the ( back on, we'll need to pop back to it again
596+
597+
return true;
598+
}
599+
577600
function isEmpty()
578601
{
579602
return empty($this->stack);

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "optimistex/yii2-expression",
33
"description": "Taken from http://www.phpclasses.org/browse/file/11680.html, cred to Miles Kaufmann",
44
"type": "yii2-extension",
5-
"version": "2.0.1.1",
5+
"version": "2.0.1.2",
66
"license": "MIT",
77
"authors": [
88
{

tests/Expression.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,4 +299,39 @@ public function testBug_1()
299299
$this->assertEquals($expr->evaluate($formula), 364);
300300
}
301301
// */
302+
303+
public function testFormulaWithBrackets()
304+
{
305+
$e = new Expression();
306+
$e->suppress_errors = true;
307+
$e->functions = [
308+
'min' => function ($v1, $v2) {
309+
return min($v1, $v2);
310+
},
311+
'max' => function ($v1, $v2) {
312+
return max($v1, $v2);
313+
},
314+
];
315+
// $formula = 'max(2,(2+2)*2)';
316+
317+
$data = [
318+
'max(2,(2+2)*2)' => 8,
319+
'max((2),(0))' => 2,
320+
'max((2+2)*2,(3+2)*2)' => 10,
321+
'max((4+2)*2,(3+2)*2)' => 12,
322+
'min(max((2+2)*2,(3+2)*2), 1)' => 1,
323+
'min(1, max(2,3))' => 1,
324+
'min(1, max((4+2)*2,(3+2)*2))' => 1,
325+
'min(max((2+2)*2,(3+2)*2), max((4+2)*2,(3+2)*2))' => 10,
326+
'max( min((2+2)*2,(3+2)*2), min((4+2)*2,(3+2)*2) )' => 10,
327+
'1 + max( min(2-(2+2)*2,(3+2)*2), min((4+2)*2,(3+2)*2) ) - 2' => 9,
328+
'max(1,max(2,max(3,4)))' => 4,
329+
'max( min(1 + 2, 4), 5)' => 5,
330+
'min(2,(2+2)*2)' => 2,
331+
];
332+
333+
foreach ($data as $formula => $result) {
334+
$this->assertEquals($e->evaluate($formula), $result);
335+
}
336+
}
302337
}

0 commit comments

Comments
 (0)