http://muparser.sourceforge.net/
if you want to convert the code. fairly complex. better make this one a DLL

extension of PHP.

"Pedro Graca" <he****@dodgeit.com> wrote in message

news:sl*******************@ID-203069.user.individual.net...

werner wrote: I agree with the parser suggestion and have also

previously searched for an expression parser, as this would be the best

approach.

I am also looking at maybe porting an existing Java solution, what do

you think? It's just going to take some time, and I sadly don't have

much of that left.

Use bc! :-)

http://us2.php.net/manual/en/ref.bc.php (does not have eval-like function)

instead of using backticks, use the library?

http://www.bestcode.com/html/bcparserx.html COM control that evals math,

co$t$ money.

http://www.freevbcode.com/ShowCode.asp?ID=2090 vree vb parser, not too

complicated. I am converting it now. here.

<?php

//Attribute VB_Name = "Module1"

/*

' Acclaimer and Disclaimer!

'************************************************* ********************************************

'* This code is written by Jonathan Adamit (c) 20th in December

2000. *

'* I Am in no way responsible for anything that occures as a result of

implementing *

'* this code or by using it in any way.

*

'* This code is given to you for free to do whatever you want to do with

it. *

'* My only request is that if you use it - you give me the credit for it.

*

'* I will also be very greatful if you E-mail me any improvements or

changes you made to it.*

'* Any comments or changes are to be sent to:

ad****@bgumail.bgu.ac.il *

'************************************************* ********************************************

version 1.0

'

' Example of use and remarks!

'************************************************* ************************************************** **************

'* On Error Resume Next

*

'* Dim X As Double

*

'* X = Parse("2+3*4^2*2*cos[3]+2")

*

'* if Err.Number = 11 Then MsgBox "Division by Zero", , "Uncalculatable"

*

'* if Err.Number = vbObjectError + 555 Then MsgBox "Specified Text Sequence

Not Allowed!", , "Invalid Equation" *

'*---------------------------------------------------------------------------------------------------------------*

'* cos/sin/tan/atn must be enclosed with [] as the example shows.

*

'* Use of absolute signs (|) is allowed.

*

'************************************************* ************************************************** **************

*/

function RemSpaces($Exp) {

return str_replace(" ","",$Exp);

}

function RemPlusMinus($Exp) {

$NewExp="";

$RPM = $Exp;

$Place = strpos($Exp, "+-");

if ($Place === false) { } else {

$NewExp = substr($Exp, 0, ($Place - 1)+1) . "-" . substr($Exp, -1,

(strlen($Exp) - $Place - 1)+1);

if (strpos($NewExp, "+-") === false) { } else {

$NewExp = RemPlusMinus($NewExp);

}

$RPM = $NewExp;

}

$Place = strpos($NewExp, "--");

if ($Place === false) { } else {

$NewExp = substr($NewExp, 0, ($Place - 1)+1) . "+" . substr($NewExp, -1,

(strlen($NewExp) - $Place - 1)+1);

if (strpos($NewExp, "--") === false) { } else {

$NewExp = RemPlusMinus($NewExp);

}

return $NewExp;

}

return $RPM;

}

function isnumeric($x) {

return 1==preg_match("/[\deE\.]/", $x);

}

