mathematical expression parser in Delphi?
Solution 1
The free JCL includes TEvaluator, a parser written by one of the current Delphi compiler engineers. It will likely be far more efficient than an expression evaluator based on Windows Script Host.
Solution 2
Long ago (iirc 2005), some SIG did an comparison of various expression parsers. The results are at:
http://www.mindspring.com/~rbwinston/ParserTestFiles.zip
including the classic Turbo Pascal one by Renate Schaaf.
In general, the faster ones generate native code, but are unportable, and might need fixing for DEP etc.
Writing a basic one yourself isn't that hard, and a standard task in many programming courses. I wrote one in FPC/Delphi (now part of the freepascal distribution as "Symbolic") and converted it later to Java (as an exercise in Java string handling. I still wake up screaming at night sometimes).
Its SVN location is
http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/packages/symbolic/
Note to self: I still have some unfinished code somewhere to add user definable functions and boolean arithmetic. Must finish it someday :-)
Solution 3
You are looking for something that can evaluate an expression.
Since Delphi is a compiled language, it does not have built-in support for that.
However, there are external tools that can help you with that.
For instance: the Free Pascal Scripting engine from RemObjects can do what you want.
--jeroen
Solution 4
You can use my unit, its still basic but im still writing it, it does basic bodmas right now but i will post the whole unit when i am done
Unit BODMAS;
Interface
Uses
System.SysUtils,
Math;
{
!!!!!!!!!!!!!!!!!!!!!! GLOBAL DEFINITIONS !!!!!!!!!!!!!!!!!!!!
EXPR = EXPRESSION
CURRENTPOS = POSSITION OF THE CURRENT OPPERATOR OF WHICH MATH IS BEING PERFORMED
}
Function EvalFunction(Expr: String): String;
Implementation
Function PrevOppPos(Expr: String; CurrentPos: Integer): Integer; // GETS THE PREVIOUS OPPERATOR
Var
I: Integer;
bSet: Boolean;
Begin
// THEORY
// KEEP MOVING POSITIONS DOWN FROM I ... ( MEANING < WAY IN EXPR)
// UNTIL AN OPPERATOR IS FOUND. IF NO OPPERATOR IS FOUND THE RESULT
// WILL BE THE BEGINING OF THE EXPRESSION
I := CurrentPos - 1;
bSet := False;
While ((I <= CurrentPos) AND (I >= 1)) OR (bSet = False) Do
Begin
// CHECK IF THE CHACHARACTER OF POSITION I IN EXPR IS AN OPPERATOR
// "." AND "," IS NOT AN OPPERATOR!!
If Expr[I] In ['(', ')', '+', '-', 'x', '/'] Then
Begin
Result := I;
bSet := True;
Dec(I); // Dec 1 more time to break loop
End;
Dec(I);
If (I = 0) AND (NOT(bSet)) Then
Begin
Result := 1;
bSet := True;
End;
End;
End;
Function NextOppPos(Expr: String; CurrentPos: Integer): Integer;
Var
I: Integer;
bSet: Boolean;
Begin
// THEORY
// KEEP MOVING POSITIONS UP FROM I ... ( MEANING > WAY IN EXPR)
// UNTIL AN OPPERATOR IS FOUND. IF NO OPPERATOR IS FOUND THE RESULT
// WILL BE THE LENGHT OF THE EXPRESSION
I := CurrentPos + 1;
bSet := False;
While ((I <= Length(Expr)) AND (I >= CurrentPos)) OR (bSet = False) Do
Begin
// CHECK IF THE CHACHARACTER OF POSITION I IN EXPR IS AN OPPERATOR
// "." AND "," IS NOT AN OPPERATOR!!
If Expr[I] In ['(', ')', '+', '-', 'x', '/'] Then
Begin
Result := I;
bSet := True;
Inc(I); // Inc 1 more time to break loop
End;
Inc(I);
If (I = Length(Expr) + 1) AND (NOT(bSet)) Then
Begin
Result := Length(Expr);
bSet := True;
End;
End;
End;
// EVALUATE BRACKET EXPRESSION
Function EvalBracetExpr(Expr: String): String;
Var
OppCount, I: Integer;
Ans: String;
NewExpr: String;
nOpp, pOpp, OppPos: Integer;
nExpr, pExpr: String;
Begin
Ans := '';
// EVALUATE EXPRESSION
// ALL MULTIPLICATION IN BRACKETS
While Pos('x', Expr) <> 0 Do
Begin
OppPos := Pos('x', Expr); // Opperator Position
nOpp := NextOppPos(Expr, OppPos); // Next Opperator Position
pOpp := PrevOppPos(Expr, OppPos); // Previous Opperator Position
// COPY FROM THE OPPERATOR POS TO THE LENGTH OF THE EXPRESSION - THE POSITION OF THE NEXT EXPRESSION
// When Next opperator is the length of the expression
If nOpp = Length(Expr) Then
nExpr := Copy(Expr, OppPos + 1, Length(Expr) - (Length(Expr) - 1))
Else
nExpr := Copy(Expr, OppPos + 1, Length(Expr) - nOpp);
// COPY FROM THE PREVIOUS OPPERATOR POS TO THE OPPERATOR POSITION -1
pExpr := Copy(Expr, pOpp + 1, (OppPos - 1) - pOpp);
Delete(Expr, pOpp, nOpp);
Ans := Ans + FloatToStr(StrToFloat(pExpr) * StrToFloat(nExpr));
End;
// ALL ADDITION IN BRACKETS
While Pos('+', Expr) <> 0 Do
Begin
OppPos := Pos('+', Expr); // Opperator Position
nOpp := NextOppPos(Expr, OppPos); // Next Opperator Position
pOpp := PrevOppPos(Expr, OppPos); // Previous Opperator Position
// COPY FROM THE OPPERATOR POS TO THE LENGTH OF THE EXPRESSION - THE POSITION OF THE NEXT EXPRESSION
// When Next opperator is the length of the expression
If nOpp = Length(Expr) Then
nExpr := Copy(Expr, OppPos + 1, Length(Expr) - (Length(Expr) - 1))
Else
nExpr := Copy(Expr, OppPos + 1, Length(Expr) - nOpp - 1);
// COPY FROM THE PREVIOUS OPPERATOR POS TO THE OPPERATOR POSITION -1
pExpr := Copy(Expr, pOpp + 1, (OppPos - 1) - pOpp);
Delete(Expr, pOpp, nOpp);
Ans := Ans + FloatToStr(StrToFloat(pExpr) + StrToFloat(nExpr));
End;
Result := Ans;
End;
// EVALUTE ADDITION EXPRESSION
Function EvalAddExpr(Expr: String): String;
Var
Expr1, Expr2: String;
Begin
Expr1 := Copy(Expr, 1, Pos('+', Expr) - 1);
Expr2 := Copy(Expr, Pos('+', Expr) + 1, Length(Expr));
Result := FloatToStr(StrToFloat(Expr1) + StrToFloat(Expr2));
End;
Function EvalFunction(Expr: String): String;
Var
bOPos, bCPos: Integer; // bracket Open/Closed Position
sExpr: String;
FinalExpr: String;
OppPos: Integer;
PrevOpp, NextOpp: Integer;
Begin
While Pos('(', Expr) <> 0 Do
Begin
// Find first open bracket
bOPos := Pos('(', Expr);
// Find first closed bracket
bCPos := Pos(')', Expr);
// Get the expression between the 2 brackets
sExpr := Copy(Expr, bOPos, bCPos);
// Remove sExpr from the Expression
Delete(Expr, bOPos, bCPos + 1 - bOPos);
// Concatenate the expression of what was before the bracket and that after the bracket, as well as the result in the middle
FinalExpr := Copy(Expr, 1, bOPos - 1) + EvalBracetExpr(sExpr) + Copy(Expr, bOPos, Length(Expr));
// Return the result
Expr := FinalExpr;
End;
While Pos('+', Expr) <> 0 Do
Begin
// 1) Find the first + opperator in expression
OppPos := Pos('+', Expr);
// 2) find first part of expression
PrevOpp := PrevOppPos(Expr, OppPos);
// 3) find the next part of the expression
NextOpp := NextOppPos(Expr, OppPos);
// 4) get the full expression between the opperators
//
// if prev opp <> 1 then
// move indicator 1 pos ahead
If PrevOpp <> 1 Then
Inc(PrevOpp);
// if next opp <> len of expr then
// move indicator 1 pos back
If NextOpp <> Length(Expr) Then
Dec(NextOpp);
sExpr := Copy(Expr, PrevOpp, NextOpp);
// 5) evaluating expression
Delete(Expr, PrevOpp, NextOpp);
FinalExpr := Copy(Expr, 1, PrevOpp-1) + EvalAddExpr(sExpr) + Copy(Expr, PrevOpp, Length(Expr));
End;
Result := Expr;
End;
End.
you will use the EvalFunction to return results
Solution 5
No, it's impossible except of parsing string. And how can you convert unknown number x to float?
Admin
Updated on July 05, 2022Comments
-
Admin almost 2 years
Duplicate
Is there a built-in Delphi function which would convert a string such as '2*x+power(x,2)' or any equation to float? StrToFloat raises an exception because of the char X and power.
Thanks.
-
Marco van de Voort over 11 yearsA scripting language is IMHO overkill if all that is needed is an math evaluator.
-
Jeroen Wiert Pluimers over 11 yearsHence my upvote for the
TEvaluator
answer (: -
Rudy Velthuis over 5 yearsIf you think Java string handling is bad, try Objective-C and Cocoa on the Mac. <g>