static function eval_expression($args, $context) { $op_stack = array(); $num_stack = array(); $expression_pos = 0; // Tracks position in the expression for better error reporting. $back_track_pos = 0; // Tracks position in the expression when backtracking for better error reporting. foreach ($args as $arg) { $expression_pos++; if (is_array($arg) && isset($arg['operator'])) { $back_track_pos = $expression_pos; while (Evaluator::higher_op_on_stack($arg['operator'], $op_stack)) { $val = Evaluator::eval_op_stack($op_stack, $num_stack, $back_track_pos); $num_stack[] = $val; } $op_stack[] = $arg['operator']; } else { if (is_array($arg) && isset($arg['parentheses'])) { if ($arg['parentheses'] === '(') { $op_stack[] = '('; } else { $back_track_pos = $expression_pos; while ($op = array_pop($op_stack)) { if ($op === '(') { break; } $op_stack[] = $op; $num_stack[] = Evaluator::eval_op_stack($op_stack, $num_stack, $back_track_pos); } if (is_null($op)) { throw new Exception("No opening paren for ')' at {$expression_pos}"); } } } else { $num_stack[] = $context->resolve($arg); } } } $back_track_pos = $expression_pos; while (count($op_stack) > 0) { $num_stack[] = Evaluator::eval_op_stack($op_stack, $num_stack, $back_track_pos); } return array_pop($num_stack); }