100 public $suppress_errors =
false;
102 public $last_error = null;
104 public $last_error_code = null;
106 public $v = array(
'e' => 2.71,
'pi' => 3.14159);
112 public $vb = array(
'e',
'pi');
116 'sin',
'sinh',
'arcsin',
'asin',
'arcsinh',
'asinh',
'cos',
'cosh',
'arccos',
'acos',
'arccosh',
'acosh',
'tan',
'tanh',
'arctan',
'atan',
'arctanh',
'atanh',
'sqrt',
'abs',
'ln',
'log',
'intval');
124 $this->v[
'pi'] = pi();
125 $this->v[
'e'] = exp(1);
134 public function e($expr)
147 $this->last_error = null;
148 $this->last_error_code = null;
150 if (substr($expr, - 1, 1) ==
';')
151 $expr = substr($expr, 0, strlen($expr) - 1);
155 if (preg_match(
'/^\s*([a-z]\w*)\s*=\s*(.+)$/', $expr, $matches)) {
156 if (in_array($matches[1], $this->vb)) {
157 return $this->
trigger(1,
"cannot assign to constant '$matches[1]'", $matches[1]);
159 if (($tmp = $this->
pfx($this->
nfx($matches[2]))) ===
false)
161 $this->v[$matches[1]] = $tmp;
162 return $this->v[$matches[1]];
165 } elseif (preg_match(
'/^\s*([a-z]\w*)\s*\(\s*([a-z]\w*(?:\s*,\s*[a-z]\w*)*)\s*\)\s*=\s*(.+)$/', $expr, $matches)) {
167 if (in_array($matches[1], $this->fb)) {
168 return $this->
trigger(2,
"cannot redefine built-in function '$matches[1]()'", $matches[1]);
170 $args = explode(
",", preg_replace(
"/\s+/",
"", $matches[2]));
171 if (($stack = $this->
nfx($matches[3])) ===
false)
173 $nbstack = count($stack);
174 for ($i = 0; $i < $nbstack; $i++) {
176 if (preg_match(
'/^[a-z]\w*$/', $token) and !in_array($token, $args)) {
177 if (array_key_exists($token, $this->v)) {
178 $stack[$i] = $this->v[$token];
180 return $this->
trigger(3,
"undefined variable '$token' in function definition", $token);
184 $this->f[$fnn] = array(
'args' => $args,
'func' => $stack);
188 return $this->
pfx($this->
nfx($expr));
200 unset($output[
'pi']);
213 foreach ($this->f as $fnn => $dat)
214 $output[] = $fnn.
'('.implode(
',', $dat[
'args']).
')';
226 private function nfx($expr)
231 $expr = trim(strtolower($expr));
233 $ops = array(
'+',
'-',
'*',
'/',
'^',
'_');
234 $ops_r = array(
'+' => 0,
'-' => 0,
'*' => 0,
'/' => 0,
'^' => 1);
235 $ops_p = array(
'+' => 0,
'-' => 0,
'*' => 1,
'/' => 1,
'_' => 1,
'^' => 2);
237 $expecting_op =
false;
241 if (preg_match(
"/[^\w\s+*^\/()\.,-]/", $expr, $matches)) {
242 return $this->
trigger(4,
"illegal character '{$matches[0]}'", $matches[0]);
246 $op = substr($expr, $index, 1);
249 $ex = preg_match(
'/^([a-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/', substr($expr, $index), $match);
251 if ($op ==
'-' and !$expecting_op) {
254 } elseif ($op ==
'_') {
255 return $this->
trigger(4,
"illegal character '_'",
"_");
257 } elseif ((in_array($op, $ops) or $ex) and $expecting_op) {
263 while ($stack->count > 0 and ($o2 = $stack->last()) and in_array($o2, $ops) and ($ops_r[$op] ? $ops_p[$op] < $ops_p[$o2] : $ops_p[$op] <= $ops_p[$o2])) {
264 $output[] = $stack->pop();
269 $expecting_op =
false;
271 } elseif ($op ==
')' and $expecting_op) {
272 while (($o2 = $stack->pop()) !=
'(') {
274 return $this->
trigger(5,
"unexpected ')'",
")");
279 if (preg_match(
"/^([a-z]\w*)\($/", $stack->last(2), $matches)) {
281 $arg_count = $stack->pop();
282 $output[] = $stack->pop();
283 if (in_array($fnn, $this->fb)) {
285 return $this->
trigger(6,
"wrong number of arguments ($arg_count given, 1 expected)", array($arg_count, 1));
286 } elseif (array_key_exists($fnn, $this->f)) {
287 if ($arg_count != count($this->f[$fnn][
'args']))
288 return $this->
trigger(6,
"wrong number of arguments ($arg_count given, ".count($this->f[$fnn][
'args']).
" expected)", array($arg_count, count($this->f[$fnn][
'args'])));
290 return $this->
trigger(7,
"internal error");
295 } elseif ($op ==
',' and $expecting_op) {
296 while (($o2 = $stack->pop()) !=
'(') {
298 return $this->
trigger(5,
"unexpected ','",
",");
304 if (!preg_match(
"/^([a-z]\w*)\($/", $stack->last(2), $matches))
305 return $this->
trigger(5,
"unexpected ','",
",");
306 $stack->push($stack->pop() + 1);
309 $expecting_op =
false;
311 } elseif ($op ==
'(' and !$expecting_op) {
316 } elseif ($ex and !$expecting_op) {
317 $expecting_op =
true;
319 if (preg_match(
"/^([a-z]\w*)\($/", $val, $matches)) {
320 if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f)) {
324 $expecting_op =
false;
332 $index += strlen($val);
334 } elseif ($op ==
')') {
335 return $this->
trigger(5,
"unexpected ')'",
")");
336 } elseif (in_array($op, $ops) and !$expecting_op) {
337 return $this->
trigger(8,
"unexpected operator '$op'", $op);
339 return $this->
trigger(9,
"an unexpected error occured");
341 if ($index == strlen($expr)) {
342 if (in_array($op, $ops)) {
343 return $this->
trigger(10,
"operator '$op' lacks operand", $op);
348 while (substr($expr, $index, 1) ==
' ') {
352 while (!is_null($op = $stack->pop())) {
354 return $this->
trigger(11,
"expecting ')'",
")");
367 private function pfx($tokens, $vars = array())
369 if ($tokens ==
false)
374 foreach ($tokens as $token) {
377 if (in_array($token, array(
'+',
'-',
'*',
'/',
'^'))) {
378 if (is_null($op2 = $stack->pop()))
379 return $this->
trigger(12,
"internal error");
380 if (is_null($op1 = $stack->pop()))
381 return $this->
trigger(13,
"internal error");
384 $stack->push($op1 + $op2);
387 $stack->push($op1 - $op2);
390 $stack->push($op1 * $op2);
394 return $this->
trigger(14,
"division by zero");
395 $stack->push($op1 / $op2);
398 $stack->push(pow($op1, $op2));
402 } elseif ($token ==
"_") {
403 $stack->push(-1 * $stack->pop());
405 } elseif (preg_match(
"/^([a-z]\w*)\($/", $token, $matches)) {
407 if (in_array($fnn, $this->fb)) {
408 if (is_null($op1 = $stack->pop()))
409 return $this->
trigger(15,
"internal error");
410 $fnn = preg_replace(
"/^arc/",
"a", $fnn);
413 eval(
'$stack->push('.$fnn.
'($op1));');
414 } elseif (array_key_exists($fnn, $this->f)) {
417 for ($i = count($this->f[$fnn][
'args']) - 1; $i >= 0; $i--) {
418 if (is_null($args[$this->f[$fnn][
'args'][$i]] = $stack->pop()))
419 return $this->
trigger(16,
"internal error");
421 $stack->push($this->
pfx($this->f[$fnn][
'func'], $args));
425 if (is_numeric($token)) {
426 $stack->push($token);
427 } elseif (array_key_exists($token, $this->v)) {
428 $stack->push($this->v[$token]);
429 } elseif (array_key_exists($token, $vars)) {
430 $stack->push($vars[$token]);
432 return $this->
trigger(17,
"undefined variable '$token'", $token);
437 if ($stack->count != 1)
438 return $this->
trigger(18,
"internal error");
439 return $stack->pop();
450 public function trigger($code, $msg, $info = null)
452 $this->last_error = $msg;
453 $this->last_error_code = array($code, $info);
454 if (!$this->suppress_errors)
455 trigger_error($msg, E_USER_WARNING);
466 public $stack = array();
478 $this->stack[$this->count] = $val;
489 if ($this->count > 0) {
491 return $this->stack[$this->count];
504 if (isset($this->stack[$this->count - $n])) {
505 return $this->stack[$this->count - $n];
trigger($code, $msg, $info=null)
trigger an error, but nicely, if need be
nfx($expr)
Convert infix to postfix notation.
__construct()
Constructor.
pfx($tokens, $vars=array())
evaluate postfix notation