110 public array
$v = array(
'e' => 2.71,
'pi' => 3.14);
111 public array
$f = [];
112 public array
$vb = array(
'e',
'pi');
114 'sin',
'sinh',
'arcsin',
'asin',
'arcsinh',
'asinh',
115 'cos',
'cosh',
'arccos',
'acos',
'arccosh',
'acosh',
116 'tan',
'tanh',
'arctan',
'atan',
'arctanh',
'atanh',
117 'sqrt',
'abs',
'ln',
'log');
122 $this->v[
'pi'] = pi();
123 $this->v[
'exp'] = exp(1);
124 $this->v[
'e'] = exp(1);
128 public function e(
string $expr)
136 $expr = preg_replace_callback(
137 "/(\\d{0,1})e([+-]{0,1}\\d+)/is",
138 fn($hit):
string => $hit[1] . ((strlen($hit[1])) ?
'*' :
'') .
'10^(' . ltrim($hit[2],
'+') .
')',
142 $this->last_error =
null;
144 if (substr($expr, -1, 1) ==
';') {
145 $expr = substr($expr, 0, strlen($expr) - 1);
149 if (preg_match(
'/^\s*([a-z]\w*)\s*=\s*(.+)$/', $expr, $matches)) {
150 if (in_array($matches[1], $this->vb)) {
151 return $this->
trigger(
"cannot assign to constant '$matches[1]'");
153 if (($tmp = $this->
pfx($this->
nfx($matches[2]))) ===
false) {
156 $this->v[$matches[1]] = $tmp;
157 return $this->v[$matches[1]];
160 } elseif (preg_match(
'/^\s*([a-z]\w*)\s*\(\s*([a-z]\w*(?:\s*,\s*[a-z]\w*)*)\s*\)\s*=\s*(.+)$/', $expr, $matches)) {
162 if (in_array($matches[1], $this->fb)) {
163 return $this->
trigger(
"cannot redefine built-in function '$matches[1]()'");
165 $args = explode(
",", preg_replace(
"/\s+/",
"", $matches[2]));
166 if (($stack = $this->
nfx($matches[3])) ===
false) {
169 for ($i = 0; $i < count($stack); $i++) {
171 if (preg_match(
'/^[a-z]\w*$/',
$token) and !in_array(
$token, $args)) {
172 if (array_key_exists(
$token, $this->v)) {
173 $stack[$i] = $this->v[
$token];
175 return $this->
trigger(
"undefined variable '$token' in function definition");
179 $this->f[$fnn] = array(
'args' => $args,
'func' => $stack);
182 return $this->
pfx($this->
nfx($expr));
189 unset($output[
'pi']);
200 foreach ($this->f as $fnn => $dat) {
201 $output[] = $fnn .
'(' . implode(
',', $dat[
'args']) .
')';
206 public function nfx($expr)
211 $expr = trim(strtolower($expr));
213 $ops = array(
'+',
'-',
'*',
'/',
'^',
'_');
214 $ops_r = array(
'+' => 0,
'-' => 0,
'*' => 0,
'/' => 0,
'^' => 1);
215 $ops_p = array(
'+' => 0,
'-' => 0,
'*' => 1,
'/' => 1,
'_' => 1,
'^' => 2);
217 $expecting_op =
false;
220 if (preg_match(
"/[^\w\s+*^\/()\.,-]/", $expr, $matches)) {
221 return $this->
trigger(
"illegal character '{$matches[0]}'");
225 $op = substr($expr, $index, 1);
227 $ex = preg_match(
'/^([01]+[bB]|[\da-fA-F]+[hH]|[a-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/', substr($expr, $index), $match);
229 if ($op ==
'-' and !$expecting_op) {
232 } elseif ($op ==
'_') {
233 return $this->
trigger(
"illegal character '_'");
235 } elseif ((in_array($op, $ops) or $ex) and $expecting_op) {
241 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])) {
242 $output[] = $stack->pop();
247 $expecting_op =
false;
249 } elseif ($op ==
')' and $expecting_op) {
250 while (($o2 = $stack->pop()) !=
'(') {
252 return $this->
trigger(
"unexpected ')'");
257 if (preg_match(
"/^([a-z]\w*)\($/", (
string) $stack->last(2), $matches)) {
259 $arg_count = $stack->pop();
260 $output[] = $stack->pop();
261 if (in_array($fnn, $this->fb)) {
262 if ($arg_count > 1) {
263 return $this->
trigger(
"too many arguments ($arg_count given, 1 expected)");
265 } elseif (array_key_exists($fnn, $this->f)) {
266 if ($arg_count != count($this->f[$fnn][
'args'])) {
267 return $this->
trigger(
"wrong number of arguments ($arg_count given, " . count($this->f[$fnn][
'args']) .
" expected)");
270 return $this->
trigger(
"internal error");
275 } elseif ($op ==
',' and $expecting_op) {
276 while (($o2 = $stack->pop()) !=
'(') {
278 return $this->
trigger(
"unexpected ','");
285 if (!preg_match(
"/^([a-z]\w*)\($/", $stack->last(2), $matches)) {
286 return $this->
trigger(
"unexpected ','");
288 $stack->push($stack->pop() + 1);
291 $expecting_op =
false;
293 } elseif ($op ==
'(' and !$expecting_op) {
298 } elseif ($ex and !$expecting_op) {
299 $expecting_op =
true;
301 if (preg_match(
"/^([a-z]\w*)\($/", $val, $matches)) {
302 if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f)) {
306 $expecting_op =
false;
314 $index += strlen($val);
316 } elseif ($op ==
')') {
317 return $this->
trigger(
"unexpected ')'");
318 } elseif (in_array($op, $ops) and !$expecting_op) {
319 return $this->
trigger(
"unexpected operator '$op'");
321 return $this->
trigger(
"an unexpected error occured");
323 if ($index == strlen($expr)) {
324 if (in_array($op, $ops)) {
325 return $this->
trigger(
"operator '$op' lacks operand");
330 while (substr($expr, $index, 1) ==
' ') {
334 while (!is_null($op = $stack->pop())) {
336 return $this->
trigger(
"expecting ')'");
344 public function pfx($tokens, $vars = [])
346 if ($tokens ==
false) {
352 foreach ($tokens as
$token) {
354 if (in_array(
$token, array(
'+',
'-',
'*',
'/',
'^'))) {
355 if (is_null($op2 = $stack->pop())) {
356 return $this->
trigger(
"internal error");
358 if (is_null($op1 = $stack->pop())) {
359 return $this->
trigger(
"internal error");
373 return $this->
trigger(
"division by zero");
382 } elseif (
$token ==
"_") {
383 $stack->push(-1 * $stack->pop());
385 } elseif (preg_match(
"/^([a-z]\w*)\($/",
$token, $matches)) {
387 if (in_array($fnn, $this->fb)) {
388 if (is_null($op1 = $stack->pop())) {
389 return $this->
trigger(
"internal error");
391 $fnn = preg_replace(
"/^arc/",
"a", $fnn);
394 } elseif ($fnn ==
'ln') {
398 $stack->push($fnn($op1));
399 } elseif (array_key_exists($fnn, $this->f)) {
402 for ($i = count($this->f[$fnn][
'args']) - 1; $i >= 0; $i--) {
403 if (is_null($args[$this->f[$fnn][
'args'][$i]] = $stack->pop())) {
404 return $this->
trigger(
"internal error");
407 $stack->push($this->
pfx($this->f[$fnn][
'func'], $args));
413 } elseif (($hex = $this->
from_hexbin($token)) !==
false) {
415 } elseif (array_key_exists(
$token, $this->v)) {
416 $stack->push($this->v[
$token]);
417 } elseif (array_key_exists(
$token, $vars)) {
418 $stack->push($vars[
$token]);
420 return $this->
trigger(
"undefined variable '$token'");
425 if ($stack->count != 1) {
426 return $this->
trigger(
"internal error");
428 return $stack->pop();
434 $this->last_error = $msg;
435 if (!$this->suppress_errors) {
436 trigger_error($msg, E_USER_WARNING);
445 if (strtoupper(substr(
$token, -1, 1)) ==
'H') {
448 if (strtoupper(substr(
$token, -1, 1)) ==
'B') {
474 public function push($val): void
482 if ($this->count > 0) {
491 if (isset($this->stack[$this->count - $n])) {
492 return $this->stack[$this->count - $n];
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _sub($left_operand, $right_operand, int $scale=50)
static _pow($left_operand, $right_operand, int $scale=50)
static _add($left_operand, $right_operand, int $scale=50)
static _div($left_operand, $right_operand, int $scale=50)
static _mul($left_operand, $right_operand, int $scale=50)