<?php
/**
* E2pdf Shortcode If Helper
* @copyright Copyright 2017 https://e2pdf.com
* @license GPLv3
* @version 1
* @link https://e2pdf.com
* @since 1.22.00
*/
if (!defined('ABSPATH')) {
die('Access denied.');
}
/** @license
* Based on https://gist.github.com/ircmaxell/1232629
*/
class Helper_E2pdf_Math {
private $helper;
protected $variables = array();
protected $stack = array();
protected $output = array();
public function __construct() {
$this->helper = Helper_E2pdf_Helper::instance();
}
public function evaluate($string) {
try {
$this->stack = $this->parse($string);
return $this->run();
} catch (Exception $e) {
return $e->getMessage();
}
}
public function parse($string) {
$tokens = $this->tokenize($string);
$this->output = array();
$this->operators = array();
foreach ($tokens as $token) {
$token = $this->extractVariables($token);
$expression = $this->factory($token);
if ($expression->operator) {
$this->parseOperator($expression);
} elseif (isset($expression->type) && $expression->type == 'parenthesis') {
$this->parseParenthesis($expression);
} else {
$this->output[] = $expression;
}
}
while (($op = array_pop($this->operators))) {
if (isset($op->type) && $op->type == 'parenthesis') {
throw new Exception(__('Mismatched Parenthesis in Math operation', 'e2pdf'));
}
$this->output[] = $op;
}
return $this->output;
}
public function registerVariable($name, $value) {
$this->variables[$name] = $value;
}
public function run() {
while (($operator = array_pop($this->stack)) && $operator->operator) {
$value = $this->operate($operator);
if (!is_null($value)) {
$this->stack[] = $this->factory($value);
}
}
return $operator ? $operator->value : $this->render();
}
protected function extractVariables($token) {
if ($token[0] == '$') {
$key = substr($token, 1);
return isset($this->variables[$key]) ? $this->variables[$key] : 0;
}
return $token;
}
protected function render() {
$output = '';
while (($el = array_pop($this->stack))) {
$output .= $el->value;
}
if ($output) {
return $output;
}
throw new Exception(__('Could not parse Math operation', 'e2pdf'));
}
protected function parseParenthesis($expression) {
if ($expression->open) {
$this->operators[] = $expression;
} else {
$clean = false;
while (($end = array_pop($this->operators))) {
if (isset($end->type) && $end->type == 'parenthesis') {
$clean = true;
break;
} else {
$this->output[] = $end;
}
}
if (!$clean) {
throw new Exception(__('Mismatched Parenthesis in Math operation', 'e2pdf'));
}
}
}
protected function parseOperator($expression) {
$end = end($this->operators);
if (!$end) {
$this->operators[] = $expression;
} elseif ($end->operator) {
do {
if ($expression->operator && $expression->precidence <= $end->precidence) {
$this->output[] = array_pop($this->operators);
} elseif (!$expression->operator && $expression->precidence < $end->precidence) {
$this->output[] = array_pop($this->operators);
} else {
break;
}
} while (($end = end($this->operators)) && $end->operator);
$this->operators[] = $expression;
} else {
$this->operators[] = $expression;
}
}
protected function tokenize($string) {
$parts = preg_split('((\b\d[\d.]*\b|\+|-|\(|\)|\*|¦|/)|\s+)', $string, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$parts = array_map('trim', $parts);
return $parts;
}
protected function factory($value) {
$expression = new stdClass();
$expression->open = false;
$expression->operator = false;
$expression->precidence = 0;
$expression->type = 'expression';
if (is_object($value)) {
return $value;
} elseif (is_numeric($value)) {
$expression->type = 'number';
} elseif ($value == '+') {
$expression->operator = true;
$expression->type = 'addition';
$expression->precidence = 4;
} elseif ($value == '-') {
$expression->operator = true;
$expression->type = 'subtraction';
$expression->precidence = 4;
} elseif ($value == '*') {
$expression->operator = true;
$expression->type = 'multiplication';
$expression->precidence = 5;
} elseif ($value === '¦') {
$expression->operator = true;
$expression->type = 'modulus';
$expression->precidence = 5;
} elseif ($value == '/') {
$expression->operator = true;
$expression->type = 'division';
$expression->precidence = 5;
} elseif ($value == '^') {
$expression->operator = true;
$expression->type = 'power';
$expression->precidence = 6;
} elseif (in_array($value, array('(', ')'))) {
$expression->type = 'parenthesis';
$expression->precidence = 7;
$expression->open = $value == '(';
}
$expression->value = $value;
return $expression;
}
protected function operate($expression) {
$value = null;
if (isset($expression->type)) {
switch ($expression->type) {
case 'number':
$value = $expression->value;
break;
case 'addition':
$value = $this->operate(array_pop($this->stack)) + $this->operate(array_pop($this->stack));
break;
case 'subtraction':
$left = $this->operate(array_pop($this->stack));
$right = $this->operate(array_pop($this->stack));
$value = $right - $left;
break;
case 'multiplication':
$value = $this->operate(array_pop($this->stack)) * $this->operate(array_pop($this->stack));
break;
case 'division':
$left = $this->operate(array_pop($this->stack));
$right = $this->operate(array_pop($this->stack));
$value = $right / $left;
break;
case 'power':
$left = $this->operate(array_pop($this->stack));
$right = $this->operate(array_pop($this->stack));
$value = pow($right, $left);
break;
case 'modulus':
$left = $this->operate(array_pop($this->stack));
$right = $this->operate(array_pop($this->stack));
$value = ($left == 0) ? 0 : ($right % $left);
break;
default:
break;
}
}
return $value;
}
}
|