function IsValid($Exp) {

for ($X = 0; $X < strlen($Exp); $X++) {

switch ($Exp{$X}) {

case "[": case "]": case "(": case ")": case "*": case "/": case "+": case

"-": case "^":

break;

case "|":

if (substr($Exp, $X, 2) == "||") {

echo "mdlParse: Specified Text Sequence Not Allowed";

return false;

}

break;

default:

switch(isnumeric($Exp{$X})) {

case false:

switch($X) {

case 0://1:

$Temp = substr($Exp, $X, 3);

if (($Temp != "tan") && ($Temp != "cos") && ($Temp != "sin") && ($Temp

!= "atn")) {

echo "mdlParse: Specified Text Sequence Not Allowed";

return false;

}

break;

case 1://2:

$Temp = substr($Exp, $X - 1, 3);

if (($Temp != "tan") && ($Temp != "cos") && ($Temp != "sin") && ($Temp

!= "atn")) {

echo "mdlParse: Specified Text Sequence Not Allowed";

return false;

}

break;

default://case Is > 2

$Temp = substr($Exp, $X - 2, 6);

if ((strpos($Temp, "cos[") === false) && (strpos($Temp, "sin[") ===

false) && (strpos($Temp, "tan[") === false) && (strpos($Temp, "atn[") ===

false)) {

echo "mdlParse: Specified Text Sequence Not Allowed";

return false;

}

break;

}

break;

case true:

$Temp = substr($Exp, $X, 2);

//if ((strpos($Temp, "[") >= 0) || (strpos($Temp, "(") >= 0) ||

(strpos($Temp, "s") >= 0) || (strpos($Temp, "c") >= 0) || (strpos($Temp,

"t") >= 0) || (strpos($Temp, "a") >= 0)) {

if ((strpos($Temp, "[") === false) && (strpos($Temp, "(") === false) &&

(strpos($Temp, "s") === false) && (strpos($Temp, "c") === false) &&

(strpos($Temp, "t") === false) && (strpos($Temp, "a") === false)) { } else {

echo "mdlParse: Specified Text Sequence Not Allowed";

return false;

}

break;

} //end switch $X

//Ok:

} //end switch $Exp{$X}

} //end for

return true;

}

function Mul($Exp) {

//Dim Place As Integer //'Where the sign is found

//Dim X As Integer //'Travel backward and forward on the string

//Dim Y As Integer //'Counter

//Dim Before As String //'What number goes before the sign

//Dim After As String //'What number goes after the sign

//Dim NewExp As String

//Dim BeforeStr As String //'Used to save the text that goes before our

calculation

$Exp = RemPlusMinus($Exp); //'Make sure previous calculation didn't leave

+- or --

$M = $Exp; //'Incase there's nothing to do, make sure the

function returns correct information

$Place = strpos($Exp, "*");

if ($Place === false) { } else {

$Y = 0;

$X = $Place;

do {//'loop backwards on the string to find out the number before the sign

$Y++;

$X--;

if ($X < 0) break; //'incase we got to the start of the string

if (($Exp{$X} != "+") && ($Exp{$X} != "-" || $Y == 1) && ($Exp{$X} !=

"/")) {

$Before = substr($Exp, $X, $Y);

}

} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}

== "/"))); //'As Long as we didn't get to the start or to another sign -

continue traveling backwards.

$BeforeStr = substr($Exp, 0, $X+1);

$Y = 0;

$X = $Place;

do {

$Y++;

$X++;

if (($Exp{$X} != "+") && ($Exp{$X} != "-" || $Y == 1) && ($Exp{$X} !=

"*") && ($Exp{$X} != "/")) {

$After = substr($Exp, $Place + 1, $Y+1); //+1 or -1? I think it's +1.

}

} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}

== "*") || ($Exp{$X} == "/") || ($X >= strlen($Exp)-1))); //'As long as we

didn't get to the end or to another sign - continue traveling forward.

$NewExp = $BeforeStr . ((double)$Before * (double)$After) . substr($Exp,

$X+1);

if (strpos($NewExp, "*") === false) { } else {

$NewExp = Mul($NewExp); //'Recurse incase there are *'s left

}

return $NewExp;

} //end if

return $M;

}

