The following code was written by me.
It is used for evaluating constant expressions.
I think it is good. Any criticism?
namespace Churunmin.Hello World
{
using System;
using System.Collecti ons.Generic;
using System.Diagnost ics;
using System.Text;
using System.Reflecti on;
public class Calculator
{
[AttributeUsage( AttributeTarget s.Field)]
public class TokenAttribute: Attribute
{
private string name;
public TokenAttribute( string name)
{
this.name = name;
}
public string Name
{
get
{
return this.name;
}
}
}
[AttributeUsage( AttributeTarget s.Field)]
public class KeywordAttribut e: TokenAttribute
{
public KeywordAttribut e(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, stringtokenDict ionary =
new Dictionary<int, string>();
public static string TokenToString(i nt token)
{
if (token < 0)
{
return tokenDictionary[token];
}
else if (SIMPLE_TOKENS. IndexOf((char)t oken) >= 0)
{
return ((char)token).T oString();
}
throw new Exception("Atte mpt to convert an invalid token to string:
" + token);
}
private static readonly Dictionary<stri ng, intkeywordDicti onary =
new Dictionary<stri ng, int>();
public static int TokenFromKeywor d(string keyword)
{
if (keywordDiction ary.ContainsKey (keyword))
{
return keywordDictiona ry[keyword];
}
throw new Exception("Atte mpt to convert an invalid keyword to
token: " + keyword);
}
public static void Test()
{
Console.WriteLi ne("keywordDict ionary:");
foreach (KeyValuePair<s tring, intkv in keywordDictiona ry)
{
Console.WriteLi ne("{0} --{1}", kv.Key, kv.Value);
}
Console.WriteLi ne("tokenDictio nary:");
foreach (KeyValuePair<i nt, stringkv in tokenDictionary )
{
Console.WriteLi ne("{0} --{1}", kv.Key, kv.Value);
}
}
static Tokener()
{
FieldInfo[] fields = typeof(Tokener) .GetFields(Bind ingFlags.Static
| BindingFlags.De claredOnly | BindingFlags.Pu blic |
BindingFlags.No nPublic);
foreach (FieldInfo field in fields)
{
TokenAttribute attr = Attribute.GetCu stomAttribute(f ield,
typeof(TokenAtt ribute), false) as TokenAttribute;
if (attr != null)
{
int v = (int)field.GetR awConstantValue ();
tokenDictionary[v] = attr.Name;
if (attr.GetType() == typeof(KeywordA ttribute))
{
keywordDictiona ry[attr.Name] = v;
}
}
}
}
}
public class Scanner: Tokener
{
public double DoubleData;
public int Token;
private StringBuilder cache = new StringBuilder(2 56);
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.Appe nd(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.Leng th = 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(th is.cache.ToStri ng());
return true;
}
private bool ReadName()
{
this.cache.Leng th = 0;
if (!(IsLetter(thi s.c) || this.c == '_'))
{
return false;
}
SaveRead();
while (IsLetter(this. c) || IsDigit(this.c) || this.c == '_')
{
SaveRead();
}
this.Token = TokenFromKeywor d(this.cache.To String());
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("Enco unter invalid character when scanning for
token: '" + this.c + "'");
}
public static new void Test()
{
string s = "3*6+2*pi+e^2%6 6-9000";
Console.WriteLi ne("Scan \"" + s + "\" for tokens:");
Scanner scanner = new Scanner(s);
while (scanner.Token != Scanner.TK_NONE )
{
Console.WriteLi ne(Scanner.Toke nToString(scann er.Token));
scanner.Next();
}
}
}
public class Parser: Scanner
{
private static readonly Dictionary<int, intleftPriority = new
Dictionary<int, int>();
private static readonly Dictionary<int, intrightPriorit y = new
Dictionary<int, int>();
static Parser()
{
leftPriority.Ad d('+', 6); rightPriority.A dd('+', 6);
leftPriority.Ad d('-', 6); rightPriority.A dd('-', 6);
leftPriority.Ad d('*', 7); rightPriority.A dd('*', 7);
leftPriority.Ad d('/', 7); rightPriority.A dd('/', 7);
leftPriority.Ad d('%', 7); rightPriority.A dd('%', 7);
leftPriority.Ad d('^', 10); rightPriority.A dd('^', 9); // right
associative
}
public Parser(string data): base(data)
{
}
public int ParseExpression (out double v, int limit)
{
ParseSimpleExpr ession(out v); // the expression must start with a
simple expression
int op = this.Token;
// expand while operators have priorities higher than 'limit'
while (leftPriority.C ontainsKey(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("Expe cted an operator token, but was: '" +
TokenToString(o p) + "'");
}
op = nextop;
}
return op;
}
private void ParseSimpleExpr ession(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("Unex pected token: '" +
TokenToString(t his.Token) + "'");
}
private void ExpectToken(int t)
{
if (this.Token != t) throw new Exception(Strin g.Format("Expec ted
token: '{0]', but was '{1}'", TokenToString(t ),
TokenToString(t his.Token)));
Next();
}
public static new void Test()
{
double v;
string s = "3*6 + 2 - 10 % 99 + lg(8)/lg(2)";
new Parser(s).Parse Expression(out v, 0);
Console.WriteLi ne("{0} = {1}", s, v);
s = "ln(e^2)";
new Parser(s).Parse Expression(out v, 0);
Console.WriteLi ne("{0} = {1}", s, v);
s = "sin(pi/2)";
new Parser(s).Parse Expression(out v, 0);
Console.WriteLi ne("{0} = {1}", s, v);
foreach(Encodin gInfo ei in Encoding.GetEnc odings())
{
Encoding e = ei.GetEncoding( );
Console.Write( "{0,-6} {1,-25} {2,-40}", ei.CodePage, ei.Name,
ei.DisplayName) ;
Console.WriteLi ne();
}
}
}
public static double Evaluate(string s)
{
double v;
new Parser(s).Parse Expression(out v, 0);
return v;
}
}
}