ln.templates/Expression.cs

442 lines
13 KiB
C#

using System;
using System.Reflection;
using System.IO;
using System.Text;
using System.Collections.Generic;
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);
}
private void Compile()
{
StringReader reader = new StringReader(Source);
List<Token> tokens = new List<Token>();
while (reader.Peek() != -1)
ReadToken(reader, tokens);
TopEval = BuildEval(tokens);
}
private Eval BuildEval(List<Token> tokens)
{
Eval currentEval = null;
while (tokens.Count > 0)
{
Token token = tokens[0];
tokens.RemoveAt(0);
switch (token.TokenType)
{
case TokenType.DOT:
token = tokens[0];
tokens.RemoveAt(0);
if (token.TokenType != TokenType.NAME)
throw new ArgumentException("missing fieldname after .");
currentEval = new Name(currentEval, token.Value);
break;
case TokenType.NAME:
currentEval = new Name(currentEval, token.Value);
break;
case TokenType.NUMBER:
currentEval = new Number(token.Value);
break;
case TokenType.STRING:
currentEval = new CString(token.Value);
break;
case TokenType.BRACKET:
if (token.Value.Equals("["))
{
currentEval = new Indexer(currentEval, BuildEval(tokens));
} else if (token.Value.Equals("]"))
{
return currentEval;
}
break;
}
}
return currentEval;
}
private void ReadToken(TextReader reader, List<Token> tokens)
{
if (reader.Peek() == -1)
return;
char ch = (char)reader.Peek();
if (char.IsWhiteSpace(ch))
{
reader.Read();
}
else if (char.IsLetter(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 == '[')| (ch == ']'))
{
reader.Read();
tokens.Add(new Token(TokenType.BRACKET, new string(new char[] { ch })));
}
else
{
throw new FormatException(String.Format("Unexpected character: {0}", (char)reader.Peek()));
}
}
private void ReadName(TextReader reader, List<Token> tokens)
{
StringBuilder sb = new StringBuilder();
int ch = 0;
while (char.IsLetterOrDigit((char)(ch = reader.Peek())))
{
sb.Append((char)reader.Read());
}
tokens.Add(new Token(TokenType.NAME, sb.ToString()));
}
private void ReadNumber(TextReader reader, List<Token> 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<Token> 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, BRACKET }
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<string,object> MappedValues { get; }
public Context(Context source)
: this(source.This, source.MappedValues)
{
}
public Context(object o,IEnumerable<KeyValuePair<string,object>> mappedValues)
{
this.This = o;
this.MappedValues = new Dictionary<string, object>();
if (mappedValues != null)
foreach (KeyValuePair<string, object> 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 Number : Eval
{
object value;
public Number(String sourceValue)
: base(null)
{
try
{
value = int.Parse(sourceValue);
} catch
{
value = float.Parse(sourceValue);
}
}
public override object Evaluate(Context context)
{
return value;
}
}
class CString : Eval
{
string value;
public CString(String sourceValue)
: base(null)
{
this.value = sourceValue;
}
public override object Evaluate(Context context)
{
return this.value;
}
}
//private String read(TextReader reader,ConditionDelegate condition)
//{
// StringBuilder sb = new StringBuilder();
// int ch;
// while (((ch = reader.Peek())!=-1)&&condition((char)ch))
// {
// sb.Append((char)ch);
// }
// return sb.ToString();
//}
//private Token ReadToken(TextReader reader)
//{
// int ch = reader.Peek();
// if (char.IsLetter((char)ch))
// return ReadFieldName(reader);
// throw new FormatException();
//}
//private Token ReadPath(TextReader reader)
//{
// String read(reader, (ch) => 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);
// }
//}
}
}