using System.Collections.Generic;
Add, Sub, Mul, Div, Mod, Equals,
LesserEquals, Lesser, CompEquals, NotEquals, Greater, GreaterEquals, Not, And, Or, Xor,
LeftParenth, RightParenth, LeftBracket, RightBracket, LeftBrace, RightBrace,
Colon, Semicolon, Comma, Dot, Money, At, Sharp, Question,
BitwiseAnd, BitwiseOr, LeftShift, RightShift,
Increment, Decrement, AddTo, SubTo, MulBy, DivBy,
public static class Extensions
public static bool IsLetter(this char c)
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
public static bool IsDigit(this char c)
return (c >= '0') && (c <= '9');
public static bool IsSymbol(this char c)
return (c == '+') || (c == '-') || (c == '*') || (c == '/') || (c == '%') || (c == '=')
|| (c == '(') || (c == ')') || (c == '{') || (c == '}') || (c == '[') || (c == ']')
|| (c == ':') || (c == ';') || (c == ',') || (c == '.') || (c == '#') || (c == '@') || (c == '$')
|| (c == '&') || (c == '|') || (c == '!') || (c == '^') || (c == '<') || (c == '>')
|| (c == '\\') || (c == '~') || (c == '?');
public static bool IsDoubleOp(this string s)
return (s == "++") || (s == "--")
|| (s == "+=") || (s == "-=") || (s == "*=") || (s == "/=")
|| (s == "<=") || (s == "==") || (s == "!=") || (s == ">=")
|| (s == "&&") || (s == "||")
|| (s == "<<") || (s == ">>");
public static bool IsKeyword(this string s)
public TokenTypes Type { get; private set; }
public object Value { get; private set; }
public Token(TokenTypes _type, object _value)
public override string ToString()
return "Type: " + Type.ToString() + "\t\t Value: " + Value.ToString();
public IList<Token> Tokens { get; private set; }
#region Character Handling
try { return Input[CharPosition]; }
try { return Input[CharPosition + 1]; }
private bool IsEOF { get { return (CharPosition + 1 == Input.Length); } }
private bool IsLineFeed { get { return (CurrentChar == '\t') || (CurrentChar == '\r') || (CurrentChar == '\n'); } }
private bool IsDoubleSymbol
string Op = CurrentChar.ToString() + NextChar.ToString();
return (Op == "++") || (Op == "--")
|| (Op == ">=") || (Op == "==") || (Op == "!=") || (Op == "<=")
|| (Op == "&&") || (Op == "||")
|| (Op == "+=") || (Op == "-=") || (Op == "*=") || (Op == "/=");
private bool IsComment { get { return (CurrentChar == '/' && NextChar == '/'); } }
#region Private Scanning Methods
private void ScanString()
while (CurrentChar != '"' && !IsEOF)
case '\\': theString += "\\"; break;
case 'r': theString += "\r"; break;
case 'n': theString += "\n"; break;
case 't': theString += "\t"; break;
default: throw new Exception("Unsupported literal: '" + NextChar.ToString() + "', aborting.");
theString += CurrentChar;
if (IsEOF && CurrentChar != '"') throw new Exception("Unterminated string, aborting.");
Tokens.Add(new Token(TokenTypes.String, theString));
throw new Exception("Empty char literal.");
char c = CurrentChar; CharPosition = 1;
throw new Exception("Expected ending char literal.");
Tokens.Add(new Token(TokenTypes.Char, c));
private void ScanIdentOrKeyword()
while (!IsEOF && (CurrentChar != ' ' && (CurrentChar.IsLetter() || CurrentChar.IsDigit() || CurrentChar == '_')))
theString += CurrentChar;
Tokens.Add(new Token(TokenTypes.Ident, theString));
private void ScanNumber()
while (CurrentChar.IsDigit())
theString += CurrentChar;
while (CurrentChar.IsDigit())
theString += CurrentChar;
Tokens.Add(new Token(TokenTypes.Number, double.Parse(theString)));
private void ScanSymbol()
case '+': S = Symbols.Add; break;
case '-': S = Symbols.Sub; break;
case '*': S = Symbols.Mul; break;
case '/': S = Symbols.Div; break;
case '%': S = Symbols.Mod; break;
case '=': S = Symbols.Equals; break;
case '(': S = Symbols.LeftParenth; break;
case ')': S = Symbols.RightParenth; break;
case '[': S = Symbols.LeftBracket; break;
case ']': S = Symbols.RightBracket; break;
case '{': S = Symbols.LeftBrace; break;
case '}': S = Symbols.RightBrace; break;
case '<': S = Symbols.Lesser; break;
case '>': S = Symbols.Greater; break;
case '!': S = Symbols.Not; break;
case '&': S = Symbols.BitwiseAnd; break;
case '|': S = Symbols.BitwiseOr; break;
case '^': S = Symbols.Xor; break;
case '#': S = Symbols.Sharp; break;
case '@': S = Symbols.At; break;
case '$': S = Symbols.Money; break;
case ';': S = Symbols.Semicolon; break;
case ':': S = Symbols.Colon; break;
case '.': S = Symbols.Dot; break;
case ',': S = Symbols.Comma; break;
case '~': S = Symbols.Destructor; break;
case '?': S = Symbols.Question; break;
case '\\': S = Symbols.InvertedBar; break;
default: throw new Exception("Unsupported Symbol, aborting.");
Tokens.Add(new Token(TokenTypes.Symbol, S));
private void ScanDoubleSymbol()
string Compare = CurrentChar.ToString() + NextChar.ToString();
case "++": S = Symbols.Increment; break;
case "--": S = Symbols.Decrement; break;
case "+=": S = Symbols.AddTo; break;
case "-=": S = Symbols.SubTo; break;
case "*=": S = Symbols.MulBy; break;
case "/=": S = Symbols.DivBy; break;
case ">=": S = Symbols.GreaterEquals; break;
case "<=": S = Symbols.LesserEquals; break;
case "==": S = Symbols.CompEquals; break;
case "!=": S = Symbols.NotEquals; break;
case "&&": S = Symbols.And; break;
case "||": S = Symbols.Or; break;
default: throw new Exception("Unsupported symbol, '" + Compare + "' aborting.");
Tokens.Add(new Token(TokenTypes.Symbol, S));
private void SkipComments()
while (CurrentChar != '\n')
public IList<Token> Scan()
while (CharPosition < Input.Length)
if (CurrentChar == ' ' || IsLineFeed)
else if (CurrentChar == '"')
else if (CurrentChar == '\'')
else if (CurrentChar.IsLetter() || CurrentChar == '_')
else if (CurrentChar.IsDigit())
else if (CurrentChar.IsSymbol())
throw new Exception("Non recognized character, aborting.");
public tLexer(string _input)
Tokens = new List<Token>();
Input = _input.ToCharArray();
public abstract class Node
public abstract object Eval();
public class NumberNode : Node
public NumberNode(double d) { Value = d; }
public override object Eval() { return Value; }
public class NegNumberNode : Node
public NegNumberNode(Node d) { Value = d; }
public override object Eval() { return -(double)Value.Eval(); }
public class BooleanNode : Node
public BooleanNode(bool v) { Value = v; }
public override object Eval() { return Value; }
public class NotBooleanNode : Node
public NotBooleanNode(Node v) { Value = v; }
public override object Eval() { return !(bool)Value.Eval(); }
public class StringNode : Node
public StringNode(string t) { Text = t; }
public override object Eval() { return Text; }
public class LinkNode : Node
private Symbols Operator;
private bool IsForStrings()
return (Left.Eval().GetType() != typeof(string) && (Right.Eval().GetType() == typeof(string)))
|| (Left.Eval().GetType() == typeof(string) && (Right.Eval().GetType() != typeof(string)))
|| (Left.Eval().GetType() == typeof(string) && (Right.Eval().GetType() == typeof(string)));
public override object Eval()
if (IsForStrings()) { Rtn = Left.Eval().ToString() + Right.Eval().ToString(); break; }
Rtn = (double)Left.Eval() + (double)Right.Eval();
if (IsForStrings()) throw new Exception("Operator not supported for strings.");
Rtn = (double)Left.Eval() - (double)Right.Eval();
if (IsForStrings()) throw new Exception("Operator not supported for strings.");
Rtn = (double)Left.Eval() * (double)Right.Eval();
if (IsForStrings()) throw new Exception("Operator not supported for strings.");
if ((double)Right.Eval() == 0) throw new Exception("Division by zero.");
Rtn = (double)Left.Eval() / (double)Right.Eval();
if (IsForStrings()) throw new Exception("Operator not supported for strings.");
Rtn = (double)Left.Eval() % (double)Right.Eval();
case Symbols.LesserEquals: Rtn = (double)Left.Eval() <= (double)Right.Eval(); break;
case Symbols.Lesser: Rtn = (double)Left.Eval() < (double)Right.Eval(); break;
case Symbols.CompEquals: Rtn = Left.Eval().Equals(Right.Eval()); break;
case Symbols.NotEquals: Rtn = !Left.Eval().Equals(Right.Eval()); break;
case Symbols.Greater: Rtn = (double)Left.Eval() > (double)Right.Eval(); break;
case Symbols.GreaterEquals: Rtn = (double)Left.Eval() >= (double)Right.Eval(); break;
case Symbols.And: Rtn = (bool)Left.Eval() && (bool)Right.Eval(); break;
case Symbols.Or: Rtn = (bool)Left.Eval() || (bool)Right.Eval(); break;
public LinkNode(Symbols s, Node l, Node r)
Operator = s; Left = l; Right = r;
private int CurrentTokenPosition;
private IList<Token> Tokens;
private Token CurrentToken { get { try { return Tokens[CurrentTokenPosition]; } catch { return null; } } }
private Token NextToken { get { try { return Tokens[CurrentTokenPosition + 1]; } catch { return null; } } }
public IDictionary<string, object> SymbolsTable;
private Token MatchAndEat(object o)
if (CurrentToken.Value.Equals(o) || CurrentToken.Type.Equals(o)) { CurrentTokenPosition++; return CurrentToken; }
throw new Exception("Expected: " + o.ToString() + ", but there was: " + CurrentToken.ToString());
#region Boolean Conditions
if (CurrentToken.Value.Equals(Symbols.LeftParenth))
MatchAndEat(Symbols.LeftParenth);
MatchAndEat(Symbols.RightParenth);
else if (CurrentToken.Type.Equals(TokenTypes.Number))
Rtn = new NumberNode((double)CurrentToken.Value);
MatchAndEat(TokenTypes.Number);
if (CurrentToken.Value.Equals(Symbols.Sub)) { MatchAndEat(Symbols.Sub); return new NegNumberNode(Factor()); }
while (CurrentToken != null && (CurrentToken.Value.Equals(Symbols.Mul)
|| CurrentToken.Value.Equals(Symbols.Div)
|| CurrentToken.Value.Equals(Symbols.Mod)))
switch ((Symbols)CurrentToken.Value)
case Symbols.Mul: Rtn = new LinkNode(Symbols.Mul, Rtn, Mul()); break;
case Symbols.Div: Rtn = new LinkNode(Symbols.Div, Rtn, Div()); break;
case Symbols.Mod: Rtn = new LinkNode(Symbols.Mod, Rtn, Mod()); break;
private Node Add(){ MatchAndEat(Symbols.Add); return Term();}
private Node Sub(){ MatchAndEat(Symbols.Sub); return Term(); }
private Node Mul(){ MatchAndEat(Symbols.Mul); return NotFactor(); }
private Node Div(){ MatchAndEat(Symbols.Div); return NotFactor(); }
private Node Mod(){ MatchAndEat(Symbols.Mod); return NotFactor(); }
while (CurrentToken != null && (CurrentToken.Value.Equals(Symbols.Add) || CurrentToken.Value.Equals(Symbols.Sub)))
switch ((Symbols)CurrentToken.Value)
case Symbols.Add: Rtn = new LinkNode(Symbols.Add, Rtn, Add()); break;
case Symbols.Sub: Rtn = new LinkNode(Symbols.Sub, Rtn, Sub()); break;
private Node GreaterEquals(Node l) { MatchAndEat(Symbols.GreaterEquals); return new LinkNode(Symbols.GreaterEquals, l, ArithExpr()); }
private Node Greater(Node l) { MatchAndEat(Symbols.Greater); return new LinkNode(Symbols.Greater, l, ArithExpr()); }
private Node Equals(Node l) { MatchAndEat(Symbols.CompEquals); return new LinkNode(Symbols.CompEquals, l, ArithExpr()); }
private Node NotEquals(Node l) { MatchAndEat(Symbols.NotEquals); return new LinkNode(Symbols.NotEquals, l, ArithExpr()); }
private Node Lesser(Node l) { MatchAndEat(Symbols.Lesser); return new LinkNode(Symbols.Lesser, l, ArithExpr()); }
private Node LesserEquals(Node l) { MatchAndEat(Symbols.LesserEquals); return new LinkNode(Symbols.LesserEquals, l, ArithExpr()); }
switch ((Symbols)CurrentToken.Value)
case Symbols.GreaterEquals: Left = GreaterEquals(Left); break;
case Symbols.Greater: Left = Greater(Left); break;
case Symbols.CompEquals: Left = Equals(Left); break;
case Symbols.NotEquals: Left = NotEquals(Left); break;
case Symbols.Lesser: Left = Lesser(Left); break;
case Symbols.LesserEquals: Left = LesserEquals(Left); break;
private Node BooleanFactor()
private Node NotBoolFactor()
if (CurrentToken.Value.Equals(Symbols.Not)) { MatchAndEat(Symbols.Not); return new NotBooleanNode(BooleanFactor()); }
private Node BooleanTerm()
Node L = NotBoolFactor();
while (CurrentToken != null && CurrentToken.Value.Equals(Symbols.Or))
switch ((Symbols)CurrentToken.Value)
case Symbols.Or: MatchAndEat(Symbols.Or); L = new LinkNode(Symbols.Or, L, BoolExpr()); break;
while (CurrentToken != null && CurrentToken.Value.Equals(Symbols.And))
switch ((Symbols)CurrentToken.Value)
case Symbols.And: MatchAndEat(Symbols.And); L = new LinkNode(Symbols.And, L, BoolExpr()); break;
private Node Expression()
Console.WriteLine("Result: " + n.Eval());
public Parser(IList<Token> _pToks)
Tokens = new List<Token>(_pToks);
SymbolsTable = new Dictionary<string, object>();
CurrentTokenPosition = 0;
public static void Main()
Console.Write("Type an expression> ");
tLexer t = new tLexer(Console.ReadLine());
foreach (Token i in t.Scan()) Console.WriteLine(i.ToString());
Parser p = new Parser(t.Tokens);
p.Parse(); Console.WriteLine("\n");