177 $this->last_error =
null;
178 $this->last_error_code =
null;
180 if (substr($expr, - 1, 1) ==
';') {
181 $expr = substr($expr, 0, strlen($expr) - 1);
186 if (preg_match(
'/^\s*([a-z]\w*)\s*=\s*(.+)$/', $expr, $matches)) {
187 if (in_array($matches[1], $this->vb)) {
188 return $this->
trigger(1,
"cannot assign to constant '$matches[1]'", $matches[1]);
190 if (($tmp = $this->
pfx($this->
nfx($matches[2]))) ===
false) {
193 $this->v[$matches[1]] = $tmp;
194 return $this->v[$matches[1]];
197 } elseif (preg_match(
'/^\s*([a-z]\w*)\s*\(\s*([a-z]\w*(?:\s*,\s*[a-z]\w*)*)\s*\)\s*=\s*(.+)$/', $expr, $matches)) {
199 if (in_array($matches[1], $this->fb)) {
200 return $this->
trigger(2,
"cannot redefine built-in function '$matches[1]()'", $matches[1]);
202 $args = explode(
",", preg_replace(
"/\s+/",
"", $matches[2]));
203 if (($stack = $this->
nfx($matches[3])) ===
false) {
206 $nbstack = count($stack);
207 for ($i = 0; $i < $nbstack; $i++) {
209 if (preg_match(
'/^[a-z]\w*$/', $token) and !in_array($token, $args)) {
210 if (array_key_exists($token, $this->v)) {
211 $stack[$i] = $this->v[$token];
213 return $this->
trigger(3,
"undefined variable '$token' in function definition", $token);
217 $this->f[$fnn] = array(
'args' => $args,
'func' => $stack);
221 return $this->
pfx($this->
nfx($expr));
260 private function nfx($expr)
265 $expr = trim(strtolower($expr));
267 $ops = array(
'+',
'-',
'*',
'/',
'^',
'_');
268 $ops_r = array(
'+' => 0,
'-' => 0,
'*' => 0,
'/' => 0,
'^' => 1);
269 $ops_p = array(
'+' => 0,
'-' => 0,
'*' => 1,
'/' => 1,
'_' => 1,
'^' => 2);
271 $expecting_op =
false;
275 if (preg_match(
"/[^\w\s+*^\/()\.,-]/", $expr, $matches)) {
276 return $this->
trigger(4,
"illegal character '".$matches[0].
"'", $matches[0]);
280 $op = substr($expr, $index, 1);
283 $ex = preg_match(
'/^([a-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/', substr($expr, $index), $match);
285 if ($op ==
'-' and !$expecting_op) {
288 } elseif ($op ==
'_') {
289 return $this->
trigger(4,
"illegal character '_'",
"_");
291 } elseif ((in_array($op, $ops) or $ex) and $expecting_op) {
297 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])) {
298 $output[] = $stack->pop();
303 $expecting_op =
false;
305 } elseif ($op ==
')' and $expecting_op) {
306 while (($o2 = $stack->pop()) !=
'(') {
308 return $this->
trigger(5,
"unexpected ')'",
")");
313 if (preg_match(
"/^([a-z]\w*)\($/", $stack->last(2), $matches)) {
315 $arg_count = $stack->pop();
316 $output[] = $stack->pop();
317 if (in_array($fnn, $this->fb)) {
318 if ($arg_count > 1) {
319 return $this->
trigger(6,
"wrong number of arguments ($arg_count given, 1 expected)", array($arg_count, 1));
321 } elseif (array_key_exists($fnn, $this->f)) {
322 if ($arg_count != count($this->f[$fnn][
'args'])) {
323 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'])));
326 return $this->
trigger(7,
"internal error");
331 } elseif ($op ==
',' and $expecting_op) {
332 while (($o2 = $stack->pop()) !=
'(') {
334 return $this->
trigger(5,
"unexpected ','",
",");
340 if (!preg_match(
"/^([a-z]\w*)\($/", $stack->last(2), $matches)) {
341 return $this->
trigger(5,
"unexpected ','",
",");
343 $stack->push((
string) ($stack->pop() + 1));
346 $expecting_op =
false;
348 } elseif ($op ==
'(' and !$expecting_op) {
353 } elseif ($ex and !$expecting_op) {
354 $expecting_op =
true;
356 if (preg_match(
"/^([a-z]\w*)\($/", $val, $matches)) {
357 if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f)) {
361 $expecting_op =
false;
369 $index += strlen($val);
371 } elseif ($op ==
')') {
372 return $this->
trigger(5,
"unexpected ')'",
")");
373 } elseif (in_array($op, $ops) and !$expecting_op) {
374 return $this->
trigger(8,
"unexpected operator '$op'", $op);
376 return $this->
trigger(9,
"an unexpected error occurred");
378 if ($index == strlen($expr)) {
379 if (in_array($op, $ops)) {
380 return $this->
trigger(10,
"operator '$op' lacks operand", $op);
385 while (substr($expr, $index, 1) ==
' ') {
389 while (!is_null($ope = $stack->pop())) {
391 return $this->
trigger(11,
"expecting ')'",
")");
406 private function pfx($tokens, $vars = array())
410 foreach ($tokens as $token) {
413 if (in_array($token, array(
'+',
'-',
'*',
'/',
'^'))) {
414 if (is_null($op2 = $stack->pop())) {
415 return $this->
trigger(12,
"internal error");
417 if (is_null($op1 = $stack->pop())) {
418 return $this->
trigger(13,
"internal error");
422 $stack->push((
string) ($op1 + $op2));
425 $stack->push((
string) ($op1 - $op2));
428 $stack->push((
string) ($op1 * $op2));
432 return $this->
trigger(14,
"division by zero");
434 $stack->push((
string) ($op1 / $op2));
437 $stack->push((
string) pow($op1, $op2));
441 } elseif ($token ==
"_") {
442 $stack->push((
string) (-1 * $stack->pop()));
444 } elseif (preg_match(
"/^([a-z]\w*)\($/", $token, $matches)) {
446 if (in_array($fnn, $this->fb)) {
447 if (is_null($op1 = $stack->pop())) {
448 return $this->
trigger(15,
"internal error");
450 $fnn = preg_replace(
"/^arc/",
"a", $fnn);
455 eval(
'$stack->push('.$fnn.
'($op1));');
456 } elseif (array_key_exists($fnn, $this->f)) {
459 for ($i = count($this->f[$fnn][
'args']) - 1; $i >= 0; $i--) {
460 if (is_null($args[$this->f[$fnn][
'args'][$i]] = $stack->pop())) {
461 return $this->
trigger(16,
"internal error");
464 $stack->push($this->
pfx($this->f[$fnn][
'func'], $args));
468 if (is_numeric($token)) {
469 $stack->push($token);
470 } elseif (array_key_exists($token, $this->v)) {
471 $stack->push($this->v[$token]);
472 } elseif (array_key_exists($token, $vars)) {
473 $stack->push($vars[$token]);
475 return $this->
trigger(17,
"undefined variable '$token'", $token);
480 if ($stack->count != 1) {
481 return $this->
trigger(18,
"internal error");
483 return $stack->pop();
494 public function trigger($code, $msg, $info =
null)
496 $this->last_error = $msg;
497 $this->last_error_code = array($code, $info);
498 if (!$this->suppress_errors) {
499 trigger_error($msg, E_USER_WARNING);