function Add($Exp) {

$Exp = RemPlusMinus($Exp);

$A = $Exp;

$Place = strpos($Exp, "+");

if ($Place === false) { } else {

$Y = 0;

$X = $Place;

do { //'loop backwards on the string to find out the number before the

sign

$Y++;

$X--;

if ($X < 0) break; //'incase we got to the start of the string

if (($Exp{$X} != "-" || $X == 0) && ($Exp{$X} != "*") && ($Exp{$X} !=

"/")) {

$Before = substr($Exp, $X, $Y);

}

} while (!(($Exp{$X} == "*") Or ($Exp{$X} == "-" && $X != 0) || ($Exp{$X}

== "/")));

$BeforeStr = substr($Exp, 0, $X+1);

$Y = 0;

$X = $Place;

do {

$Y++;

$X++;

if (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&

($Exp{$X} != "/")) {

$After = substr($Exp, $Place + 1, $Y);

}

} while (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&

($Exp{$X} != "/") && ($X < strlen($Exp)-1));

$NewExp = $BeforeStr . ((double)$Before + (double)$After) . substr($Exp,

$X+1);

if (strpos($NewExp, "+") === false) { } else {

$NewExp = Add($NewExp); //'Recurse incase there are +'s left

}

return $NewExp;

} //end if

return $A;

}

function Subt($Exp) {

$Exp = RemPlusMinus($Exp);

$S = $Exp;

$Place = strpos($Exp, "-");

if ($Place === 0) $Place = strpos($Exp, "-", 1);

if ($Place > 0) {

$Y = 0;

$X = $Place;

do { //'loop backwards on the string to find out the number before the

sign

$Y++;

$X--;

if ($X < 0) break; //'incase we got to the start of the string

if (($Exp{$X} != "+") && ($Exp{$X} != "*") && ($Exp{$X} != "/")) {

$Before = substr($Exp, $X, $Y);

}

} while (!(($Exp{$X} == "*") || ($Exp{$X} == "+") || ($Exp{$X} == "/")));

$BeforeStr = substr($Exp, 0, $X+1); //+1 added afterwards by Jim

$Y = 0;

$X = $Place;

do {

$Y++;

$X++;

if (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&

($Exp{$X} != "/")) {

$After = substr($Exp, $Place + 1, $Y+1);

}

} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-") || ($Exp{$X} == "*") ||

($Exp{$X} = "/") || ($X >= strlen(Exp)-1)));

$NewExp = $BeforeStr . ((double)$Before - (double)$After) . substr($Exp,

$X+1);

if (strpos($NewExp, "-") === false) { } else {

$NewExp = Subt($NewExp); //'Recurse incase there are -'s left

}

return $NewExp;

}

return $S;

}

function Parentheses($Exp) {

$Exp = RemPlusMinus($Exp);

$P = $Exp;

$MiddleStr="";

$Place = strpos($Exp, ")");

if ($Place === false) { } else {

$Y = 0;

$X = $Place;

do { //'loop to find the inside of the parentheses

$Y++;

$X--;

if ($X < 0) break; //'incase we got to the start of the string

if ($Exp{$X} != "(") {

$MiddleStr = substr($Exp, $X, $Y);

}

} while ($Exp{$X} != "(");

$BeforeStr = ""; //'if it's the beginning of the string - there is nothing

before.

if ($X >= 0) $BeforeStr = substr($Exp, 0, ($X - 1)+1);

$NewExp = $BeforeStr . Parse($MiddleStr) . substr($Exp, $Place + 1);

if (strpos($Exp, ")") === false) { } else {

$NewExp = Parentheses($NewExp); //'Recurse incase there are more

parentheses

}

return $NewExp;

}

return $P;

}

function Div($Exp) {

//On Error Resume Next

$Exp = RemPlusMinus($Exp);

$D = $Exp;

$Place = strpos($Exp, "/");

if ($Place === false) { } else {

$Y = 0;

$X = $Place;

do {//'loop backwards on the string to find out the number before the sign

$Y++;

$X--;

if ($X < 0) break; //'incase we got to the start of the string

if (($Exp{$X} != "+") && ($Exp{$X} && "-" || $Y == 1) && ($Exp{$X} !=

"*")) {

$Before = substr($Exp, $X, $Y);

}

} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}

== "*")));

