This commit is contained in:
2025-10-08 09:49:37 +08:00
commit 284e764345
99 changed files with 21742 additions and 0 deletions

67
PublicTypes/Exceptions.cs Normal file
View 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;
}
}

View 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
}
}

View 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
}
}

View 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
}
}

View 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
View 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
}
}

View 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
}
}

View 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"
}
}