Flee
This commit is contained in:
67
PublicTypes/Exceptions.cs
Normal file
67
PublicTypes/Exceptions.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Flee.InternalTypes;
|
||||
using Flee.Parsing;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
public enum CompileExceptionReason
|
||||
{
|
||||
SyntaxError,
|
||||
ConstantOverflow,
|
||||
TypeMismatch,
|
||||
UndefinedName,
|
||||
FunctionHasNoReturnValue,
|
||||
InvalidExplicitCast,
|
||||
AmbiguousMatch,
|
||||
AccessDenied,
|
||||
InvalidFormat
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Serializable()]
|
||||
public sealed class ExpressionCompileException : Exception
|
||||
{
|
||||
private readonly CompileExceptionReason _myReason;
|
||||
internal ExpressionCompileException(string message, CompileExceptionReason reason) : base(message)
|
||||
{
|
||||
_myReason = reason;
|
||||
}
|
||||
|
||||
internal ExpressionCompileException(ParserLogException parseException) : base(string.Empty, parseException)
|
||||
{
|
||||
_myReason = CompileExceptionReason.SyntaxError;
|
||||
}
|
||||
|
||||
private ExpressionCompileException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
|
||||
{
|
||||
_myReason = (CompileExceptionReason)info.GetInt32("Reason");
|
||||
}
|
||||
|
||||
public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
|
||||
{
|
||||
base.GetObjectData(info, context);
|
||||
info.AddValue("Reason", Convert.ToInt32(_myReason));
|
||||
}
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_myReason == CompileExceptionReason.SyntaxError)
|
||||
{
|
||||
Exception innerEx = this.InnerException;
|
||||
string msg = $"{Utility.GetCompileErrorMessage(CompileErrorResourceKeys.SyntaxError)}: {innerEx.Message}";
|
||||
return msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CompileExceptionReason Reason => _myReason;
|
||||
}
|
||||
}
|
251
PublicTypes/ExpressionContext.cs
Normal file
251
PublicTypes/ExpressionContext.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
using Flee.CalcEngine.InternalTypes;
|
||||
using Flee.CalcEngine.PublicTypes;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.Parsing;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
public sealed class ExpressionContext
|
||||
{
|
||||
|
||||
#region "Fields"
|
||||
|
||||
private PropertyDictionary _myProperties;
|
||||
|
||||
private readonly object _mySyncRoot = new object();
|
||||
|
||||
private VariableCollection _myVariables;
|
||||
#endregion
|
||||
|
||||
#region "Constructor"
|
||||
|
||||
public ExpressionContext() : this(DefaultExpressionOwner.Instance)
|
||||
{
|
||||
}
|
||||
|
||||
public ExpressionContext(object expressionOwner)
|
||||
{
|
||||
Utility.AssertNotNull(expressionOwner, "expressionOwner");
|
||||
_myProperties = new PropertyDictionary();
|
||||
|
||||
_myProperties.SetValue("CalculationEngine", null);
|
||||
_myProperties.SetValue("CalcEngineExpressionName", null);
|
||||
_myProperties.SetValue("IdentifierParser", null);
|
||||
|
||||
_myProperties.SetValue("ExpressionOwner", expressionOwner);
|
||||
|
||||
_myProperties.SetValue("ParserOptions", new ExpressionParserOptions(this));
|
||||
|
||||
_myProperties.SetValue("Options", new ExpressionOptions(this));
|
||||
_myProperties.SetValue("Imports", new ExpressionImports());
|
||||
this.Imports.SetContext(this);
|
||||
_myVariables = new VariableCollection(this);
|
||||
|
||||
_myProperties.SetToDefault<bool>("NoClone");
|
||||
|
||||
this.RecreateParser();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Methods - Private"
|
||||
|
||||
private void AssertTypeIsAccessibleInternal(Type t)
|
||||
{
|
||||
bool isPublic = t.IsPublic;
|
||||
|
||||
if (t.IsNested == true)
|
||||
{
|
||||
isPublic = t.IsNestedPublic;
|
||||
}
|
||||
|
||||
bool isSameModuleAsOwner = object.ReferenceEquals(t.Module, this.ExpressionOwner.GetType().Module);
|
||||
|
||||
// Public types are always accessible. Otherwise they have to be in the same module as the owner
|
||||
bool isAccessible = isPublic | isSameModuleAsOwner;
|
||||
|
||||
if (isAccessible == false)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.TypeNotAccessibleToExpression, t.Name);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertNestedTypeIsAccessible(Type t)
|
||||
{
|
||||
while ((t != null))
|
||||
{
|
||||
AssertTypeIsAccessibleInternal(t);
|
||||
t = t.DeclaringType;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Methods - Internal"
|
||||
internal ExpressionContext CloneInternal(bool cloneVariables)
|
||||
{
|
||||
ExpressionContext context = (ExpressionContext)this.MemberwiseClone();
|
||||
context._myProperties = _myProperties.Clone();
|
||||
context._myProperties.SetValue("Options", context.Options.Clone());
|
||||
context._myProperties.SetValue("ParserOptions", context.ParserOptions.Clone());
|
||||
context._myProperties.SetValue("Imports", context.Imports.Clone());
|
||||
context.Imports.SetContext(context);
|
||||
|
||||
if (cloneVariables == true)
|
||||
{
|
||||
context._myVariables = new VariableCollection(context);
|
||||
this.Variables.Copy(context._myVariables);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
internal void AssertTypeIsAccessible(Type t)
|
||||
{
|
||||
if (t.IsNested == true)
|
||||
{
|
||||
AssertNestedTypeIsAccessible(t);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertTypeIsAccessibleInternal(t);
|
||||
}
|
||||
}
|
||||
|
||||
internal ExpressionElement Parse(string expression, IServiceProvider services)
|
||||
{
|
||||
lock (_mySyncRoot)
|
||||
{
|
||||
System.IO.StringReader sr = new System.IO.StringReader(expression);
|
||||
ExpressionParser parser = this.Parser;
|
||||
parser.Reset(sr);
|
||||
parser.Tokenizer.Reset(sr);
|
||||
FleeExpressionAnalyzer analyzer = (FleeExpressionAnalyzer)parser.Analyzer;
|
||||
|
||||
analyzer.SetServices(services);
|
||||
|
||||
Node rootNode = DoParse();
|
||||
analyzer.Reset();
|
||||
ExpressionElement topElement = (ExpressionElement)rootNode.Values[0];
|
||||
return topElement;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RecreateParser()
|
||||
{
|
||||
lock (_mySyncRoot)
|
||||
{
|
||||
FleeExpressionAnalyzer analyzer = new FleeExpressionAnalyzer();
|
||||
ExpressionParser parser = new ExpressionParser(TextReader.Null, analyzer, this);
|
||||
_myProperties.SetValue("ExpressionParser", parser);
|
||||
}
|
||||
}
|
||||
|
||||
internal Node DoParse()
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.Parser.Parse();
|
||||
}
|
||||
catch (ParserLogException ex)
|
||||
{
|
||||
// Syntax error; wrap it in our exception and rethrow
|
||||
throw new ExpressionCompileException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetCalcEngine(CalculationEngine engine, string calcEngineExpressionName)
|
||||
{
|
||||
_myProperties.SetValue("CalculationEngine", engine);
|
||||
_myProperties.SetValue("CalcEngineExpressionName", calcEngineExpressionName);
|
||||
}
|
||||
|
||||
internal IdentifierAnalyzer ParseIdentifiers(string expression)
|
||||
{
|
||||
ExpressionParser parser = this.IdentifierParser;
|
||||
StringReader sr = new StringReader(expression);
|
||||
parser.Reset(sr);
|
||||
parser.Tokenizer.Reset(sr);
|
||||
|
||||
IdentifierAnalyzer analyzer = (IdentifierAnalyzer)parser.Analyzer;
|
||||
analyzer.Reset();
|
||||
|
||||
parser.Parse();
|
||||
|
||||
return (IdentifierAnalyzer)parser.Analyzer;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Methods - Public"
|
||||
|
||||
public ExpressionContext Clone()
|
||||
{
|
||||
return this.CloneInternal(true);
|
||||
}
|
||||
|
||||
public IDynamicExpression CompileDynamic(string expression)
|
||||
{
|
||||
return new Flee.InternalTypes.Expression<object>(expression, this, false);
|
||||
}
|
||||
|
||||
public IGenericExpression<TResultType> CompileGeneric<TResultType>(string expression)
|
||||
{
|
||||
return new Flee.InternalTypes.Expression<TResultType>(expression, this, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Properties - Private"
|
||||
|
||||
private ExpressionParser IdentifierParser
|
||||
{
|
||||
get
|
||||
{
|
||||
ExpressionParser parser = _myProperties.GetValue<ExpressionParser>("IdentifierParser");
|
||||
|
||||
if (parser == null)
|
||||
{
|
||||
IdentifierAnalyzer analyzer = new IdentifierAnalyzer();
|
||||
parser = new ExpressionParser(System.IO.TextReader.Null, analyzer, this);
|
||||
//parser = new ExpressionParser(System.IO.StringReader.Null, analyzer, this);
|
||||
_myProperties.SetValue("IdentifierParser", parser);
|
||||
}
|
||||
|
||||
return parser;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Properties - Internal"
|
||||
|
||||
internal bool NoClone
|
||||
{
|
||||
get { return _myProperties.GetValue<bool>("NoClone"); }
|
||||
set { _myProperties.SetValue("NoClone", value); }
|
||||
}
|
||||
|
||||
internal object ExpressionOwner => _myProperties.GetValue<object>("ExpressionOwner");
|
||||
|
||||
internal string CalcEngineExpressionName => _myProperties.GetValue<string>("CalcEngineExpressionName");
|
||||
|
||||
internal ExpressionParser Parser => _myProperties.GetValue<ExpressionParser>("ExpressionParser");
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Properties - Public"
|
||||
public ExpressionOptions Options => _myProperties.GetValue<ExpressionOptions>("Options");
|
||||
|
||||
public ExpressionImports Imports => _myProperties.GetValue<ExpressionImports>("Imports");
|
||||
|
||||
public VariableCollection Variables => _myVariables;
|
||||
|
||||
public CalculationEngine CalculationEngine => _myProperties.GetValue<CalculationEngine>("CalculationEngine");
|
||||
|
||||
public ExpressionParserOptions ParserOptions => _myProperties.GetValue<ExpressionParserOptions>("ParserOptions");
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
195
PublicTypes/ExpressionImports.cs
Normal file
195
PublicTypes/ExpressionImports.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using System.Reflection;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
public sealed class ExpressionImports
|
||||
{
|
||||
|
||||
private static Dictionary<string, Type> OurBuiltinTypeMap = CreateBuiltinTypeMap();
|
||||
private NamespaceImport MyRootImport;
|
||||
private TypeImport MyOwnerImport;
|
||||
|
||||
private ExpressionContext MyContext;
|
||||
internal ExpressionImports()
|
||||
{
|
||||
MyRootImport = new NamespaceImport("true");
|
||||
}
|
||||
|
||||
private static Dictionary<string, Type> CreateBuiltinTypeMap()
|
||||
{
|
||||
Dictionary<string, Type> map = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
map.Add("boolean", typeof(bool));
|
||||
map.Add("byte", typeof(byte));
|
||||
map.Add("sbyte", typeof(sbyte));
|
||||
map.Add("short", typeof(short));
|
||||
map.Add("ushort", typeof(UInt16));
|
||||
map.Add("int", typeof(Int32));
|
||||
map.Add("uint", typeof(UInt32));
|
||||
map.Add("long", typeof(long));
|
||||
map.Add("ulong", typeof(ulong));
|
||||
map.Add("single", typeof(float));
|
||||
map.Add("double", typeof(double));
|
||||
map.Add("decimal", typeof(decimal));
|
||||
map.Add("char", typeof(char));
|
||||
map.Add("object", typeof(object));
|
||||
map.Add("string", typeof(string));
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
#region "Methods - Non public"
|
||||
internal void SetContext(ExpressionContext context)
|
||||
{
|
||||
MyContext = context;
|
||||
MyRootImport.SetContext(context);
|
||||
}
|
||||
|
||||
internal ExpressionImports Clone()
|
||||
{
|
||||
ExpressionImports copy = new ExpressionImports();
|
||||
|
||||
copy.MyRootImport = (NamespaceImport)MyRootImport.Clone();
|
||||
copy.MyOwnerImport = MyOwnerImport;
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
internal void ImportOwner(Type ownerType)
|
||||
{
|
||||
MyOwnerImport = new TypeImport(ownerType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, false);
|
||||
MyOwnerImport.SetContext(MyContext);
|
||||
}
|
||||
|
||||
internal bool HasNamespace(string ns)
|
||||
{
|
||||
NamespaceImport import = MyRootImport.FindImport(ns) as NamespaceImport;
|
||||
return (import != null);
|
||||
}
|
||||
|
||||
internal NamespaceImport GetImport(string ns)
|
||||
{
|
||||
if (ns.Length == 0)
|
||||
{
|
||||
return MyRootImport;
|
||||
}
|
||||
|
||||
NamespaceImport import = MyRootImport.FindImport(ns) as NamespaceImport;
|
||||
|
||||
if (import == null)
|
||||
{
|
||||
import = new NamespaceImport(ns);
|
||||
MyRootImport.Add(import);
|
||||
}
|
||||
|
||||
return import;
|
||||
}
|
||||
|
||||
internal MemberInfo[] FindOwnerMembers(string memberName, System.Reflection.MemberTypes memberType)
|
||||
{
|
||||
return MyOwnerImport.FindMembers(memberName, memberType);
|
||||
}
|
||||
|
||||
internal Type FindType(string[] typeNameParts)
|
||||
{
|
||||
string[] namespaces = new string[typeNameParts.Length - 1];
|
||||
string typeName = typeNameParts[typeNameParts.Length - 1];
|
||||
|
||||
System.Array.Copy(typeNameParts, namespaces, namespaces.Length);
|
||||
ImportBase currentImport = MyRootImport;
|
||||
|
||||
foreach (string ns in namespaces)
|
||||
{
|
||||
currentImport = currentImport.FindImport(ns);
|
||||
if (currentImport == null)
|
||||
{
|
||||
break; // TODO: might not be correct. Was : Exit For
|
||||
}
|
||||
}
|
||||
|
||||
return currentImport?.FindType(typeName);
|
||||
}
|
||||
|
||||
static internal Type GetBuiltinType(string name)
|
||||
{
|
||||
Type t = null;
|
||||
|
||||
if (OurBuiltinTypeMap.TryGetValue(name, out t) == true)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Methods - Public"
|
||||
public void AddType(Type t, string ns)
|
||||
{
|
||||
Utility.AssertNotNull(t, "t");
|
||||
Utility.AssertNotNull(ns, "namespace");
|
||||
|
||||
MyContext.AssertTypeIsAccessible(t);
|
||||
|
||||
NamespaceImport import = this.GetImport(ns);
|
||||
import.Add(new TypeImport(t, BindingFlags.Public | BindingFlags.Static, false));
|
||||
}
|
||||
|
||||
public void AddType(Type t)
|
||||
{
|
||||
this.AddType(t, string.Empty);
|
||||
}
|
||||
|
||||
public void AddMethod(string methodName, Type t, string ns)
|
||||
{
|
||||
Utility.AssertNotNull(methodName, "methodName");
|
||||
Utility.AssertNotNull(t, "t");
|
||||
Utility.AssertNotNull(ns, "namespace");
|
||||
|
||||
MethodInfo mi = t.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase);
|
||||
|
||||
if (mi == null)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.CouldNotFindPublicStaticMethodOnType, methodName, t.Name);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
|
||||
this.AddMethod(mi, ns);
|
||||
}
|
||||
|
||||
public void AddMethod(MethodInfo mi, string ns)
|
||||
{
|
||||
Utility.AssertNotNull(mi, "mi");
|
||||
Utility.AssertNotNull(ns, "namespace");
|
||||
|
||||
MyContext.AssertTypeIsAccessible(mi.ReflectedType);
|
||||
|
||||
if (mi.IsStatic == false | mi.IsPublic == false)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.OnlyPublicStaticMethodsCanBeImported);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
|
||||
NamespaceImport import = this.GetImport(ns);
|
||||
import.Add(new MethodImport(mi));
|
||||
}
|
||||
|
||||
public void ImportBuiltinTypes()
|
||||
{
|
||||
foreach (KeyValuePair<string, Type> pair in OurBuiltinTypeMap)
|
||||
{
|
||||
this.AddType(pair.Value, pair.Key);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Properties - Public"
|
||||
public NamespaceImport RootImport => MyRootImport;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
207
PublicTypes/ExpressionOptions.cs
Normal file
207
PublicTypes/ExpressionOptions.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System.Reflection;
|
||||
using System.Globalization;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
public sealed class ExpressionOptions
|
||||
{
|
||||
|
||||
private PropertyDictionary _myProperties;
|
||||
private Type _myOwnerType;
|
||||
private readonly ExpressionContext _myOwner;
|
||||
internal event EventHandler CaseSensitiveChanged;
|
||||
|
||||
internal ExpressionOptions(ExpressionContext owner)
|
||||
{
|
||||
_myOwner = owner;
|
||||
_myProperties = new PropertyDictionary();
|
||||
|
||||
this.InitializeProperties();
|
||||
}
|
||||
|
||||
#region "Methods - Private"
|
||||
|
||||
private void InitializeProperties()
|
||||
{
|
||||
this.StringComparison = System.StringComparison.Ordinal;
|
||||
this.OwnerMemberAccess = BindingFlags.Public;
|
||||
|
||||
_myProperties.SetToDefault<bool>("CaseSensitive");
|
||||
_myProperties.SetToDefault<bool>("Checked");
|
||||
_myProperties.SetToDefault<bool>("EmitToAssembly");
|
||||
_myProperties.SetToDefault<Type>("ResultType");
|
||||
_myProperties.SetToDefault<bool>("IsGeneric");
|
||||
_myProperties.SetToDefault<bool>("IntegersAsDoubles");
|
||||
_myProperties.SetValue("ParseCulture", CultureInfo.CurrentCulture);
|
||||
this.SetParseCulture(this.ParseCulture);
|
||||
_myProperties.SetValue("RealLiteralDataType", RealLiteralDataType.Double);
|
||||
}
|
||||
|
||||
private void SetParseCulture(CultureInfo ci)
|
||||
{
|
||||
ExpressionParserOptions po = _myOwner.ParserOptions;
|
||||
po.DecimalSeparator = Convert.ToChar(ci.NumberFormat.NumberDecimalSeparator);
|
||||
po.FunctionArgumentSeparator = Convert.ToChar(ci.TextInfo.ListSeparator);
|
||||
po.DateTimeFormat = ci.DateTimeFormat.ShortDatePattern;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Methods - Internal"
|
||||
|
||||
internal ExpressionOptions Clone()
|
||||
{
|
||||
ExpressionOptions clonedOptions = (ExpressionOptions)this.MemberwiseClone();
|
||||
clonedOptions._myProperties = _myProperties.Clone();
|
||||
return clonedOptions;
|
||||
}
|
||||
|
||||
internal bool IsOwnerType(Type t)
|
||||
{
|
||||
return this._myOwnerType.IsAssignableFrom(t);
|
||||
}
|
||||
|
||||
internal void SetOwnerType(Type ownerType)
|
||||
{
|
||||
_myOwnerType = ownerType;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Properties - Public"
|
||||
public Type ResultType
|
||||
{
|
||||
get { return _myProperties.GetValue<Type>("ResultType"); }
|
||||
set
|
||||
{
|
||||
Utility.AssertNotNull(value, "value");
|
||||
_myProperties.SetValue("ResultType", value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Checked
|
||||
{
|
||||
get { return _myProperties.GetValue<bool>("Checked"); }
|
||||
set { _myProperties.SetValue("Checked", value); }
|
||||
}
|
||||
|
||||
public StringComparison StringComparison
|
||||
{
|
||||
get { return _myProperties.GetValue<StringComparison>("StringComparison"); }
|
||||
set { _myProperties.SetValue("StringComparison", value); }
|
||||
}
|
||||
|
||||
public bool EmitToAssembly
|
||||
{
|
||||
get { return _myProperties.GetValue<bool>("EmitToAssembly"); }
|
||||
set { _myProperties.SetValue("EmitToAssembly", value); }
|
||||
}
|
||||
|
||||
public BindingFlags OwnerMemberAccess
|
||||
{
|
||||
get { return _myProperties.GetValue<BindingFlags>("OwnerMemberAccess"); }
|
||||
set { _myProperties.SetValue("OwnerMemberAccess", value); }
|
||||
}
|
||||
|
||||
public bool CaseSensitive
|
||||
{
|
||||
get { return _myProperties.GetValue<bool>("CaseSensitive"); }
|
||||
set
|
||||
{
|
||||
if (this.CaseSensitive != value)
|
||||
{
|
||||
_myProperties.SetValue("CaseSensitive", value);
|
||||
if (CaseSensitiveChanged != null)
|
||||
{
|
||||
CaseSensitiveChanged(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IntegersAsDoubles
|
||||
{
|
||||
get { return _myProperties.GetValue<bool>("IntegersAsDoubles"); }
|
||||
set { _myProperties.SetValue("IntegersAsDoubles", value); }
|
||||
}
|
||||
|
||||
public CultureInfo ParseCulture
|
||||
{
|
||||
get { return _myProperties.GetValue<CultureInfo>("ParseCulture"); }
|
||||
set
|
||||
{
|
||||
Utility.AssertNotNull(value, "ParseCulture");
|
||||
if ((value.LCID != this.ParseCulture.LCID))
|
||||
{
|
||||
_myProperties.SetValue("ParseCulture", value);
|
||||
this.SetParseCulture(value);
|
||||
_myOwner.ParserOptions.RecreateParser();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RealLiteralDataType RealLiteralDataType
|
||||
{
|
||||
get { return _myProperties.GetValue<RealLiteralDataType>("RealLiteralDataType"); }
|
||||
set { _myProperties.SetValue("RealLiteralDataType", value); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Properties - Non Public"
|
||||
internal IEqualityComparer<string> StringComparer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.CaseSensitive == true)
|
||||
{
|
||||
return System.StringComparer.Ordinal;
|
||||
}
|
||||
else
|
||||
{
|
||||
return System.StringComparer.OrdinalIgnoreCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal MemberFilter MemberFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.CaseSensitive == true)
|
||||
{
|
||||
return Type.FilterName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Type.FilterNameIgnoreCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal StringComparison MemberStringComparison
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.CaseSensitive == true)
|
||||
{
|
||||
return System.StringComparison.Ordinal;
|
||||
}
|
||||
else
|
||||
{
|
||||
return System.StringComparison.OrdinalIgnoreCase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal Type OwnerType => _myOwnerType;
|
||||
|
||||
internal bool IsGeneric
|
||||
{
|
||||
get { return _myProperties.GetValue<bool>("IsGeneric"); }
|
||||
set { _myProperties.SetValue("IsGeneric", value); }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
99
PublicTypes/ExpressionParserOptions.cs
Normal file
99
PublicTypes/ExpressionParserOptions.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System.Globalization;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
public class ExpressionParserOptions
|
||||
{
|
||||
private PropertyDictionary _myProperties;
|
||||
private readonly ExpressionContext _myOwner;
|
||||
private readonly CultureInfo _myParseCulture;
|
||||
|
||||
private NumberStyles NumberStyles = NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.None;
|
||||
internal ExpressionParserOptions(ExpressionContext owner)
|
||||
{
|
||||
_myOwner = owner;
|
||||
_myProperties = new PropertyDictionary();
|
||||
_myParseCulture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
|
||||
this.InitializeProperties();
|
||||
}
|
||||
|
||||
#region "Methods - Public"
|
||||
|
||||
public void RecreateParser()
|
||||
{
|
||||
_myOwner.RecreateParser();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Methods - Internal"
|
||||
|
||||
internal ExpressionParserOptions Clone()
|
||||
{
|
||||
ExpressionParserOptions copy = (ExpressionParserOptions)this.MemberwiseClone();
|
||||
copy._myProperties = _myProperties.Clone();
|
||||
return copy;
|
||||
}
|
||||
|
||||
internal double ParseDouble(string image)
|
||||
{
|
||||
return double.Parse(image, NumberStyles, _myParseCulture);
|
||||
}
|
||||
|
||||
internal float ParseSingle(string image)
|
||||
{
|
||||
return float.Parse(image, NumberStyles, _myParseCulture);
|
||||
}
|
||||
|
||||
internal decimal ParseDecimal(string image)
|
||||
{
|
||||
return decimal.Parse(image, NumberStyles, _myParseCulture);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Methods - Private"
|
||||
|
||||
private void InitializeProperties()
|
||||
{
|
||||
this.DateTimeFormat = "dd/MM/yyyy";
|
||||
this.RequireDigitsBeforeDecimalPoint = false;
|
||||
this.DecimalSeparator = '.';
|
||||
this.FunctionArgumentSeparator = ',';
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Properties - Public"
|
||||
|
||||
public string DateTimeFormat
|
||||
{
|
||||
get { return _myProperties.GetValue<string>("DateTimeFormat"); }
|
||||
set { _myProperties.SetValue("DateTimeFormat", value); }
|
||||
}
|
||||
|
||||
public bool RequireDigitsBeforeDecimalPoint
|
||||
{
|
||||
get { return _myProperties.GetValue<bool>("RequireDigitsBeforeDecimalPoint"); }
|
||||
set { _myProperties.SetValue("RequireDigitsBeforeDecimalPoint", value); }
|
||||
}
|
||||
|
||||
public char DecimalSeparator
|
||||
{
|
||||
get { return _myProperties.GetValue<char>("DecimalSeparator"); }
|
||||
set
|
||||
{
|
||||
_myProperties.SetValue("DecimalSeparator", value);
|
||||
_myParseCulture.NumberFormat.NumberDecimalSeparator = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public char FunctionArgumentSeparator
|
||||
{
|
||||
get { return _myProperties.GetValue<char>("FunctionArgumentSeparator"); }
|
||||
set { _myProperties.SetValue("FunctionArgumentSeparator", value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
417
PublicTypes/ImportTypes.cs
Normal file
417
PublicTypes/ImportTypes.cs
Normal file
@@ -0,0 +1,417 @@
|
||||
using System.Reflection;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
public abstract class ImportBase : IEnumerable<ImportBase>, IEquatable<ImportBase>
|
||||
{
|
||||
private ExpressionContext _myContext;
|
||||
|
||||
internal ImportBase()
|
||||
{
|
||||
}
|
||||
|
||||
#region "Methods - Non Public"
|
||||
internal virtual void SetContext(ExpressionContext context)
|
||||
{
|
||||
_myContext = context;
|
||||
this.Validate();
|
||||
}
|
||||
|
||||
internal abstract void Validate();
|
||||
|
||||
protected abstract void AddMembers(string memberName, MemberTypes memberType, ICollection<MemberInfo> dest);
|
||||
protected abstract void AddMembers(MemberTypes memberType, ICollection<MemberInfo> dest);
|
||||
|
||||
internal ImportBase Clone()
|
||||
{
|
||||
return (ImportBase)this.MemberwiseClone();
|
||||
}
|
||||
|
||||
protected static void AddImportMembers(ImportBase import, string memberName, MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
import.AddMembers(memberName, memberType, dest);
|
||||
}
|
||||
|
||||
protected static void AddImportMembers(ImportBase import, MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
import.AddMembers(memberType, dest);
|
||||
}
|
||||
|
||||
protected static void AddMemberRange(ICollection<MemberInfo> members, ICollection<MemberInfo> dest)
|
||||
{
|
||||
foreach (MemberInfo mi in members)
|
||||
{
|
||||
dest.Add(mi);
|
||||
}
|
||||
}
|
||||
|
||||
protected bool AlwaysMemberFilter(MemberInfo member, object criteria)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
internal abstract bool IsMatch(string name);
|
||||
internal abstract Type FindType(string typename);
|
||||
|
||||
internal virtual ImportBase FindImport(string name)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
internal MemberInfo[] FindMembers(string memberName, MemberTypes memberType)
|
||||
{
|
||||
List<MemberInfo> found = new List<MemberInfo>();
|
||||
this.AddMembers(memberName, memberType, found);
|
||||
return found.ToArray();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Methods - Public"
|
||||
public MemberInfo[] GetMembers(MemberTypes memberType)
|
||||
{
|
||||
List<MemberInfo> found = new List<MemberInfo>();
|
||||
this.AddMembers(memberType, found);
|
||||
return found.ToArray();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "IEnumerable Implementation"
|
||||
public virtual System.Collections.Generic.IEnumerator<ImportBase> GetEnumerator()
|
||||
{
|
||||
List<ImportBase> coll = new List<ImportBase>();
|
||||
return coll.GetEnumerator();
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator GetEnumerator1()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator1();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "IEquatable Implementation"
|
||||
public bool Equals(ImportBase other)
|
||||
{
|
||||
return this.EqualsInternal(other);
|
||||
}
|
||||
|
||||
protected abstract bool EqualsInternal(ImportBase import);
|
||||
#endregion
|
||||
|
||||
#region "Properties - Protected"
|
||||
protected ExpressionContext Context => _myContext;
|
||||
|
||||
#endregion
|
||||
|
||||
#region "Properties - Public"
|
||||
public abstract string Name { get; }
|
||||
|
||||
public virtual bool IsContainer => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public sealed class TypeImport : ImportBase
|
||||
{
|
||||
private readonly Type _myType;
|
||||
private readonly BindingFlags _myBindFlags;
|
||||
private readonly bool _myUseTypeNameAsNamespace;
|
||||
public TypeImport(Type importType) : this(importType, false)
|
||||
{
|
||||
}
|
||||
|
||||
public TypeImport(Type importType, bool useTypeNameAsNamespace) : this(importType, BindingFlags.Public | BindingFlags.Static, useTypeNameAsNamespace)
|
||||
{
|
||||
}
|
||||
|
||||
#region "Methods - Non Public"
|
||||
internal TypeImport(Type t, BindingFlags flags, bool useTypeNameAsNamespace)
|
||||
{
|
||||
Utility.AssertNotNull(t, "t");
|
||||
_myType = t;
|
||||
_myBindFlags = flags;
|
||||
_myUseTypeNameAsNamespace = useTypeNameAsNamespace;
|
||||
}
|
||||
|
||||
internal override void Validate()
|
||||
{
|
||||
this.Context.AssertTypeIsAccessible(_myType);
|
||||
}
|
||||
|
||||
protected override void AddMembers(string memberName, MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
MemberInfo[] members = _myType.FindMembers(memberType, _myBindFlags, this.Context.Options.MemberFilter, memberName);
|
||||
ImportBase.AddMemberRange(members, dest);
|
||||
}
|
||||
|
||||
protected override void AddMembers(MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
if (_myUseTypeNameAsNamespace == false)
|
||||
{
|
||||
MemberInfo[] members = _myType.FindMembers(memberType, _myBindFlags, this.AlwaysMemberFilter, null);
|
||||
ImportBase.AddMemberRange(members, dest);
|
||||
}
|
||||
}
|
||||
|
||||
internal override bool IsMatch(string name)
|
||||
{
|
||||
if (_myUseTypeNameAsNamespace == true)
|
||||
{
|
||||
return string.Equals(_myType.Name, name, this.Context.Options.MemberStringComparison);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal override Type FindType(string typeName)
|
||||
{
|
||||
if (string.Equals(typeName, _myType.Name, this.Context.Options.MemberStringComparison) == true)
|
||||
{
|
||||
return _myType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool EqualsInternal(ImportBase import)
|
||||
{
|
||||
TypeImport otherSameType = import as TypeImport;
|
||||
return (otherSameType != null) && object.ReferenceEquals(_myType, otherSameType._myType);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Methods - Public"
|
||||
public override IEnumerator<ImportBase> GetEnumerator()
|
||||
{
|
||||
if (_myUseTypeNameAsNamespace == true)
|
||||
{
|
||||
List<ImportBase> coll = new List<ImportBase>();
|
||||
coll.Add(new TypeImport(_myType, false));
|
||||
return coll.GetEnumerator();
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.GetEnumerator();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region "Properties - Public"
|
||||
public override bool IsContainer => _myUseTypeNameAsNamespace;
|
||||
|
||||
public override string Name => _myType.Name;
|
||||
|
||||
public Type Target => _myType;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public sealed class MethodImport : ImportBase
|
||||
{
|
||||
|
||||
private readonly MethodInfo _myMethod;
|
||||
public MethodImport(MethodInfo importMethod)
|
||||
{
|
||||
Utility.AssertNotNull(importMethod, "importMethod");
|
||||
_myMethod = importMethod;
|
||||
}
|
||||
|
||||
internal override void Validate()
|
||||
{
|
||||
this.Context.AssertTypeIsAccessible(_myMethod.ReflectedType);
|
||||
}
|
||||
|
||||
protected override void AddMembers(string memberName, MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
if (string.Equals(memberName, _myMethod.Name, this.Context.Options.MemberStringComparison) == true && (memberType & MemberTypes.Method) != 0)
|
||||
{
|
||||
dest.Add(_myMethod);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddMembers(MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
if ((memberType & MemberTypes.Method) != 0)
|
||||
{
|
||||
dest.Add(_myMethod);
|
||||
}
|
||||
}
|
||||
|
||||
internal override bool IsMatch(string name)
|
||||
{
|
||||
return string.Equals(_myMethod.Name, name, this.Context.Options.MemberStringComparison);
|
||||
}
|
||||
|
||||
internal override Type FindType(string typeName)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override bool EqualsInternal(ImportBase import)
|
||||
{
|
||||
MethodImport otherSameType = import as MethodImport;
|
||||
return (otherSameType != null) && _myMethod.MethodHandle.Equals(otherSameType._myMethod.MethodHandle);
|
||||
}
|
||||
|
||||
public override string Name => _myMethod.Name;
|
||||
|
||||
public MethodInfo Target => _myMethod;
|
||||
}
|
||||
|
||||
public sealed class NamespaceImport : ImportBase, ICollection<ImportBase>
|
||||
{
|
||||
private readonly string _myNamespace;
|
||||
private readonly List<ImportBase> _myImports;
|
||||
public NamespaceImport(string importNamespace)
|
||||
{
|
||||
Utility.AssertNotNull(importNamespace, "importNamespace");
|
||||
if (importNamespace.Length == 0)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.InvalidNamespaceName);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
|
||||
_myNamespace = importNamespace;
|
||||
_myImports = new List<ImportBase>();
|
||||
}
|
||||
|
||||
internal override void SetContext(ExpressionContext context)
|
||||
{
|
||||
base.SetContext(context);
|
||||
|
||||
foreach (ImportBase import in _myImports)
|
||||
{
|
||||
import.SetContext(context);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void Validate()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void AddMembers(string memberName, MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
foreach (ImportBase import in this.NonContainerImports)
|
||||
{
|
||||
AddImportMembers(import, memberName, memberType, dest);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddMembers(MemberTypes memberType, ICollection<MemberInfo> dest)
|
||||
{
|
||||
}
|
||||
|
||||
internal override Type FindType(string typeName)
|
||||
{
|
||||
foreach (ImportBase import in this.NonContainerImports)
|
||||
{
|
||||
Type t = import.FindType(typeName);
|
||||
|
||||
if ((t != null))
|
||||
{
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal override ImportBase FindImport(string name)
|
||||
{
|
||||
foreach (ImportBase import in _myImports)
|
||||
{
|
||||
if (import.IsMatch(name) == true)
|
||||
{
|
||||
return import;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal override bool IsMatch(string name)
|
||||
{
|
||||
return string.Equals(_myNamespace, name, this.Context.Options.MemberStringComparison);
|
||||
}
|
||||
|
||||
private ICollection<ImportBase> NonContainerImports
|
||||
{
|
||||
get
|
||||
{
|
||||
List<ImportBase> found = new List<ImportBase>();
|
||||
|
||||
foreach (ImportBase import in _myImports)
|
||||
{
|
||||
if (import.IsContainer == false)
|
||||
{
|
||||
found.Add(import);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool EqualsInternal(ImportBase import)
|
||||
{
|
||||
NamespaceImport otherSameType = import as NamespaceImport;
|
||||
return (otherSameType != null) && _myNamespace.Equals(otherSameType._myNamespace, this.Context.Options.MemberStringComparison);
|
||||
}
|
||||
|
||||
public override bool IsContainer => true;
|
||||
|
||||
public override string Name => _myNamespace;
|
||||
|
||||
#region "ICollection implementation"
|
||||
public void Add(ImportBase item)
|
||||
{
|
||||
Utility.AssertNotNull(item, "item");
|
||||
|
||||
if ((this.Context != null))
|
||||
{
|
||||
item.SetContext(this.Context);
|
||||
}
|
||||
|
||||
_myImports.Add(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_myImports.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(ImportBase item)
|
||||
{
|
||||
return _myImports.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo(ImportBase[] array, int arrayIndex)
|
||||
{
|
||||
_myImports.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(ImportBase item)
|
||||
{
|
||||
return _myImports.Remove(item);
|
||||
}
|
||||
|
||||
public override System.Collections.Generic.IEnumerator<ImportBase> GetEnumerator()
|
||||
{
|
||||
return _myImports.GetEnumerator();
|
||||
}
|
||||
|
||||
public int Count => _myImports.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
177
PublicTypes/Miscellaneous.cs
Normal file
177
PublicTypes/Miscellaneous.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
public interface IExpression
|
||||
{
|
||||
IExpression Clone();
|
||||
string Text { get; }
|
||||
ExpressionInfo Info { get; }
|
||||
ExpressionContext Context { get; }
|
||||
object Owner { get; set; }
|
||||
}
|
||||
|
||||
public interface IDynamicExpression : IExpression
|
||||
{
|
||||
object Evaluate();
|
||||
}
|
||||
|
||||
public interface IGenericExpression<T> : IExpression
|
||||
{
|
||||
T Evaluate();
|
||||
}
|
||||
|
||||
public sealed class ExpressionInfo
|
||||
{
|
||||
|
||||
|
||||
private readonly IDictionary<string, object> _myData;
|
||||
internal ExpressionInfo()
|
||||
{
|
||||
_myData = new Dictionary<string, object>
|
||||
{
|
||||
{"ReferencedVariables", new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)}
|
||||
};
|
||||
}
|
||||
|
||||
internal void AddReferencedVariable(string name)
|
||||
{
|
||||
IDictionary<string, string> dict = (IDictionary<string, string>)_myData["ReferencedVariables"];
|
||||
dict[name] = name;
|
||||
}
|
||||
|
||||
public string[] GetReferencedVariables()
|
||||
{
|
||||
IDictionary<string, string> dict = (IDictionary<string, string>)_myData["ReferencedVariables"];
|
||||
string[] arr = new string[dict.Count];
|
||||
dict.Keys.CopyTo(arr, 0);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class ExpressionOwnerMemberAccessAttribute : Attribute
|
||||
{
|
||||
|
||||
|
||||
private readonly bool _myAllowAccess;
|
||||
public ExpressionOwnerMemberAccessAttribute(bool allowAccess)
|
||||
{
|
||||
_myAllowAccess = allowAccess;
|
||||
}
|
||||
|
||||
internal bool AllowAccess => _myAllowAccess;
|
||||
}
|
||||
|
||||
public class ResolveVariableTypeEventArgs : EventArgs
|
||||
{
|
||||
private readonly string _myName;
|
||||
private Type _myType;
|
||||
internal ResolveVariableTypeEventArgs(string name)
|
||||
{
|
||||
this._myName = name;
|
||||
}
|
||||
|
||||
public string VariableName => _myName;
|
||||
|
||||
public Type VariableType
|
||||
{
|
||||
get { return _myType; }
|
||||
set { _myType = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ResolveVariableValueEventArgs : EventArgs
|
||||
{
|
||||
private readonly string _myName;
|
||||
private readonly Type _myType;
|
||||
|
||||
private object MyValue;
|
||||
internal ResolveVariableValueEventArgs(string name, Type t)
|
||||
{
|
||||
_myName = name;
|
||||
_myType = t;
|
||||
}
|
||||
|
||||
public string VariableName
|
||||
{
|
||||
get { return _myName; }
|
||||
}
|
||||
|
||||
public Type VariableType
|
||||
{
|
||||
get { return _myType; }
|
||||
}
|
||||
|
||||
public object VariableValue
|
||||
{
|
||||
get { return MyValue; }
|
||||
set { MyValue = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class ResolveFunctionEventArgs : EventArgs
|
||||
{
|
||||
|
||||
private readonly string MyName;
|
||||
private readonly Type[] MyArgumentTypes;
|
||||
|
||||
private Type _myReturnType;
|
||||
internal ResolveFunctionEventArgs(string name, Type[] argumentTypes)
|
||||
{
|
||||
MyName = name;
|
||||
MyArgumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public string FunctionName
|
||||
{
|
||||
get { return MyName; }
|
||||
}
|
||||
|
||||
public Type[] ArgumentTypes
|
||||
{
|
||||
get { return MyArgumentTypes; }
|
||||
}
|
||||
|
||||
public Type ReturnType
|
||||
{
|
||||
get { return _myReturnType; }
|
||||
set { _myReturnType = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public class InvokeFunctionEventArgs : EventArgs
|
||||
{
|
||||
|
||||
private readonly string _myName;
|
||||
private readonly object[] _myArguments;
|
||||
|
||||
private object _myFunctionResult;
|
||||
internal InvokeFunctionEventArgs(string name, object[] arguments)
|
||||
{
|
||||
_myName = name;
|
||||
_myArguments = arguments;
|
||||
}
|
||||
|
||||
public string FunctionName
|
||||
{
|
||||
get { return _myName; }
|
||||
}
|
||||
|
||||
public object[] Arguments
|
||||
{
|
||||
get { return _myArguments; }
|
||||
}
|
||||
|
||||
public object Result
|
||||
{
|
||||
get { return _myFunctionResult; }
|
||||
set { _myFunctionResult = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public enum RealLiteralDataType
|
||||
{
|
||||
Single,
|
||||
Double,
|
||||
Decimal
|
||||
}
|
||||
}
|
404
PublicTypes/VariableCollection.cs
Normal file
404
PublicTypes/VariableCollection.cs
Normal file
@@ -0,0 +1,404 @@
|
||||
using Flee.InternalTypes;
|
||||
using Flee.Resources;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Flee.PublicTypes
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public sealed class VariableCollection : IDictionary<string, object>
|
||||
{
|
||||
private IDictionary<string, IVariable> _myVariables;
|
||||
private readonly ExpressionContext _myContext;
|
||||
|
||||
public event EventHandler<ResolveVariableTypeEventArgs> ResolveVariableType;
|
||||
|
||||
public event EventHandler<ResolveVariableValueEventArgs> ResolveVariableValue;
|
||||
|
||||
public event EventHandler<ResolveFunctionEventArgs> ResolveFunction;
|
||||
|
||||
public event EventHandler<InvokeFunctionEventArgs> InvokeFunction;
|
||||
|
||||
internal VariableCollection(ExpressionContext context)
|
||||
{
|
||||
_myContext = context;
|
||||
this.CreateDictionary();
|
||||
this.HookOptions();
|
||||
}
|
||||
|
||||
#region "Methods - Non Public"
|
||||
|
||||
private void HookOptions()
|
||||
{
|
||||
_myContext.Options.CaseSensitiveChanged += OnOptionsCaseSensitiveChanged;
|
||||
}
|
||||
|
||||
private void CreateDictionary()
|
||||
{
|
||||
_myVariables = new Dictionary<string, IVariable>(_myContext.Options.StringComparer);
|
||||
}
|
||||
|
||||
private void OnOptionsCaseSensitiveChanged(object sender, EventArgs e)
|
||||
{
|
||||
this.CreateDictionary();
|
||||
}
|
||||
|
||||
internal void Copy(VariableCollection dest)
|
||||
{
|
||||
dest.CreateDictionary();
|
||||
dest.HookOptions();
|
||||
|
||||
foreach (KeyValuePair<string, IVariable> pair in _myVariables)
|
||||
{
|
||||
IVariable copyVariable = pair.Value.Clone();
|
||||
dest._myVariables.Add(pair.Key, copyVariable);
|
||||
}
|
||||
}
|
||||
|
||||
internal void DefineVariableInternal(string name, Type variableType, object variableValue)
|
||||
{
|
||||
Utility.AssertNotNull(variableType, "variableType");
|
||||
|
||||
if (_myVariables.ContainsKey(name) == true)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.VariableWithNameAlreadyDefined, name);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
|
||||
IVariable v = this.CreateVariable(variableType, variableValue);
|
||||
_myVariables.Add(name, v);
|
||||
}
|
||||
|
||||
internal Type GetVariableTypeInternal(string name)
|
||||
{
|
||||
IVariable value = null;
|
||||
bool success = _myVariables.TryGetValue(name, out value);
|
||||
|
||||
if (success == true)
|
||||
{
|
||||
return value.VariableType;
|
||||
}
|
||||
|
||||
ResolveVariableTypeEventArgs args = new ResolveVariableTypeEventArgs(name);
|
||||
ResolveVariableType?.Invoke(this, args);
|
||||
|
||||
return args.VariableType;
|
||||
}
|
||||
|
||||
private IVariable GetVariable(string name, bool throwOnNotFound)
|
||||
{
|
||||
IVariable value = null;
|
||||
bool success = _myVariables.TryGetValue(name, out value);
|
||||
|
||||
if (success == false & throwOnNotFound == true)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.UndefinedVariable, name);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private IVariable CreateVariable(Type variableValueType, object variableValue)
|
||||
{
|
||||
Type variableType = default(Type);
|
||||
|
||||
// Is the variable value an expression?
|
||||
IExpression expression = variableValue as IExpression;
|
||||
ExpressionOptions options = null;
|
||||
|
||||
if (expression != null)
|
||||
{
|
||||
options = expression.Context.Options;
|
||||
// Get its result type
|
||||
variableValueType = options.ResultType;
|
||||
|
||||
// Create a variable that wraps the expression
|
||||
|
||||
if (options.IsGeneric == false)
|
||||
{
|
||||
variableType = typeof(DynamicExpressionVariable<>);
|
||||
}
|
||||
else
|
||||
{
|
||||
variableType = typeof(GenericExpressionVariable<>);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a variable for a regular value
|
||||
_myContext.AssertTypeIsAccessible(variableValueType);
|
||||
variableType = typeof(GenericVariable<>);
|
||||
}
|
||||
|
||||
// Create the generic variable instance
|
||||
variableType = variableType.MakeGenericType(variableValueType);
|
||||
IVariable v = (IVariable)Activator.CreateInstance(variableType);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
internal Type ResolveOnDemandFunction(string name, Type[] argumentTypes)
|
||||
{
|
||||
ResolveFunctionEventArgs args = new ResolveFunctionEventArgs(name, argumentTypes);
|
||||
ResolveFunction?.Invoke(this, args);
|
||||
return args.ReturnType;
|
||||
}
|
||||
|
||||
private static T ReturnGenericValue<T>(object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateSetValueType(Type requiredType, object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
// Can always assign null value
|
||||
return;
|
||||
}
|
||||
|
||||
Type valueType = value.GetType();
|
||||
|
||||
if (requiredType.IsAssignableFrom(valueType) == false)
|
||||
{
|
||||
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.VariableValueNotAssignableToType, valueType.Name, requiredType.Name);
|
||||
throw new ArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
internal static MethodInfo GetVariableLoadMethod(Type variableType)
|
||||
{
|
||||
MethodInfo mi = typeof(VariableCollection).GetMethod("GetVariableValueInternal", BindingFlags.Public | BindingFlags.Instance);
|
||||
mi = mi.MakeGenericMethod(variableType);
|
||||
return mi;
|
||||
}
|
||||
|
||||
internal static MethodInfo GetFunctionInvokeMethod(Type returnType)
|
||||
{
|
||||
MethodInfo mi = typeof(VariableCollection).GetMethod("GetFunctionResultInternal", BindingFlags.Public | BindingFlags.Instance);
|
||||
mi = mi.MakeGenericMethod(returnType);
|
||||
return mi;
|
||||
}
|
||||
|
||||
internal static MethodInfo GetVirtualPropertyLoadMethod(Type returnType)
|
||||
{
|
||||
MethodInfo mi = typeof(VariableCollection).GetMethod("GetVirtualPropertyValueInternal", BindingFlags.Public | BindingFlags.Instance);
|
||||
mi = mi.MakeGenericMethod(returnType);
|
||||
return mi;
|
||||
}
|
||||
|
||||
private Dictionary<string, object> GetNameValueDictionary()
|
||||
{
|
||||
Dictionary<string, object> dict = new Dictionary<string, object>();
|
||||
|
||||
foreach (KeyValuePair<string, IVariable> pair in _myVariables)
|
||||
{
|
||||
dict.Add(pair.Key, pair.Value.ValueAsObject);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
#endregion "Methods - Non Public"
|
||||
|
||||
#region "Methods - Public"
|
||||
|
||||
public Type GetVariableType(string name)
|
||||
{
|
||||
IVariable v = this.GetVariable(name, true);
|
||||
return v.VariableType;
|
||||
}
|
||||
|
||||
public void DefineVariable(string name, Type variableType)
|
||||
{
|
||||
this.DefineVariableInternal(name, variableType, null);
|
||||
}
|
||||
|
||||
public T GetVariableValueInternal<T>(string name)
|
||||
{
|
||||
if (_myVariables.TryGetValue(name, out IVariable variable))
|
||||
{
|
||||
if (variable is IGenericVariable<T> generic)
|
||||
{
|
||||
return (T)generic.GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
GenericVariable<T> result = new GenericVariable<T>();
|
||||
GenericVariable<T> vTemp = new GenericVariable<T>();
|
||||
ResolveVariableValueEventArgs args = new ResolveVariableValueEventArgs(name, typeof(T));
|
||||
ResolveVariableValue?.Invoke(this, args);
|
||||
|
||||
ValidateSetValueType(typeof(T), args.VariableValue);
|
||||
vTemp.ValueAsObject = args.VariableValue;
|
||||
result = vTemp;
|
||||
return (T)result.GetValue();
|
||||
}
|
||||
|
||||
public T GetVirtualPropertyValueInternal<T>(string name, object component)
|
||||
{
|
||||
PropertyDescriptorCollection coll = TypeDescriptor.GetProperties(component);
|
||||
PropertyDescriptor pd = coll.Find(name, true);
|
||||
|
||||
object value = pd.GetValue(component);
|
||||
ValidateSetValueType(typeof(T), value);
|
||||
return ReturnGenericValue<T>(value);
|
||||
}
|
||||
|
||||
public T GetFunctionResultInternal<T>(string name, object[] arguments)
|
||||
{
|
||||
InvokeFunctionEventArgs args = new InvokeFunctionEventArgs(name, arguments);
|
||||
if (InvokeFunction != null)
|
||||
{
|
||||
InvokeFunction(this, args);
|
||||
}
|
||||
|
||||
object result = args.Result;
|
||||
ValidateSetValueType(typeof(T), result);
|
||||
|
||||
return ReturnGenericValue<T>(result);
|
||||
}
|
||||
|
||||
#endregion "Methods - Public"
|
||||
|
||||
#region "IDictionary Implementation"
|
||||
|
||||
private void Add1(System.Collections.Generic.KeyValuePair<string, object> item)
|
||||
{
|
||||
this.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
void System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>.Add(System.Collections.Generic.KeyValuePair<string, object> item)
|
||||
{
|
||||
Add1(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_myVariables.Clear();
|
||||
}
|
||||
|
||||
private bool Contains1(System.Collections.Generic.KeyValuePair<string, object> item)
|
||||
{
|
||||
return this.ContainsKey(item.Key);
|
||||
}
|
||||
|
||||
bool System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>.Contains(System.Collections.Generic.KeyValuePair<string, object> item)
|
||||
{
|
||||
return Contains1(item);
|
||||
}
|
||||
|
||||
private void CopyTo(System.Collections.Generic.KeyValuePair<string, object>[] array, int arrayIndex)
|
||||
{
|
||||
Dictionary<string, object> dict = this.GetNameValueDictionary();
|
||||
ICollection<KeyValuePair<string, object>> coll = dict;
|
||||
coll.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
private bool Remove1(System.Collections.Generic.KeyValuePair<string, object> item)
|
||||
{
|
||||
return this.Remove(item.Key);
|
||||
}
|
||||
|
||||
bool System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<string, object>>.Remove(System.Collections.Generic.KeyValuePair<string, object> item)
|
||||
{
|
||||
return Remove1(item);
|
||||
}
|
||||
|
||||
public void Add(string name, object value)
|
||||
{
|
||||
Utility.AssertNotNull(value, "value");
|
||||
this.DefineVariableInternal(name, value.GetType(), value);
|
||||
this[name] = value;
|
||||
}
|
||||
|
||||
public bool ContainsKey(string name)
|
||||
{
|
||||
return _myVariables.ContainsKey(name);
|
||||
}
|
||||
|
||||
public bool Remove(string name)
|
||||
{
|
||||
return _myVariables.Remove(name);
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, out object value)
|
||||
{
|
||||
IVariable v = this.GetVariable(key, false);
|
||||
value = v?.ValueAsObject;
|
||||
return v != null;
|
||||
}
|
||||
|
||||
public System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<string, object>> GetEnumerator()
|
||||
{
|
||||
Dictionary<string, object> dict = this.GetNameValueDictionary();
|
||||
return dict.GetEnumerator();
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator GetEnumerator1()
|
||||
{
|
||||
return this.GetEnumerator();
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator1();
|
||||
}
|
||||
|
||||
public int Count => _myVariables.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public object this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
IVariable v = this.GetVariable(name, true);
|
||||
return v.ValueAsObject;
|
||||
}
|
||||
set
|
||||
{
|
||||
IVariable v = null;
|
||||
|
||||
if (_myVariables.TryGetValue(name, out v) == true)
|
||||
{
|
||||
v.ValueAsObject = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Add(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public System.Collections.Generic.ICollection<string> Keys => _myVariables.Keys;
|
||||
|
||||
public System.Collections.Generic.ICollection<object> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
Dictionary<string, object> dict = this.GetNameValueDictionary();
|
||||
return dict.Values;
|
||||
}
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
|
||||
{
|
||||
CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
#endregion "IDictionary Implementation"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user