$BeforeStr = substr($Exp, 0, $X+1);

$Y = 0;

$X = $Place;

do {

$Y++;

$X++;

if (($Exp{$X} != "+") && ($Exp{$X} != "-" Or Y == 1) && ($Exp{$X} != "*")

&& ($Exp{$X} != "/")) {

$After = substr($Exp, $Place + 1, $Y+1);

}

} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && $Y != 1) || ($Exp{$X}

== "*") || ($Exp{$X} == "/") || ($X >= strlen($Exp)-1)));

if ((double)$After==0.0) {

return "Impossible";

}

$NewExp = $BeforeStr . ((double)$Before / (double)$After) . substr($Exp,

$X+1);

if (strpos($NewExp, "/") === false) { } else {

$NewExp = Div($NewExp); //'Recurse incase there are /'s left

}

return $NewExp;

}

return $D;

}

function Power($Exp) {

$Exp = RemPlusMinus($Exp);

$P = $Exp;

$Place = strpos($Exp, "^");

if ($Place === false) { } else {

$Y = 0;

$X = $Place;

do { //'loop backwards on the string to find out the number before the

sign

$Y++;

$X--;

if ($X < 0) break; //'incase we got to the start of the string

if (($Exp{$X} != "+") && ($Exp{$X} != "-" || X == 0) && ($Exp{$X} != "*")

&& ($Exp{$X} != "/")) {

$Before = substr($Exp, $X, $Y);

}

} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-" && X != 0) || ($Exp{$X}

== "*") || ($Exp{$X} == "/")));

$BeforeStr = substr($Exp, 0, $X+1);

$Y = 0;

$X = $Place;

do {

$Y++;

$X++;

if (($Exp{$X} != "+") && ($Exp{$X} != "-") && ($Exp{$X} != "*") &&

($Exp{$X} != "/")) {

$After = substr($Exp, $Place + 1, $Y+1);

}

} while (!(($Exp{$X} == "+") || ($Exp{$X} == "-") || ($Exp{$X} == "*") ||

($Exp{$X} == "/") || ($X >= strlen($Exp)-1)));

$NewExp = $BeforeStr . bcpow($Before, $After) . substr($Exp, $X+1);

if (strpos($NewExp, "^") === false) { } else {

$NewExp = Power($NewExp); //`'Recurse incase there are ^'s left

}

return $NewExp;

}

return $P;

}

function Abso($Exp) {

$A = $Exp;

$First = strpos($Exp, "|");

if ($First === false) return NULL;

$Second = strpos($Exp, "|", $First + 1);

if ($Second === false) return NULL;

$NewExp = substr($Exp, $First + 1, $Second - $First - 1);

$NewExp = Parse($NewExp);

return substr($Exp, 0, ($First - 1)+1) . abs((double)$NewExp) .

substr($Exp, $Second + 1);

}

function CSTA($Exp) {

//Dim X As Integer 'CSTA = Cos Sin Tan Atn

$MiddleStr="";

$Exp = RemPlusMinus($Exp);

$C = $Exp;

$Place = strpos($Exp, "]");

if ($Place === false) { } else {

$Y = 0;

$X = $Place;

do { //'loop to find the inside of the brackets

$Y++;

$X--;

if ($X < 0) break; //'incase we got to the start of the string

if ($Exp{$X} != "[") {

$MiddleStr = substr($Exp, $X, $Y);

}

} while ($Exp{$X} != "[");

$BeforeStr = ""; //'if it's the beginning of the string - there is nothing

before.

if ($X > 0) $BeforeStr = substr($Exp, 0, ($X - 4)+1);

if ($X > 0) $XType = substr($Exp, $X - 3, 3);

switch($XType) {

case "cos":

$NewExp = $BeforeStr . cos((double)Parse($MiddleStr)) . substr($Exp,

$Place + 1);

break;

case "sin":

$NewExp = $BeforeStr . sin((double)Parse($MiddleStr)) . substr($Exp,

$Place + 1);

break;

case "tan":

$NewExp = $BeforeStr . tan((double)Parse($MiddleStr)) . substr($Exp,

$Place + 1);

break;

case "atn":

$NewExp = $BeforeStr . atan((double)Parse($MiddleStr)) . substr($Exp,

$Place + 1);

break;

}

if (strpos($Exp, "]") === false) { } else {

$NewExp = CSTA($NewExp); // 'Recurse incase there are more brackets

}

return $NewExp;

}

return $C;

}

