6

Duplicate

Best algorithm for evaluating a mathematical expression?

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.

3

6 Answers 6

12

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.

Sign up to request clarification or add additional context in comments.

Comments

4

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 :-)

Comments

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

3 Comments

A scripting language is IMHO overkill if all that is needed is an math evaluator.
Hence my upvote for the TEvaluator answer (:
If you think Java string handling is bad, try Objective-C and Cocoa on the Mac. <g>
1

No, it's impossible except of parsing string. And how can you convert unknown number x to float?

Comments

1

In our SMImport suite we wrote the own expression parser/evaluator which is based on original TFatExpression component by Gasper Kozak, [email protected]

Works very good.

Comments

0

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

2 Comments

Did you ever finish / update this?
PS - Trying a simple expression 5 * 8 is simply returning the original expression, instead of a calculated result of 40.