class
EvalMath {
var
$suppress_errors
= false;
var
$last_error
= null;
var
$v
=
array
(
'e'
=>2.71,
'pi'
=>3.14);
var
$f
=
array
();
var
$vb
=
array
(
'e'
,
'pi'
);
var
$fb
=
array
(
'sin'
,
'sinh'
,
'arcsin'
,
'asin'
,
'arcsinh'
,
'asinh'
,
'cos'
,
'cosh'
,
'arccos'
,
'acos'
,
'arccosh'
,
'acosh'
,
'tan'
,
'tanh'
,
'arctan'
,
'atan'
,
'arctanh'
,
'atanh'
,
'sqrt'
,
'abs'
,
'ln'
,
'log'
);
function
EvalMath() {
$this
->v[
'pi'
] = pi();
$this
->v[
'e'
] =
exp
(1);
}
function
e(
$expr
) {
return
$this
->evaluate(
$expr
);
}
function
evaluate(
$expr
) {
$this
->last_error = null;
$expr
= trim(
$expr
);
if
(
substr
(
$expr
, -1, 1) ==
';'
)
$expr
=
substr
(
$expr
, 0,
strlen
(
$expr
)-1);
if
(preg_match(
'/^\s*([a-z]\w*)\s*=\s*(.+)$/'
,
$expr
,
$matches
)) {
if
(in_array(
$matches
[1],
$this
->vb)) {
return
$this
->trigger(
"cannot assign to constant '$matches[1]'"
);
}
if
((
$tmp
=
$this
->pfx(
$this
->nfx(
$matches
[2]))) === false)
return
false;
$this
->v[
$matches
[1]] =
$tmp
;
return
$this
->v[
$matches
[1]];
}
elseif
(preg_match(
'/^\s*([a-z]\w*)\s*\(\s*([a-z]\w*(?:\s*,\s*[a-z]\w*)*)\s*\)\s*=\s*(.+)$/'
,
$expr
,
$matches
)) {
$fnn
=
$matches
[1];
if
(in_array(
$matches
[1],
$this
->fb)) {
return
$this
->trigger(
"cannot redefine built-in function '$matches[1]()'"
);
}
$args
=
explode
(
","
, preg_replace(
"/\s+/"
,
""
,
$matches
[2]));
if
((
$stack
=
$this
->nfx(
$matches
[3])) === false)
return
false;
for
(
$i
= 0;
$i
<
count
(
$stack
);
$i
++) {
$token
=
$stack
[
$i
];
if
(preg_match(
'/^[a-z]\w*$/'
,
$token
)
and
!in_array(
$token
,
$args
)) {
if
(
array_key_exists
(
$token
,
$this
->v)) {
$stack
[
$i
] =
$this
->v[
$token
];
}
else
{
return
$this
->trigger(
"undefined variable '$token' in function definition"
);
}
}
}
$this
->f[
$fnn
] =
array
(
'args'
=>
$args
,
'func'
=>
$stack
);
return
true;
}
else
{
return
$this
->pfx(
$this
->nfx(
$expr
));
}
}
function
vars() {
$output
=
$this
->v;
unset(
$output
[
'pi'
]);
unset(
$output
[
'e'
]);
return
$output
;
}
function
funcs() {
$output
=
array
();
foreach
(
$this
->f
as
$fnn
=>
$dat
)
$output
[] =
$fnn
.
'('
. implode(
','
,
$dat
[
'args'
]) .
')'
;
return
$output
;
}
function
nfx(
$expr
) {
$index
= 0;
$stack
=
new
EvalMathStack;
$output
=
array
();
$expr
= trim(
strtolower
(
$expr
));
$ops
=
array
(
'+'
,
'-'
,
'*'
,
'/'
,
'^'
,
'_'
);
$ops_r
=
array
(
'+'
=>0,
'-'
=>0,
'*'
=>0,
'/'
=>0,
'^'
=>1);
$ops_p
=
array
(
'+'
=>0,
'-'
=>0,
'*'
=>1,
'/'
=>1,
'_'
=>1,
'^'
=>2);
$expecting_op
= false;
if
(preg_match(
"/[^\w\s+*^\/()\.,-]/"
,
$expr
,
$matches
)) {
return
$this
->trigger(
"illegal character '{$matches[0]}'"
);
}
while
(1) {
$op
=
substr
(
$expr
,
$index
, 1);
$ex
= preg_match(
'/^([a-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/'
,
substr
(
$expr
,
$index
),
$match
);
if
(
$op
==
'-'
and
!
$expecting_op
) {
$stack
->push(
'_'
);
$index
++;
}
elseif
(
$op
==
'_'
) {
return
$this
->trigger(
"illegal character '_'"
);
}
elseif
((in_array(
$op
,
$ops
)
or
$ex
)
and
$expecting_op
) {
if
(
$ex
) {
$op
=
'*'
;
$index
--;
}
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
])) {
$output
[] =
$stack
->pop();
}
$stack
->push(
$op
);
$index
++;
$expecting_op
= false;
}
elseif
(
$op
==
')'
and
$expecting_op
) {
while
((
$o2
=
$stack
->pop()) !=
'('
) {
if
(
is_null
(
$o2
))
return
$this
->trigger(
"unexpected ')'"
);
else
$output
[] =
$o2
;
}