function Parse($Exp) {

/*'************************************************ **************************************** '*This is the main function. In Mul function you will find detailedexplanation. No need* '*to explain others because they are pretty much the same but with littlechanges. * '************************************************* ***************************************/ $Exp = RemSpaces($Exp); //'Remove Spaces $Exp = RemPlusMinus($Exp); //'Change +- to - and -- to + if (IsValid($Exp)) { //'Invalid equations trapping //'The if's are to make sure the code is run only if neccessary if (strpos($Exp, "[") === false) {} else { $Exp = CSTA($Exp);}//'Eliminate Cos/Sin/Tan/Atn from equation. if (strpos($Exp, "|") === false) { } else { $Exp = Abso($Exp);}//'Eliminate Absolute signs from equation. if (strpos($Exp, "(") === false) {} else { $Exp = Parentheses($Exp);}//'Eliminate Parentheses from equation. if (strpos($Exp, "^") === false) {} else { $Exp = Power($Exp);}//'Eliminate Exponentials from equation. if (strpos($Exp, "*") === false) {} else { $Exp = Mul($Exp);}//'Eliminate Multiplications from equation. if (strpos($Exp, "/")=== false) {} else { $Exp = Div($Exp);}//'Eliminate Divisions from equation. if ($Exp == "Impossible") { //'if Division byZero echo "Division by 0 error\n";//'let the calling procedure know what happened return NULL; } if (strpos($Exp, "+") === false) { } else { $Exp = Add($Exp);}//'Eliminate Addition from equation. if (strpos($Exp, "-", 1) === false) { } else { $Exp = Subt($Exp);}//'Eliminate Subtraction from equation. (why 1?) return (double)$Exp; } else { return NULL; }}//print Parse("|-2|-(4-2)*sin[-3.14]"); //chokes on this in the subtractionarea.//print Parse("2-2-2");//print Parse("|-2|-(4-2)");//print Parse("(4-2)*sin[-3.14]");?>>>>> There is bc under Unix. Although inserting user-supplied data into a>>> shell command might be even more dangerous.>> My example (below) looks safe. It could be made tighter by checking for> long lines, disabling single quotes and "strange" characters (ESC, NUL,> ...), and whatever else you might think of.>> ... but bc is, perhaps, best discussed on gnu.utils.help.>>> That is also an interesting approach, but I do feel that I would like>> to keep it a native php solution.>>> <?php> $formula = <<<FORMULA> /*> * find the hypotenuse of a right triangle> * with height = 5 and area = 25> */>> /* !!!! ATTENTION !!! */> rm -rf * ## I tried it!!> ## it seems perfectly safe (???)> /* !!! ATTENTION !!! */>> scale = 8 ## work with 8 decimals>> /* area = h * w / 2 */> h = 5 ## height> area = 25 ## area> w = 2*area / h ## compute width>> /* hypotenuse = sqrt(h*h + w*w) */> hyp = sqrt(h*h + w*w) ## compute hypotenuse> hyp ## print (and return) it> FORMULA;>> $value = escapeshellarg($formula);> $calculated = `echo $value | bc`; /* backticks! */>> // with "rm" in the formula, $calculated will have the error message> // from bc; it might be possible to remove all messages with a> // simple regular expression, I didn't look into that.> echo $calculated;> ?>>> --> If you're posting through Google read <http://cfaj.freeshell.org/google>