using System; using System.Reflection; using System.IO; using System.Text; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; namespace ln.templates { delegate bool ConditionDelegate(char ch); public class Expression { public String Source { get; } private Eval TopEval { get; set; } public Expression(String expr) { Source = expr; Compile(); } public object Evaluate(object o) { Context context = new Context(o, null); return TopEval.Evaluate(context); } public object Evaluate(Context context) { return TopEval.Evaluate(context); } public bool IsTrue(Context context) { try { object v = Evaluate(context); if ((v is bool) && ((bool)v)) return true; if ((v is string) && (!String.Empty.Equals(v))) return true; if ((v is int) && ((int)v != 0)) return true; return false; } catch (Exception e) { return false; } } private void Compile() { try { StringReader reader = new StringReader(Source); List tokens = new List(); while (reader.Peek() != -1) ReadToken(reader, tokens); Queue qtokens = new Queue(tokens); TopEval = BuildExpression(qtokens); } catch (Exception e) { throw new Exception(String.Format("Failed to compile expression: {0}", Source), e); } } private Eval BuildName(Token nameToken,Eval parent,Queue tokens) { if ((tokens.Count > 0) && (tokens.Peek().TokenType == TokenType.OPENLIST)) { tokens.Dequeue(); List pl = new List(); while (tokens.Peek().TokenType != TokenType.ENDLIST) { pl.Add(BuildExpression(tokens)); if (tokens.Peek().TokenType == TokenType.ENDLIST) break; if (tokens.Peek().TokenType != TokenType.COMMA) throw new FormatException("Expected ',' between parameters to call statement"); tokens.Dequeue(); } tokens.Dequeue(); return new MethodCall(parent, nameToken.Value, pl.ToArray()); } else { return new Name(parent, nameToken.Value); } } private Eval BuildPath(Queue tokens) { Eval currentEval = BuildName(tokens.Dequeue(), null, tokens); while (tokens.Count > 0) { Token token = tokens.Peek(); switch (token.TokenType) { case TokenType.DOT: tokens.Dequeue(); currentEval = BuildName(tokens.Dequeue(), currentEval, tokens); break; case TokenType.OPENINDEXER: tokens.Dequeue(); Eval index = BuildExpression(tokens); token = tokens.Dequeue(); if (token.TokenType != TokenType.ENDINDEXER) throw new FormatException("Expected ']'"); currentEval = new Indexer(currentEval, index); break; default: return currentEval; } } return currentEval; } private Eval BuildExpression(Queue tokens) { Token next = tokens.Peek(); switch (next.TokenType) { case TokenType.NAME: return BuildPath(tokens); case TokenType.NUMBER: return new Number(tokens.Dequeue().Value); case TokenType.STRING: return new CString(tokens.Dequeue().Value); } throw new FormatException(String.Format("unexpected Token: {0}", next.Value)); } private void ReadToken(TextReader reader, List tokens) { if (reader.Peek() == -1) return; char ch = (char)reader.Peek(); if (char.IsWhiteSpace(ch)) { reader.Read(); } else if (char.IsLetter(ch) || (ch == '_')) { ReadName(reader, tokens); } else if (char.IsDigit(ch)) { ReadNumber(reader, tokens); } else if (ch == '"') { ReadString(reader, tokens); } else if (ch == '.') { reader.Read(); tokens.Add(new Token(TokenType.DOT, ".")); } else if (ch == '[') { reader.Read(); tokens.Add(new Token(TokenType.OPENINDEXER, new string(new char[] { ch }))); } else if (ch == ']') { reader.Read(); tokens.Add(new Token(TokenType.ENDINDEXER, new string(new char[] { ch }))); } else if (ch == '(') { reader.Read(); tokens.Add(new Token(TokenType.OPENLIST, new string(new char[] { ch }))); } else if (ch == ')') { reader.Read(); tokens.Add(new Token(TokenType.ENDLIST, new string(new char[] { ch }))); } else if (ch == ',') { reader.Read(); tokens.Add(new Token(TokenType.COMMA, new string(new char[] { ch }))); } else { throw new FormatException(String.Format("Unexpected character: {0}", (char)reader.Peek())); } } private void ReadName(TextReader reader, List tokens) { StringBuilder sb = new StringBuilder(); int ch = 0; while (char.IsLetterOrDigit((char)(ch = reader.Peek())) || (ch == '_')) { sb.Append((char)reader.Read()); } tokens.Add(new Token(TokenType.NAME, sb.ToString())); } private void ReadNumber(TextReader reader, List tokens) { StringBuilder sb = new StringBuilder(); int ch = 0; while (char.IsDigit((char)(ch = reader.Peek())) | (ch == '.')) { sb.Append((char)reader.Read()); } tokens.Add(new Token(TokenType.NUMBER, sb.ToString())); } private void ReadString(TextReader reader, List tokens) { StringBuilder sb = new StringBuilder(); int ch = 0; reader.Read(); while ((ch = reader.Peek())!='"') { if (ch == '\\') { reader.Read(); char sch = (char)reader.Read(); switch (sch) { case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 't': sb.Append('\t'); break; default: sb.Append(sch); break; } } else { sb.Append((char)reader.Read()); } } reader.Read(); tokens.Add(new Token(TokenType.STRING, sb.ToString())); } enum TokenType { NAME, DOT, NUMBER, STRING, OPENINDEXER, ENDINDEXER, OPENLIST, ENDLIST, COMMA } class Token { public TokenType TokenType; public string Value; public Token(TokenType tokenType,String value) { this.TokenType = tokenType; this.Value = value; } public override string ToString() { return String.Format("[Token TokenType={0:8} Value={1}]", TokenType, Value); } } public class Context { public object This { get; } public Dictionary MappedValues { get; } public Context(Context source) : this(source.This, source.MappedValues) { } public Context(object o,IEnumerable> mappedValues) { this.This = o; this.MappedValues = new Dictionary(); if (mappedValues != null) foreach (KeyValuePair kvp in mappedValues) MappedValues.Add(kvp.Key,kvp.Value); } public void AddMappedValue(string name, object value) { MappedValues.Add(name, value); } public void SetMappedValue(string name, object value) { MappedValues[name] = value; } public void RemoveMappedValue(string name) { MappedValues.Remove(name); } public object Evaluate(string name) { if (MappedValues.ContainsKey(name)) return MappedValues[name]; if (This != null) { FieldInfo fieldInfo = This.GetType().GetField(name); if (fieldInfo != null) { return fieldInfo.GetValue(This); } PropertyInfo propertyInfo = This.GetType().GetProperty(name); if (propertyInfo != null) { return propertyInfo.GetValue(This); } } throw new KeyNotFoundException(name); } } abstract class Eval { public Eval Parent { get; } public Eval(Eval parent) { this.Parent = parent; } public abstract object Evaluate(Context context); } class Name : Eval { public string name; public Name(Eval parent,String name) :base(parent) { this.name = name; } public override object Evaluate(Context context) { if (Parent == null) { return context.Evaluate(name); } object p = Parent.Evaluate(context); if (p != null) { FieldInfo fieldInfo = p.GetType().GetField(name); if (fieldInfo != null) { return fieldInfo.GetValue(p); } PropertyInfo propertyInfo = p.GetType().GetProperty(name); if (propertyInfo != null) { return propertyInfo.GetValue(p); } throw new KeyNotFoundException(name); } throw new NullReferenceException(); } } class Indexer : Eval { Eval index; public Indexer(Eval parent,Eval index) :base(parent) { this.index = index; } public override object Evaluate(Context context) { object i = index.Evaluate(context); Type itype = i.GetType(); object p = Parent.Evaluate(context); if (p is Array) { Array pa = p as Array; return pa.GetValue((int)i); } foreach (PropertyInfo pi in p.GetType().GetProperties()) { ParameterInfo[] infos = pi.GetIndexParameters(); if (infos.Length == 1) { if (infos[0].ParameterType.IsAssignableFrom(itype)) { return pi.GetValue(p, new object[] { i }); } } } throw new KeyNotFoundException(); } } class MethodCall : Eval { Eval[] Parameters; String Name; public MethodCall(Eval parent, String name,params Eval[] p) : base(parent) { Name = name; Parameters = p; } public override object Evaluate(Context context) { object p = Parent.Evaluate(context); object[] pvalues = new object[Parameters.Length]; Type[] ptypes = new Type[Parameters.Length]; for (int i=0;i char.IsLetterOrDigit(ch)); //} //abstract class Token //{ // public abstract object Evaluate(); //} //class Field : Token //{ // Token owner; // String fieldName; // public Field(Token owner,String fieldName) // { // this.owner = owner; // this.fieldName = fieldName; // } // public override object Evaluate() // { // if (o == null) // return null; // Type t = o.GetType(); // FieldInfo fieldInfo = t.GetField(this.fieldName); // if (fieldInfo != null) // { // return fieldInfo.GetValue(owner.Evaluate()); // } // PropertyInfo propertyInfo = t.GetProperty(this.fieldName); // if (propertyInfo != null) // { // return propertyInfo.GetValue(owner.Evaluate()); // } // throw new MissingFieldException(t.FullName, this.fieldName); // } //} } }