94 public array
$v = array(
'e' => 2.71,
'pi' => 3.14);
96 public array
$vb = array(
'e',
'pi');
97 public array
$fb = array(
98 'sin',
'sinh',
'arcsin',
'asin',
'arcsinh',
'asinh',
99 'cos',
'cosh',
'arccos',
'acos',
'arccosh',
'acosh',
100 'tan',
'tanh',
'arctan',
'atan',
'arctanh',
'atanh',
101 'sqrt',
'abs',
'ln',
'log');
106 $this->v[
'pi'] = pi();
107 $this->v[
'exp'] = exp(1);
108 $this->v[
'e'] = exp(1);
112 public function e(
string $expr)
120 $expr = preg_replace_callback(
121 "/(\\d{0,1})e(-{0,1}\\d+)/is",
122 fn ($hit):
string => $hit[1] . ((strlen($hit[1])) ?
'*' :
'') .
'10^(' . $hit[2] .
')',
126 $this->last_error =
null;
128 if (substr($expr, -1, 1) ==
';') {
129 $expr = substr($expr, 0, strlen($expr) - 1);
133 if (preg_match(
'/^\s*([a-z]\w*)\s*=\s*(.+)$/', $expr, $matches)) {
134 if (in_array($matches[1], $this->vb)) {
135 return $this->
trigger(
"cannot assign to constant '$matches[1]'");
137 if (($tmp = $this->
pfx($this->
nfx($matches[2]))) ===
false) {
140 $this->v[$matches[1]] = $tmp;
141 return $this->v[$matches[1]];
144 } elseif (preg_match(
'/^\s*([a-z]\w*)\s*\(\s*([a-z]\w*(?:\s*,\s*[a-z]\w*)*)\s*\)\s*=\s*(.+)$/', $expr, $matches)) {
146 if (in_array($matches[1], $this->fb)) {
147 return $this->
trigger(
"cannot redefine built-in function '$matches[1]()'");
149 $args = explode(
",", preg_replace(
"/\s+/",
"", $matches[2]));
150 if (($stack = $this->
nfx($matches[3])) ===
false) {
153 for ($i = 0; $i < count($stack); $i++) {
155 if (preg_match(
'/^[a-z]\w*$/',
$token) and !in_array(
$token, $args)) {
156 if (array_key_exists(
$token, $this->v)) {
157 $stack[$i] = $this->v[
$token];
159 return $this->
trigger(
"undefined variable '$token' in function definition");
163 $this->f[$fnn] = array(
'args' => $args,
'func' => $stack);
166 return $this->
pfx($this->
nfx($expr));
173 unset($output[
'pi']);
184 foreach ($this->f as $fnn => $dat) {
185 $output[] = $fnn .
'(' . implode(
',', $dat[
'args']) .
')';
190 public function nfx($expr)
195 $expr = trim(strtolower($expr));
197 $ops = array(
'+',
'-',
'*',
'/',
'^',
'_');
198 $ops_r = array(
'+' => 0,
'-' => 0,
'*' => 0,
'/' => 0,
'^' => 1);
199 $ops_p = array(
'+' => 0,
'-' => 0,
'*' => 1,
'/' => 1,
'_' => 1,
'^' => 2);
201 $expecting_op =
false;
204 if (preg_match(
"/[^\w\s+*^\/()\.,-]/", $expr, $matches)) {
205 return $this->
trigger(
"illegal character '{$matches[0]}'");
209 $op = substr($expr, $index, 1);
211 $ex = preg_match(
'/^([01]+[bB]|[\da-fA-F]+[hH]|[a-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/', substr($expr, $index), $match);
213 if ($op ==
'-' and !$expecting_op) {
216 } elseif ($op ==
'_') {
217 return $this->
trigger(
"illegal character '_'");
219 } elseif ((in_array($op, $ops) or $ex) and $expecting_op) {
225 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])) {
226 $output[] = $stack->pop();
231 $expecting_op =
false;
233 } elseif ($op ==
')' and $expecting_op) {
234 while (($o2 = $stack->pop()) !=
'(') {
236 return $this->
trigger(
"unexpected ')'");
241 if (preg_match(
"/^([a-z]\w*)\($/", (
string) $stack->last(2), $matches)) {
243 $arg_count = $stack->pop();
244 $output[] = $stack->pop();
245 if (in_array($fnn, $this->fb)) {
246 if ($arg_count > 1) {
247 return $this->
trigger(
"too many arguments ($arg_count given, 1 expected)");
249 } elseif (array_key_exists($fnn, $this->f)) {
250 if ($arg_count != count($this->f[$fnn][
'args'])) {
251 return $this->
trigger(
"wrong number of arguments ($arg_count given, " . count($this->f[$fnn][
'args']) .
" expected)");
254 return $this->
trigger(
"internal error");
259 } elseif ($op ==
',' and $expecting_op) {
260 while (($o2 = $stack->pop()) !=
'(') {
262 return $this->
trigger(
"unexpected ','");
269 if (!preg_match(
"/^([a-z]\w*)\($/", $stack->last(2), $matches)) {
270 return $this->
trigger(
"unexpected ','");
272 $stack->push($stack->pop() + 1);
275 $expecting_op =
false;
277 } elseif ($op ==
'(' and !$expecting_op) {
282 } elseif ($ex and !$expecting_op) {
283 $expecting_op =
true;
285 if (preg_match(
"/^([a-z]\w*)\($/", $val, $matches)) {
286 if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f)) {
290 $expecting_op =
false;
298 $index += strlen($val);
300 } elseif ($op ==
')') {
301 return $this->
trigger(
"unexpected ')'");
302 } elseif (in_array($op, $ops) and !$expecting_op) {
303 return $this->
trigger(
"unexpected operator '$op'");
305 return $this->
trigger(
"an unexpected error occured");
307 if ($index == strlen($expr)) {
308 if (in_array($op, $ops)) {
309 return $this->
trigger(
"operator '$op' lacks operand");
314 while (substr($expr, $index, 1) ==
' ') {
318 while (!is_null($op = $stack->pop())) {
320 return $this->
trigger(
"expecting ')'");
328 public function pfx($tokens, $vars = [])
330 if ($tokens ==
false) {
336 foreach ($tokens as
$token) {
338 if (in_array($token, array(
'+',
'-',
'*',
'/',
'^'))) {
339 if (is_null($op2 = $stack->pop())) {
340 return $this->
trigger(
"internal error");
342 if (is_null($op1 = $stack->pop())) {
343 return $this->
trigger(
"internal error");
357 return $this->
trigger(
"division by zero");
366 } elseif ($token ==
"_") {
367 $stack->push(-1 * $stack->pop());
369 } elseif (preg_match(
"/^([a-z]\w*)\($/", $token, $matches)) {
371 if (in_array($fnn, $this->fb)) {
372 if (is_null($op1 = $stack->pop())) {
373 return $this->
trigger(
"internal error");
375 $fnn = preg_replace(
"/^arc/",
"a", $fnn);
378 } elseif ($fnn ==
'ln') {
382 $stack->push($fnn($op1));
383 } elseif (array_key_exists($fnn, $this->f)) {
386 for ($i = count($this->f[$fnn][
'args']) - 1; $i >= 0; $i--) {
387 if (is_null($args[$this->f[$fnn][
'args'][$i]] = $stack->pop())) {
388 return $this->
trigger(
"internal error");
391 $stack->push($this->
pfx($this->f[$fnn][
'func'], $args));
395 if (is_numeric($token)) {
396 $stack->push($token);
397 } elseif (($hex = $this->
from_hexbin($token)) !==
false) {
399 } elseif (array_key_exists($token, $this->v)) {
400 $stack->push($this->v[$token]);
401 } elseif (array_key_exists($token, $vars)) {
402 $stack->push($vars[$token]);
404 return $this->
trigger(
"undefined variable '$token'");
409 if ($stack->count != 1) {
410 return $this->
trigger(
"internal error");
412 return $stack->pop();
418 $this->last_error = $msg;
419 if (!$this->suppress_errors) {
420 trigger_error($msg, E_USER_WARNING);
429 if (strtoupper(substr(
$token, -1, 1)) ==
'H') {
432 if (strtoupper(substr(
$token, -1, 1)) ==
'B') {
455 public array $stack = [];
456 public int $count = 0;
458 public function push($val): void
460 $this->stack[$this->count] = $val;
466 if ($this->count > 0) {
468 return $this->stack[$this->count];
475 if (isset($this->stack[$this->count - $n])) {
476 return $this->stack[$this->count - $n];
static _add($left_operand, $right_operand, int $scale=50)
static _div($left_operand, $right_operand, int $scale=50)
static _pow($left_operand, $right_operand, int $scale=50)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
static _sub($left_operand, $right_operand, int $scale=50)
static _mul($left_operand, $right_operand, int $scale=50)