mathematical expression parser in Delphi?

11,590

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?

Share:
11,590
Admin
Author by

Admin

Updated on July 05, 2022

Comments

  • Admin
    Admin almost 2 years

    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.

  • Marco van de Voort
    Marco van de Voort over 11 years
    A scripting language is IMHO overkill if all that is needed is an math evaluator.
  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers over 11 years
    Hence my upvote for the TEvaluator answer (:
  • Rudy Velthuis
    Rudy Velthuis over 5 years
    If you think Java string handling is bad, try Objective-C and Cocoa on the Mac. <g>