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 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;
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;
{................................................. .............................}

{................................................. .............................}
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 := '';
While IsDigit(FLook) Do Begin
Number := Number + FLook;
GetChar;
End;
If FLook = '.' Then Begin
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
Result := 0
Else
Result := Term;

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. 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. Re: Simple math expression evaluator

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

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

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

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. 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.

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

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. Re: Simple math expression evaluator

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. Re: Simple math expression evaluator

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.

Posting Permissions

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