Results 1 to 7 of 7

Thread: Simple math expression evaluator

  1. #1

    Simple math expression evaluator

    Hi all,
    I have created a simple math expression evaluator class using code & ideas from this great compiler/interpreter creation site:

    http://compilers.iecc.com/crenshaw/

    and I thought I would share it

    It evaluates standard maths equations containing '-', '+', '*', '/', '(' and ')', and returns a floating point (Single) result.

    It says if it encountered an error, and what it was too.

    It handles floating point number input in this form currently:

    <integer part>[.<fractional part>]
    [pascal]Unit unit_Evaluator;

    Interface

    Type
    {................................................. .............................}
    TExpressionEvaluator = Class
    private
    FLook: AnsiChar;
    FBuffer: AnsiString;
    FErrorMsg: AnsiString;

    Function IsAlpha(Const c: AnsiChar): Boolean;
    Function IsAddOp(Const c: AnsiChar): Boolean;
    Function IsMulOp(Const c: AnsiChar): Boolean;
    Function IsDigit(Const c: AnsiChar): Boolean;
    Function IsWhiteSpace(Const c: AnsiChar): Boolean;
    Procedure Error(Const aErrorMsg: AnsiString);
    Function Factor: Single;
    Function Term: Single;
    Procedure Expected(s: AnsiString);
    Procedure Match(s: AnsiString);
    Procedure GetChar;
    Procedure SkipWhiteSpaces;
    Function GetNumber: Single;
    Function Expression: Single;
    public
    Function Evaluate(Const aExpression: AnsiString; Var aExpressionResult: Single): Boolean;
    Property ErrorMsg: AnsiString Read FErrorMsg;
    End;
    {................................................. .............................}

    Implementation

    Uses
    SysUtils;

    Const
    {................................................. .............................}
    cCR = #13;
    cLF = #10;
    cSpace = ' ';
    cTab = ^I;

    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.IsAlpha(Const c: AnsiChar): Boolean;
    Begin
    Result := c In['a'..'z','A'..'Z'];
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.IsDigit(Const c: AnsiChar): Boolean;
    Begin
    Result := c In['0'..'9'];
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.IsAddOp(Const c: AnsiChar): Boolean;
    Begin
    Result := c In['-','+'];
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.IsMulOp(Const c: AnsiChar): Boolean;
    Begin
    Result := c In['/','*'];
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.IsWhiteSpace(Const c: AnsiChar): Boolean;
    Begin
    Result := c In[cCR,cLF,cSpace,cTAB];
    End;
    {................................................. .............................}

    {................................................. .............................}
    Procedure TExpressionEvaluator.Error(Const aErrorMsg: AnsiString);
    Begin
    FErrorMsg := aErrorMsg;
    Raise Exception.Create(aErrorMsg);
    End;
    {................................................. .............................}

    {................................................. .............................}
    Procedure TExpressionEvaluator.Expected(s: AnsiString);
    Begin
    Error(s + ' Expected');
    End;
    {................................................. .............................}

    {................................................. .............................}
    Procedure TExpressionEvaluator.Match(s: AnsiString);
    Var
    i: Integer;
    Begin
    For i := 1 To Length(s) Do Begin
    If FLook = s[i] Then
    GetChar
    Else
    Expected('''' + s + '''');
    End;
    SkipWhiteSpaces;
    End;
    {................................................. .............................}

    {................................................. .............................}
    Procedure TExpressionEvaluator.GetChar;
    Begin
    If FBuffer = '' Then
    FLook := #0
    Else Begin
    FLook := FBuffer[1];
    Delete(FBuffer,1,1);
    End;
    End;
    {................................................. .............................}

    {................................................. .............................}
    Procedure TExpressionEvaluator.SkipWhiteSpaces;
    Begin
    While IsWhiteSpace(FLook) Do
    GetChar;
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.GetNumber: Single;
    Var
    Number: AnsiString;
    Code: Integer;
    Begin
    If Not IsDigit(FLook) Then Error('Number Expected');
    Number := '';
    // read integer part
    While IsDigit(FLook) Do Begin
    Number := Number + FLook;
    GetChar;
    End;
    If FLook = '.' Then Begin
    // read fractional part
    Number := Number + FLook;
    Match(FLook);
    If Not IsDigit(FLook) Then Error('Digit Expected after "."');
    While IsDigit(FLook) Do Begin
    Number := Number + FLook;
    GetChar;
    End;
    End;
    Val(Number,Result,Code);
    If Code <> 0 Then Error('Illegal Number "'+Number+'"');

    SkipWhiteSpaces;
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.Factor: Single;
    Begin
    If FLook = '(' Then Begin
    Match('(');
    Result := Expression;
    Match(')');
    End
    Else
    Result := GetNumber;
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.Term: Single;
    Begin
    Result := Factor;
    While IsMulOp(FLook) Do Begin
    Case FLook of
    '*': Begin
    Match('*');
    Result := Result * Term;
    End;
    '/': Begin
    Match('/');
    Result := Result / Term;
    End;
    End;
    End;
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.Expression: Single;
    begin
    If IsAddOp(FLook) Then
    Result := 0
    Else
    Result := Term;

    While IsAddOp(FLook) Do Begin
    Case FLook of
    '+': Begin
    Match('+');
    Result := Result + Term;
    End;
    '-': Begin
    Match('-');
    Result := Result - Term;
    End;
    End;
    End;
    End;
    {................................................. .............................}

    {................................................. .............................}
    Function TExpressionEvaluator.Evaluate(Const aExpression: AnsiString; Var aExpressionResult: Single): Boolean;
    Begin
    Result := True;

    FBuffer := aExpression;
    FErrorMsg := '';
    GetChar;
    Try
    aExpressionResult := Expression;
    Except
    Result := False;
    End;
    End;
    {................................................. .............................}

    {................................................. .............................}
    End.
    [/pascal]

    Enjoy!

    cheers,
    Paul

  2. #2

    Re: Simple math expression evaluator

    Ewww... Crenshaw Compilers.... Gotta love em!

    When I started looking at compiler theory and started working on my own languages I started with Crenshaws work too.

    A step up from this is to build a FORTH style compiler and use a basic stack to handle the manipulations. This quickly leads you into basic variables with local, global, and static (constants) visibility.

    Then you can move into optimizers, dynamic lexers, virtual machine implementation, and all the other fun things that come with compilers/theory.

    My first attempt at something like this was "TSimpleMath" I still have it on my website at http://eonclash.com/ViewProduct.php?ProductID=9 if you want something for comparison. I'd do it completely different today, but it works well and I haven't seen a need to rewrite it

    - Jeremy

    PS: I've spent a LOT of time doing things like this when you get stuck, there is a reason the best book on the subject is call "The Dragon Book".

  3. #3

    Re: Simple math expression evaluator

    Quote Originally Posted by jdarling
    Ewww... Crenshaw Compilers.... Gotta love em!
    LOL!

    Quote Originally Posted by jdarling
    When I started looking at compiler theory and started working on my own languages I started with Crenshaws work too.

    A step up from this is to build a FORTH style compiler and use a basic stack to handle the manipulations. This quickly leads you into basic variables with local, global, and static (constants) visibility.
    I have already done a pascal-like scripting language complete with 'op-codes' + virtual machine, and a "compiler" for a virtual computer called "RetroBox" (http://www.retrogamedev.org/retrobox...oBoxProjec.php) using his work

    It is lots of fun

    Quote Originally Posted by jdarling
    Then you can move into optimizers, dynamic lexers, virtual machine implementation, and all the other fun things that come with compilers/theory.

    My first attempt at something like this was "TSimpleMath" I still have it on my website at http://eonclash.com/ViewProduct.php?ProductID=9 if you want something for comparison. I'd do it completely different today, but it works well and I haven't seen a need to rewrite it
    Thanks
    I might take a look at it

    Quote Originally Posted by jdarling
    - Jeremy

    PS: I've spent a LOT of time doing things like this when you get stuck, there is a reason the best book on the subject is call "The Dragon Book".
    I've heard of it of course, but never seen it myself

    cheers,
    Paul

  4. #4

    Re: Simple math expression evaluator

    While I was studying compilers in the faculty we used Aflex and Ayacc to create a lexic and sintactic parser, which we used in our own little compiler.

    These two are version of the Lex and Yacc tools rewritten for Ada, but I discovered you can use them to parse any language you want as long as you provide them with a LALR grammatic.

    URL is http://www.ics.uci.edu/~arcadia/Afle...lex-ayacc.html

    If you want any more information on this I can try to dig it out from my notes.

  5. #5

    Re: Simple math expression evaluator

    Thanks

    I DID have a pascal version of lex and yacc lying around a while ago, but never used them - didn't understand how, and I'm not sure I would have liked trying to modify the created compilers to my own needs anyway (unlike a recursive descent parser hand-written)...

    cheers,
    Paul

  6. #6

    Re: Simple math expression evaluator

    Quote Originally Posted by paul_nicholls
    Thanks

    I DID have a pascal version of lex and yacc lying around a while ago, but never used them - didn't understand how, and I'm not sure I would have liked trying to modify the created compilers to my own needs anyway (unlike a recursive descent parser hand-written)...

    cheers,
    Paul
    That would be TPLY4.1 and can be found at http://www.musikwissenschaft.uni-mainz.de/~ag/tply/. I started to updated/rewrite them both a while back and then I realized I don't like Lex and Yacc . The hand written lexer and syntax tree generators I write are easier to understand and modify for me. So, never went anywhere with them. Some place on my HD I have a working copy and an IDE if anyone is interested.

    EDIT: Found it http://www.eonclash.com/tply41a/tply41a.zip

    - Jeremy

  7. #7

    Re: Simple math expression evaluator

    Quote Originally Posted by paul_nicholls

    I DID have a pascal version of lex and yacc lying around a while ago, but never used them - didn't understand how, and I'm not sure I would have liked trying to modify the created compilers to my own needs anyway (unlike a recursive descent parser hand-written)...
    oh you do not need to rewrite Lex or Yacc. You only need to provide them with a lexic and grammar and they create a lexic and sintactic parser for you.

    Creating the lexic file for Lex is quite simple. Basically you do 2 things:

    - specify your tokens and give them an internal identifier
    - specify the form your variable, constants and literals can take by giving Lex a regular expression.

    This takes no more than 1 page of "code", believe me.

    The grammar file for Yacc needs some more work since you need to make sure your grammar complies with the LALR conditions. In the university we made it for a subset of the Ada language and needed around 1 week including polishing, so for a simple grammar like yours it should not take more than 1 day.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •