PC Review


Reply
Thread Tools Rate Thread

Constant expression evaluation

 
 
CHU Run-min
Guest
Posts: n/a
 
      21st Jan 2007
The following code was written by me.
It is used for evaluating constant expressions.
I think it is good. Any criticism?





namespace Churunmin.HelloWorld
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Reflection;

public class Calculator
{
[AttributeUsage(AttributeTargets.Field)]
public class TokenAttribute: Attribute
{
private string name;

public TokenAttribute(string name)
{
this.name = name;
}

public string Name
{
get
{
return this.name;
}
}
}

[AttributeUsage(AttributeTargets.Field)]
public class KeywordAttribute: TokenAttribute
{
public KeywordAttribute(string name): base(name)
{
}
}

public class Tokener
{
[Token("<none>")]
public const int TK_NONE = -1;
[Token("<number>")]
public const int TK_NUMBER = -2;

[Keyword("pi")]
public const int TK_PI = -3;
[Keyword("e")]
public const int TK_E = -4;
[Keyword("sin")]
public const int TK_SIN = -5;
[Keyword("cos")]
public const int TK_COS = -6;
[Keyword("tan")]
public const int TK_TAN = -7;
[Keyword("lg")]
public const int TK_LG = -8;
[Keyword("ln")]
public const int TK_LN = -9;

public const string SIMPLE_TOKENS = "+-*/%^()";

private static readonly Dictionary<int, string> tokenDictionary =
new Dictionary<int, string>();
public static string TokenToString(int token)
{
if (token < 0)
{
return tokenDictionary[token];
}
else if (SIMPLE_TOKENS.IndexOf((char)token) >= 0)
{
return ((char)token).ToString();
}
throw new Exception("Attempt to convert an invalid token to string:
" + token);
}

private static readonly Dictionary<string, int> keywordDictionary =
new Dictionary<string, int>();
public static int TokenFromKeyword(string keyword)
{
if (keywordDictionary.ContainsKey(keyword))
{
return keywordDictionary[keyword];
}
throw new Exception("Attempt to convert an invalid keyword to
token: " + keyword);
}

public static void Test()
{
Console.WriteLine("keywordDictionary:");
foreach (KeyValuePair<string, int> kv in keywordDictionary)
{
Console.WriteLine("{0} --> {1}", kv.Key, kv.Value);
}

Console.WriteLine("tokenDictionary:");
foreach (KeyValuePair<int, string> kv in tokenDictionary)
{
Console.WriteLine("{0} --> {1}", kv.Key, kv.Value);
}
}

static Tokener()
{
FieldInfo[] fields = typeof(Tokener).GetFields(BindingFlags.Static
| BindingFlags.DeclaredOnly | BindingFlags.Public |
BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
TokenAttribute attr = Attribute.GetCustomAttribute(field,
typeof(TokenAttribute), false) as TokenAttribute;
if (attr != null)
{
int v = (int)field.GetRawConstantValue();
tokenDictionary[v] = attr.Name;
if (attr.GetType() == typeof(KeywordAttribute))
{
keywordDictionary[attr.Name] = v;
}
}
}
}
}

public class Scanner: Tokener
{
public double DoubleData;
public int Token;

private StringBuilder cache = new StringBuilder(256);

private string data;
private int dataIndex;

private char c;

private void Read()
{
if (dataIndex < data.Length)
{
this.c = data[dataIndex++];
}
else
{
this.c = '\0';
}
}

private bool Check(string s, char c)
{
return s.IndexOf(c) >= 0;
}

private bool Check(string s)
{
return Check(s, this.c);
}

private void Save(char c)
{
this.cache.Append(c);
}

private void Save()
{
Save(this.c);
}

private void SaveRead()
{
Save();
Read();
}

public Scanner(string data)
{
this.data = data;
Read();
Next();
}

private bool IsDigit(char c)
{
return c >= '0' && c <='9';
}

private bool IsLetter(char c)
{
return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}

private bool ReadNumber()
{
this.cache.Length = 0;
if (!IsDigit(this.c))
{
return false;
}
SaveRead();
while (IsDigit(this.c))
{
SaveRead();
}
if (this.c == '.')
{
SaveRead();
if (!IsDigit(this.c))
{
throw new Exception("The radix point must be followed by
digit.");
}
SaveRead();
while (IsDigit(this.c))
{
SaveRead();
}
}
this.Token = TK_NUMBER;
this.DoubleData = double.Parse(this.cache.ToString());
return true;
}

private bool ReadName()
{
this.cache.Length = 0;
if (!(IsLetter(this.c) || this.c == '_'))
{
return false;
}
SaveRead();
while (IsLetter(this.c) || IsDigit(this.c) || this.c == '_')
{
SaveRead();
}
this.Token = TokenFromKeyword(this.cache.ToString());
return true;
}

private void SkipBlank()
{
while (this.c == ' ')
{
Read();
}
}

public void Next()
{
SkipBlank();
if (this.c == '\0')
{
this.Token = TK_NONE;
return;
}
if (ReadNumber()) return;
if (ReadName()) return;
if (SIMPLE_TOKENS.IndexOf(this.c) >= 0)
{
this.Token = this.c; // +, -, *, /, etc.
Read();
return;
}
throw new Exception("Encounter invalid character when scanning for
token: '" + this.c + "'");
}

public static new void Test()
{
string s = "3*6+2*pi+e^2%66-9000";
Console.WriteLine("Scan \"" + s + "\" for tokens:");
Scanner scanner = new Scanner(s);
while (scanner.Token != Scanner.TK_NONE)
{
Console.WriteLine(Scanner.TokenToString(scanner.Token));
scanner.Next();
}
}
}


public class Parser: Scanner
{
private static readonly Dictionary<int, int> leftPriority = new
Dictionary<int, int>();
private static readonly Dictionary<int, int> rightPriority = new
Dictionary<int, int>();
static Parser()
{
leftPriority.Add('+', 6); rightPriority.Add('+', 6);
leftPriority.Add('-', 6); rightPriority.Add('-', 6);
leftPriority.Add('*', 7); rightPriority.Add('*', 7);
leftPriority.Add('/', 7); rightPriority.Add('/', 7);
leftPriority.Add('%', 7); rightPriority.Add('%', 7);
leftPriority.Add('^', 10); rightPriority.Add('^', 9); // right
associative
}

public Parser(string data): base(data)
{
}

public int ParseExpression(out double v, int limit)
{
ParseSimpleExpression(out v); // the expression must start with a
simple expression
int op = this.Token;
// expand while operators have priorities higher than 'limit'
while (leftPriority.ContainsKey(op) && leftPriority[op] > limit)
{
Next();
double rhs; // right hand side
// read expression with higher priority
int nextop = ParseExpression(out rhs, rightPriority[op]);
switch (op)
{
case '+':
v = v + rhs;
break;
case '-':
v = v - rhs;
break;
case '*':
v = v * rhs;
break;
case '/':
v = v / rhs;
break;
case '%':
v = v % rhs;
break;
case '^':
v = Math.Pow(v, rhs);
break;
default:
throw new Exception("Expected an operator token, but was: '" +
TokenToString(op) + "'");
}
op = nextop;
}
return op;
}

private void ParseSimpleExpression(out double v)
{
if (this.Token == TK_NUMBER)
{
v = this.DoubleData;
Next();
return;
}
if (this.Token == TK_PI)
{
v = Math.PI;
Next();
return;
}
if (this.Token == TK_E)
{
v = Math.E;
Next();
return;
}
if (this.Token == TK_SIN)
{
Next();
ExpectToken('(');
ParseExpression(out v, 0);
ExpectToken(')');
v = Math.Sin(v);
return;
}
if (this.Token == TK_COS)
{
Next();
ExpectToken('(');
ParseExpression(out v, 0);
ExpectToken(')');
v = Math.Cos(v);
return;
}
if (this.Token == TK_TAN)
{
Next();
ExpectToken('(');
ParseExpression(out v, 0);
ExpectToken(')');
v = Math.Tan(v);
return;
}
if (this.Token == TK_LG)
{
Next();
ExpectToken('(');
ParseExpression(out v, 0);
ExpectToken(')');
v = Math.Log10(v);
return;
}
if (this.Token == TK_LN)
{
Next();
ExpectToken('(');
ParseExpression(out v, 0);
ExpectToken(')');
v = Math.Log(v);
return;
}
throw new Exception("Unexpected token: '" +
TokenToString(this.Token) + "'");
}

private void ExpectToken(int t)
{
if (this.Token != t) throw new Exception(String.Format("Expected
token: '{0]', but was '{1}'", TokenToString(t),
TokenToString(this.Token)));
Next();
}

public static new void Test()
{
double v;
string s = "3*6 + 2 - 10 % 99 + lg(8)/lg(2)";
new Parser(s).ParseExpression(out v, 0);
Console.WriteLine("{0} = {1}", s, v);
s = "ln(e^2)";
new Parser(s).ParseExpression(out v, 0);
Console.WriteLine("{0} = {1}", s, v);
s = "sin(pi/2)";
new Parser(s).ParseExpression(out v, 0);
Console.WriteLine("{0} = {1}", s, v);
foreach(EncodingInfo ei in Encoding.GetEncodings())
{
Encoding e = ei.GetEncoding();
Console.Write( "{0,-6} {1,-25} {2,-40}", ei.CodePage, ei.Name,
ei.DisplayName);
Console.WriteLine();
}

}
}

public static double Evaluate(string s)
{
double v;
new Parser(s).ParseExpression(out v, 0);
return v;
}
}
}

 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
order of expression evaluation parez Microsoft C# .NET 4 29th Apr 2008 03:11 AM
expression evaluation =?Utf-8?B?RGF2aWQ=?= Microsoft C# .NET 2 4th Aug 2005 04:17 AM
Expression evaluation zdrakec Microsoft VB .NET 3 29th Jul 2005 07:24 PM
Dynamic Expression Evaluation Narayanan Sankaranarayanan Microsoft VB .NET 2 13th Apr 2004 11:18 AM
Re: Differences in expression evaluation - VB/C# cody Microsoft Dot NET Framework 0 26th Jul 2003 08:42 PM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 10:41 AM.