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

View File

@@ -0,0 +1,350 @@
using Flee.CalcEngine.PublicTypes;
namespace Flee.CalcEngine.InternalTypes
{
/// <summary>
/// Keeps track of our dependencies
/// </summary>
/// <typeparam name="T"></typeparam>
internal class DependencyManager<T>
{
/// <summary>
/// Map of a node and the nodes that depend on it
/// </summary>
private readonly Dictionary<T, Dictionary<T, object>> _myDependentsMap;
private readonly IEqualityComparer<T> _myEqualityComparer;
/// <summary>
/// Map of a node and the number of nodes that point to it
/// </summary>
private readonly Dictionary<T, int> _myPrecedentsMap;
public DependencyManager(IEqualityComparer<T> comparer)
{
_myEqualityComparer = comparer;
_myDependentsMap = new Dictionary<T, Dictionary<T, object>>(_myEqualityComparer);
_myPrecedentsMap = new Dictionary<T, int>(_myEqualityComparer);
}
private IDictionary<T, object> CreateInnerDictionary()
{
return new Dictionary<T, object>(_myEqualityComparer);
}
private IDictionary<T, object> GetInnerDictionary(T tail)
{
Dictionary<T, object> value = null;
if (_myDependentsMap.TryGetValue(tail, out value) == true)
{
return value;
}
else
{
return null;
}
}
// Create a dependency list with only the dependents of the given tails
public DependencyManager<T> CloneDependents(T[] tails)
{
IDictionary<T, object> seenNodes = this.CreateInnerDictionary();
DependencyManager<T> copy = new DependencyManager<T>(_myEqualityComparer);
foreach (T tail in tails)
{
this.CloneDependentsInternal(tail, copy, seenNodes);
}
return copy;
}
private void CloneDependentsInternal(T tail, DependencyManager<T> target, IDictionary<T, object> seenNodes)
{
if (seenNodes.ContainsKey(tail) == true)
{
// We've already added this node so just return
return;
}
else
{
// Haven't seen this node yet; mark it as visited
seenNodes.Add(tail, null);
target.AddTail(tail);
}
IDictionary<T, object> innerDict = this.GetInnerDictionary(tail);
// Do the recursive add
foreach (T head in innerDict.Keys)
{
target.AddDepedency(tail, head);
this.CloneDependentsInternal(head, target, seenNodes);
}
}
public T[] GetTails()
{
T[] arr = new T[_myDependentsMap.Keys.Count];
_myDependentsMap.Keys.CopyTo(arr, 0);
return arr;
}
public void Clear()
{
_myDependentsMap.Clear();
_myPrecedentsMap.Clear();
}
public void ReplaceDependency(T old, T replaceWith)
{
Dictionary<T, object> value = _myDependentsMap[old];
_myDependentsMap.Remove(old);
_myDependentsMap.Add(replaceWith, value);
foreach (Dictionary<T, object> innerDict in _myDependentsMap.Values)
{
if (innerDict.ContainsKey(old) == true)
{
innerDict.Remove(old);
innerDict.Add(replaceWith, null);
}
}
}
public void AddTail(T tail)
{
if (_myDependentsMap.ContainsKey(tail) == false)
{
_myDependentsMap.Add(tail, (Dictionary<T, object>)this.CreateInnerDictionary());
}
}
public void AddDepedency(T tail, T head)
{
IDictionary<T, object> innerDict = this.GetInnerDictionary(tail);
if (innerDict.ContainsKey(head) == false)
{
innerDict.Add(head, head);
this.AddPrecedent(head);
}
}
public void RemoveDependency(T tail, T head)
{
IDictionary<T, object> innerDict = this.GetInnerDictionary(tail);
this.RemoveHead(head, innerDict);
}
private void RemoveHead(T head, IDictionary<T, object> dict)
{
if (dict.Remove(head) == true)
{
this.RemovePrecedent(head);
}
}
public void Remove(T[] tails)
{
foreach (Dictionary<T, object> innerDict in _myDependentsMap.Values)
{
foreach (T tail in tails)
{
this.RemoveHead(tail, innerDict);
}
}
foreach (T tail in tails)
{
_myDependentsMap.Remove(tail);
}
}
public void GetDirectDependents(T tail, List<T> dest)
{
Dictionary<T, object> innerDict = (Dictionary<T, object>)this.GetInnerDictionary(tail);
dest.AddRange(innerDict.Keys);
}
public T[] GetDependents(T tail)
{
Dictionary<T, object> dependents = (Dictionary<T, object>)this.CreateInnerDictionary();
this.GetDependentsRecursive(tail, dependents);
T[] arr = new T[dependents.Count];
dependents.Keys.CopyTo(arr, 0);
return arr;
}
private void GetDependentsRecursive(T tail, Dictionary<T, object> dependents)
{
dependents[tail] = null;
Dictionary<T, object> directDependents = (Dictionary<T, object>)this.GetInnerDictionary(tail);
foreach (T pair in directDependents.Keys)
{
this.GetDependentsRecursive(pair, dependents);
}
}
public void GetDirectPrecedents(T head, IList<T> dest)
{
foreach (T tail in _myDependentsMap.Keys)
{
Dictionary<T, object> innerDict = (Dictionary<T, object>)this.GetInnerDictionary(tail);
if (innerDict.ContainsKey(head) == true)
{
dest.Add(tail);
}
}
}
private void AddPrecedent(T head)
{
int count = 0;
_myPrecedentsMap.TryGetValue(head, out count);
_myPrecedentsMap[head] = count + 1;
}
private void RemovePrecedent(T head)
{
int count = _myPrecedentsMap[head] - 1;
if (count == 0)
{
_myPrecedentsMap.Remove(head);
}
else
{
_myPrecedentsMap[head] = count;
}
}
public bool HasPrecedents(T head)
{
return _myPrecedentsMap.ContainsKey(head);
}
public bool HasDependents(T tail)
{
Dictionary<T, object> innerDict = (Dictionary<T, object>)this.GetInnerDictionary(tail);
return innerDict.Count > 0;
}
private string FormatValues(ICollection<T> values)
{
string[] strings = new string[values.Count];
T[] keys = new T[values.Count];
values.CopyTo(keys, 0);
for (int i = 0; i <= keys.Length - 1; i++)
{
strings[i] = keys[i].ToString();
}
if (strings.Length == 0)
{
return "<empty>";
}
else
{
return string.Join(",", strings);
}
}
/// <summary>
/// Add all nodes that don't have any incoming edges into a queue
/// </summary>
/// <param name="rootTails"></param>
/// <returns></returns>
public Queue<T> GetSources(T[] rootTails)
{
Queue<T> q = new Queue<T>();
foreach (T rootTail in rootTails)
{
if (this.HasPrecedents(rootTail) == false)
{
q.Enqueue(rootTail);
}
}
return q;
}
public IList<T> TopologicalSort(Queue<T> sources)
{
IList<T> output = new List<T>();
List<T> directDependents = new List<T>();
while (sources.Count > 0)
{
T n = sources.Dequeue();
output.Add(n);
directDependents.Clear();
this.GetDirectDependents(n, directDependents);
foreach (T m in directDependents)
{
this.RemoveDependency(n, m);
if (this.HasPrecedents(m) == false)
{
sources.Enqueue(m);
}
}
}
if (output.Count != this.Count)
{
throw new CircularReferenceException();
}
return output;
}
#if DEBUG
public string Precedents
{
get
{
List<string> list = new List<string>();
foreach (KeyValuePair<T, int> pair in _myPrecedentsMap)
{
list.Add(pair.ToString());
}
return string.Join(System.Environment.NewLine, list.ToArray());
}
}
#endif
public string DependencyGraph
{
get
{
string[] lines = new string[_myDependentsMap.Count];
int index = 0;
foreach (KeyValuePair<T, Dictionary<T, object>> pair in _myDependentsMap)
{
T key = pair.Key;
string s = this.FormatValues(pair.Value.Keys);
lines[index] = $"{key} -> {s}";
index += 1;
}
return string.Join(System.Environment.NewLine, lines);
}
}
public int Count => _myDependentsMap.Count;
}
}

View File

@@ -0,0 +1,105 @@
using Flee.Parsing;
using Flee.PublicTypes;
namespace Flee.CalcEngine.InternalTypes
{
internal class IdentifierAnalyzer : Analyzer
{
private readonly IDictionary<int, string> _myIdentifiers;
private int _myMemberExpressionCount;
private bool _myInFieldPropertyExpression;
public IdentifierAnalyzer()
{
_myIdentifiers = new Dictionary<int, string>();
}
public override Node Exit(Node node)
{
switch (node.Id)
{
case (int)ExpressionConstants.IDENTIFIER:
this.ExitIdentifier((Token)node);
break;
case (int)ExpressionConstants.FIELD_PROPERTY_EXPRESSION:
this.ExitFieldPropertyExpression();
break;
}
return node;
}
public override void Enter(Node node)
{
switch (node.Id)
{
case (int)ExpressionConstants.MEMBER_EXPRESSION:
this.EnterMemberExpression();
break;
case (int)ExpressionConstants.FIELD_PROPERTY_EXPRESSION:
this.EnterFieldPropertyExpression();
break;
}
}
private void ExitIdentifier(Token node)
{
if (_myInFieldPropertyExpression == false)
{
return;
}
if (_myIdentifiers.ContainsKey(_myMemberExpressionCount) == false)
{
_myIdentifiers.Add(_myMemberExpressionCount, node.Image);
}
}
private void EnterMemberExpression()
{
_myMemberExpressionCount += 1;
}
private void EnterFieldPropertyExpression()
{
_myInFieldPropertyExpression = true;
}
private void ExitFieldPropertyExpression()
{
_myInFieldPropertyExpression = false;
}
public override void Reset()
{
_myIdentifiers.Clear();
_myMemberExpressionCount = -1;
}
public ICollection<string> GetIdentifiers(ExpressionContext context)
{
Dictionary<string, object> dict = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
ExpressionImports ei = context.Imports;
foreach (string identifier in _myIdentifiers.Values)
{
// Skip names registered as namespaces
if (ei.HasNamespace(identifier) == true)
{
continue;
}
else if (context.Variables.ContainsKey(identifier) == true)
{
// Identifier is a variable
continue;
}
// Get only the unique values
dict[identifier] = null;
}
return dict.Keys;
}
}
}

View File

@@ -0,0 +1,114 @@
using Flee.PublicTypes;
namespace Flee.CalcEngine.InternalTypes
{
internal class PairEqualityComparer : EqualityComparer<ExpressionResultPair>
{
public override bool Equals(ExpressionResultPair x, ExpressionResultPair y)
{
return string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase);
}
public override int GetHashCode(ExpressionResultPair obj)
{
return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Name);
}
}
internal abstract class ExpressionResultPair
{
private string _myName;
protected IDynamicExpression MyExpression;
protected ExpressionResultPair()
{
}
public abstract void Recalculate();
public void SetExpression(IDynamicExpression e)
{
MyExpression = e;
}
public void SetName(string name)
{
_myName = name;
}
public override string ToString()
{
return _myName;
}
public string Name => _myName;
public abstract Type ResultType { get; }
public abstract object ResultAsObject { get; set; }
public IDynamicExpression Expression => MyExpression;
}
internal class GenericExpressionResultPair<T> : ExpressionResultPair
{
public T MyResult;
public GenericExpressionResultPair()
{
}
public override void Recalculate()
{
MyResult = (T)MyExpression.Evaluate();
}
public T Result => MyResult;
public override System.Type ResultType => typeof(T);
public override object ResultAsObject
{
get { return MyResult; }
set { MyResult = (T)value; }
}
}
internal class BatchLoadInfo
{
public string Name;
public string ExpressionText;
public ExpressionContext Context;
public BatchLoadInfo(string name, string text, ExpressionContext context)
{
this.Name = name;
this.ExpressionText = text;
this.Context = context;
}
}
public sealed class NodeEventArgs : EventArgs
{
private string _myName;
private object _myResult;
internal NodeEventArgs()
{
}
internal void SetData(string name, object result)
{
_myName = name;
_myResult = result;
}
public string Name => _myName;
public object Result => _myResult;
}
}

View File

@@ -0,0 +1,69 @@
using Flee.CalcEngine.InternalTypes;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.CalcEngine.PublicTypes
{
public sealed class BatchLoader
{
private readonly IDictionary<string, BatchLoadInfo> _myNameInfoMap;
private readonly DependencyManager<string> _myDependencies;
internal BatchLoader()
{
_myNameInfoMap = new Dictionary<string, BatchLoadInfo>(StringComparer.OrdinalIgnoreCase);
_myDependencies = new DependencyManager<string>(StringComparer.OrdinalIgnoreCase);
}
public void Add(string atomName, string expression, ExpressionContext context)
{
Utility.AssertNotNull(atomName, "atomName");
Utility.AssertNotNull(expression, "expression");
Utility.AssertNotNull(context, "context");
BatchLoadInfo info = new BatchLoadInfo(atomName, expression, context);
_myNameInfoMap.Add(atomName, info);
_myDependencies.AddTail(atomName);
ICollection<string> references = this.GetReferences(expression, context);
foreach (string reference in references)
{
_myDependencies.AddTail(reference);
_myDependencies.AddDepedency(reference, atomName);
}
}
public bool Contains(string atomName)
{
return _myNameInfoMap.ContainsKey(atomName);
}
internal BatchLoadInfo[] GetBachInfos()
{
string[] tails = _myDependencies.GetTails();
Queue<string> sources = _myDependencies.GetSources(tails);
IList<string> result = _myDependencies.TopologicalSort(sources);
BatchLoadInfo[] infos = new BatchLoadInfo[result.Count];
for (int i = 0; i <= result.Count - 1; i++)
{
infos[i] = _myNameInfoMap[result[i]];
}
return infos;
}
private ICollection<string> GetReferences(string expression, ExpressionContext context)
{
IdentifierAnalyzer analyzer = context.ParseIdentifiers(expression);
return analyzer.GetIdentifiers(context);
}
}
}

View File

@@ -0,0 +1,360 @@
using System.Reflection.Emit;
using System.Reflection;
using Flee.CalcEngine.InternalTypes;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.CalcEngine.PublicTypes
{
public class CalculationEngine
{
#region "Fields"
private readonly DependencyManager<ExpressionResultPair> _myDependencies;
/// <summary>
/// Map of name to node
/// </summary>
private readonly Dictionary<string, ExpressionResultPair> _myNameNodeMap;
#endregion
#region "Events"
public event EventHandler<NodeEventArgs> NodeRecalculated;
#endregion
#region "Constructor"
public CalculationEngine()
{
_myDependencies = new DependencyManager<ExpressionResultPair>(new PairEqualityComparer());
_myNameNodeMap = new Dictionary<string, ExpressionResultPair>(StringComparer.OrdinalIgnoreCase);
}
#endregion
#region "Methods - Private"
private void AddTemporaryHead(string headName)
{
GenericExpressionResultPair<int> pair = new GenericExpressionResultPair<int>();
pair.SetName(headName);
if (_myNameNodeMap.ContainsKey(headName) == false)
{
_myDependencies.AddTail(pair);
_myNameNodeMap.Add(headName, pair);
}
else
{
throw new ArgumentException($"An expression already exists at '{headName}'");
}
}
private void DoBatchLoadAdd(BatchLoadInfo info)
{
try
{
this.Add(info.Name, info.ExpressionText, info.Context);
}
catch (ExpressionCompileException ex)
{
this.Clear();
throw new BatchLoadCompileException(info.Name, info.ExpressionText, ex);
}
}
private ExpressionResultPair GetTail(string tailName)
{
Utility.AssertNotNull(tailName, "name");
ExpressionResultPair pair = null;
_myNameNodeMap.TryGetValue(tailName, out pair);
return pair;
}
private ExpressionResultPair GetTailWithValidate(string tailName)
{
Utility.AssertNotNull(tailName, "name");
ExpressionResultPair pair = this.GetTail(tailName);
if (pair == null)
{
throw new ArgumentException($"No expression is associated with the name '{tailName}'");
}
else
{
return pair;
}
}
private string[] GetNames(IList<ExpressionResultPair> pairs)
{
string[] names = new string[pairs.Count];
for (int i = 0; i <= names.Length - 1; i++)
{
names[i] = pairs[i].Name;
}
return names;
}
private ExpressionResultPair[] GetRootTails(string[] roots)
{
// No roots supplied so get everything
if (roots.Length == 0)
{
return _myDependencies.GetTails();
}
// Get the tail for each name
ExpressionResultPair[] arr = new ExpressionResultPair[roots.Length];
for (int i = 0; i <= arr.Length - 1; i++)
{
arr[i] = this.GetTailWithValidate(roots[i]);
}
return arr;
}
#endregion
#region "Methods - Internal"
internal void FixTemporaryHead(IDynamicExpression expression, ExpressionContext context, Type resultType)
{
Type pairType = typeof(GenericExpressionResultPair<>);
pairType = pairType.MakeGenericType(resultType);
ExpressionResultPair pair = (ExpressionResultPair)Activator.CreateInstance(pairType);
string headName = context.CalcEngineExpressionName;
pair.SetName(headName);
pair.SetExpression(expression);
ExpressionResultPair oldPair = _myNameNodeMap[headName];
_myDependencies.ReplaceDependency(oldPair, pair);
_myNameNodeMap[headName] = pair;
// Let the pair store the result of its expression
pair.Recalculate();
}
/// <summary>
/// Called by an expression when it references another expression in the engine
/// </summary>
/// <param name="tailName"></param>
/// <param name="context"></param>
internal void AddDependency(string tailName, ExpressionContext context)
{
ExpressionResultPair actualTail = this.GetTail(tailName);
string headName = context.CalcEngineExpressionName;
ExpressionResultPair actualHead = this.GetTail(headName);
// An expression could depend on the same reference more than once (ie: "a + a * a")
_myDependencies.AddDepedency(actualTail, actualHead);
}
internal Type ResolveTailType(string tailName)
{
ExpressionResultPair actualTail = this.GetTail(tailName);
return actualTail.ResultType;
}
internal bool HasTail(string tailName)
{
return _myNameNodeMap.ContainsKey(tailName);
}
internal void EmitLoad(string tailName, FleeILGenerator ilg)
{
PropertyInfo pi = typeof(ExpressionContext).GetProperty("CalculationEngine");
ilg.Emit(OpCodes.Callvirt, pi.GetGetMethod());
// Load the tail
MemberInfo[] methods = typeof(CalculationEngine).FindMembers(MemberTypes.Method, BindingFlags.Instance | BindingFlags.Public, Type.FilterNameIgnoreCase, "GetResult");
MethodInfo mi = null;
foreach (MethodInfo method in methods)
{
if (method.IsGenericMethod == true)
{
mi = method;
break; // TODO: might not be correct. Was : Exit For
}
}
Type resultType = this.ResolveTailType(tailName);
mi = mi.MakeGenericMethod(resultType);
ilg.Emit(OpCodes.Ldstr, tailName);
ilg.Emit(OpCodes.Call, mi);
}
#endregion
#region "Methods - Public"
public void Add(string atomName, string expression, ExpressionContext context)
{
Utility.AssertNotNull(atomName, "atomName");
Utility.AssertNotNull(expression, "expression");
Utility.AssertNotNull(context, "context");
this.AddTemporaryHead(atomName);
context.SetCalcEngine(this, atomName);
context.CompileDynamic(expression);
}
public bool Remove(string name)
{
ExpressionResultPair tail = this.GetTail(name);
if (tail == null)
{
return false;
}
ExpressionResultPair[] dependents = _myDependencies.GetDependents(tail);
_myDependencies.Remove(dependents);
foreach (ExpressionResultPair pair in dependents)
{
_myNameNodeMap.Remove(pair.Name);
}
return true;
}
public BatchLoader CreateBatchLoader()
{
BatchLoader loader = new BatchLoader();
return loader;
}
public void BatchLoad(BatchLoader loader)
{
Utility.AssertNotNull(loader, "loader");
this.Clear();
BatchLoadInfo[] infos = loader.GetBachInfos();
foreach (BatchLoadInfo info in infos)
{
this.DoBatchLoadAdd(info);
}
}
public T GetResult<T>(string name)
{
ExpressionResultPair tail = this.GetTailWithValidate(name);
if ((!object.ReferenceEquals(typeof(T), tail.ResultType)))
{
string msg = $"The result type of '{name}' ('{tail.ResultType.Name}') does not match the supplied type argument ('{typeof(T).Name}')";
throw new ArgumentException(msg);
}
GenericExpressionResultPair<T> actualTail = (GenericExpressionResultPair<T>)tail;
return actualTail.Result;
}
public object GetResult(string name)
{
ExpressionResultPair tail = this.GetTailWithValidate(name);
return tail.ResultAsObject;
}
public IExpression GetExpression(string name)
{
ExpressionResultPair tail = this.GetTailWithValidate(name);
return tail.Expression;
}
public string[] GetDependents(string name)
{
ExpressionResultPair pair = this.GetTail(name);
List<ExpressionResultPair> dependents = new List<ExpressionResultPair>();
if ((pair != null))
{
_myDependencies.GetDirectDependents(pair, dependents);
}
return this.GetNames(dependents);
}
public string[] GetPrecedents(string name)
{
ExpressionResultPair pair = this.GetTail(name);
List<ExpressionResultPair> dependents = new List<ExpressionResultPair>();
if ((pair != null))
{
_myDependencies.GetDirectPrecedents(pair, dependents);
}
return this.GetNames(dependents);
}
public bool HasDependents(string name)
{
ExpressionResultPair pair = this.GetTail(name);
return (pair != null) && _myDependencies.HasDependents(pair);
}
public bool HasPrecedents(string name)
{
ExpressionResultPair pair = this.GetTail(name);
return (pair != null) && _myDependencies.HasPrecedents(pair);
}
public bool Contains(string name)
{
Utility.AssertNotNull(name, "name");
return _myNameNodeMap.ContainsKey(name);
}
public void Recalculate(params string[] roots)
{
// Get the tails corresponding to the names
ExpressionResultPair[] rootTails = this.GetRootTails(roots);
// Create a dependency list based on the tails
DependencyManager<ExpressionResultPair> tempDependents = _myDependencies.CloneDependents(rootTails);
// Get the sources (ie: nodes with no incoming edges) since that's what the sort requires
Queue<ExpressionResultPair> sources = tempDependents.GetSources(rootTails);
// Do the topological sort
IList<ExpressionResultPair> calcList = tempDependents.TopologicalSort(sources);
NodeEventArgs args = new NodeEventArgs();
// Recalculate the sorted expressions
foreach (ExpressionResultPair pair in calcList)
{
pair.Recalculate();
args.SetData(pair.Name, pair.ResultAsObject);
if (NodeRecalculated != null)
{
NodeRecalculated(this, args);
}
}
}
public void Clear()
{
_myDependencies.Clear();
_myNameNodeMap.Clear();
}
#endregion
#region "Properties - Public"
public int Count
{
get { return _myDependencies.Count; }
}
public string DependencyGraph
{
get { return _myDependencies.DependencyGraph; }
}
#endregion
}
}

View File

@@ -0,0 +1,54 @@
using Flee.PublicTypes;
namespace Flee.CalcEngine.PublicTypes
{
public class CircularReferenceException : System.Exception
{
private readonly string _myCircularReferenceSource;
internal CircularReferenceException()
{
}
internal CircularReferenceException(string circularReferenceSource)
{
_myCircularReferenceSource = circularReferenceSource;
}
public override string Message
{
get
{
if (_myCircularReferenceSource == null)
{
return "Circular reference detected in calculation engine";
}
else
{
return $"Circular reference detected in calculation engine at '{_myCircularReferenceSource}'";
}
}
}
}
public class BatchLoadCompileException : Exception
{
private readonly string _myAtomName;
private readonly string _myExpressionText;
internal BatchLoadCompileException(string atomName, string expressionText, ExpressionCompileException innerException) : base(
$"Batch Load: The expression for atom '${atomName}' could not be compiled", innerException)
{
_myAtomName = atomName;
_myExpressionText = expressionText;
}
public string AtomName => _myAtomName;
public string ExpressionText => _myExpressionText;
}
}

View File

@@ -0,0 +1,121 @@
using Flee.CalcEngine.InternalTypes;
using Flee.PublicTypes;
namespace Flee.CalcEngine.PublicTypes
{
public class SimpleCalcEngine
{
#region "Fields"
private readonly IDictionary<string, IExpression> _myExpressions;
private ExpressionContext _myContext;
#endregion
#region "Constructor"
public SimpleCalcEngine()
{
_myExpressions = new Dictionary<string, IExpression>(StringComparer.OrdinalIgnoreCase);
_myContext = new ExpressionContext();
}
#endregion
#region "Methods - Private"
private void AddCompiledExpression(string expressionName, IExpression expression)
{
if (_myExpressions.ContainsKey(expressionName) == true)
{
throw new InvalidOperationException($"The calc engine already contains an expression named '{expressionName}'");
}
else
{
_myExpressions.Add(expressionName, expression);
}
}
private ExpressionContext ParseAndLink(string expressionName, string expression)
{
IdentifierAnalyzer analyzer = Context.ParseIdentifiers(expression);
ExpressionContext context2 = _myContext.CloneInternal(true);
this.LinkExpression(expressionName, context2, analyzer);
// Tell the expression not to clone the context since it's already been cloned
context2.NoClone = true;
// Clear our context's variables
_myContext.Variables.Clear();
return context2;
}
private void LinkExpression(string expressionName, ExpressionContext context, IdentifierAnalyzer analyzer)
{
foreach (string identifier in analyzer.GetIdentifiers(context))
{
this.LinkIdentifier(identifier, expressionName, context);
}
}
private void LinkIdentifier(string identifier, string expressionName, ExpressionContext context)
{
IExpression child = null;
if (_myExpressions.TryGetValue(identifier, out child) == false)
{
string msg = $"Expression '{expressionName}' references unknown name '{identifier}'";
throw new InvalidOperationException(msg);
}
context.Variables.Add(identifier, child);
}
#endregion
#region "Methods - Public"
public void AddDynamic(string expressionName, string expression)
{
ExpressionContext linkedContext = this.ParseAndLink(expressionName, expression);
IExpression e = linkedContext.CompileDynamic(expression);
this.AddCompiledExpression(expressionName, e);
}
public void AddGeneric<T>(string expressionName, string expression)
{
ExpressionContext linkedContext = this.ParseAndLink(expressionName, expression);
IExpression e = linkedContext.CompileGeneric<T>(expression);
this.AddCompiledExpression(expressionName, e);
}
public void Clear()
{
_myExpressions.Clear();
}
#endregion
#region "Properties - Public"
public IExpression this[string name]
{
get
{
IExpression e = null;
_myExpressions.TryGetValue(name, out e);
return e;
}
}
public ExpressionContext Context
{
get { return _myContext; }
set { _myContext = value; }
}
#endregion
}
}

View File

@@ -0,0 +1,327 @@
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.ExpressionElements.Base.Literals;
using Flee.ExpressionElements.Literals.Integral;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.ExpressionElements
{
internal class ArithmeticElement : BinaryExpressionElement
{
private static MethodInfo _ourPowerMethodInfo;
private static MethodInfo _ourStringConcatMethodInfo;
private static MethodInfo _ourObjectConcatMethodInfo;
private BinaryArithmeticOperation _myOperation;
public ArithmeticElement()
{
_ourPowerMethodInfo = typeof(Math).GetMethod("Pow", BindingFlags.Public | BindingFlags.Static);
_ourStringConcatMethodInfo = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) }, null);
_ourObjectConcatMethodInfo = typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }, null);
}
protected override void GetOperation(object operation)
{
_myOperation = (BinaryArithmeticOperation)operation;
}
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
{
Type binaryResultType = ImplicitConverter.GetBinaryResultType(leftType, rightType);
MethodInfo overloadedMethod = this.GetOverloadedArithmeticOperator();
// Is an overloaded operator defined for our left and right children?
if ((overloadedMethod != null))
{
// Yes, so use its return type
return overloadedMethod.ReturnType;
}
else if ((binaryResultType != null))
{
// Operands are primitive types. Return computed result type unless we are doing a power operation
if (_myOperation == BinaryArithmeticOperation.Power)
{
return this.GetPowerResultType(leftType, rightType, binaryResultType);
}
else
{
return binaryResultType;
}
}
else if (this.IsEitherChildOfType(typeof(string)) == true & (_myOperation == BinaryArithmeticOperation.Add))
{
// String concatenation
return typeof(string);
}
else
{
// Invalid types
return null;
}
}
private Type GetPowerResultType(Type leftType, Type rightType, Type binaryResultType)
{
if (this.IsOptimizablePower == true)
{
return leftType;
}
else
{
return typeof(double);
}
}
private MethodInfo GetOverloadedArithmeticOperator()
{
// Get the name of the operator
string name = GetOverloadedOperatorFunctionName(_myOperation);
return base.GetOverloadedBinaryOperator(name, _myOperation);
}
private static string GetOverloadedOperatorFunctionName(BinaryArithmeticOperation op)
{
switch (op)
{
case BinaryArithmeticOperation.Add:
return "Addition";
case BinaryArithmeticOperation.Subtract:
return "Subtraction";
case BinaryArithmeticOperation.Multiply:
return "Multiply";
case BinaryArithmeticOperation.Divide:
return "Division";
case BinaryArithmeticOperation.Mod:
return "Modulus";
case BinaryArithmeticOperation.Power:
return "Exponent";
default:
Debug.Assert(false, "unknown operator type");
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
MethodInfo overloadedMethod = this.GetOverloadedArithmeticOperator();
if ((overloadedMethod != null))
{
// Emit a call to an overloaded operator
this.EmitOverloadedOperatorCall(overloadedMethod, ilg, services);
}
else if (this.IsEitherChildOfType(typeof(string)) == true)
{
// One of our operands is a string so emit a concatenation
this.EmitStringConcat(ilg, services);
}
else
{
// Emit a regular arithmetic operation
EmitArithmeticOperation(_myOperation, ilg, services);
}
}
private static bool IsUnsignedForArithmetic(Type t)
{
return object.ReferenceEquals(t, typeof(UInt32)) | object.ReferenceEquals(t, typeof(UInt64));
}
/// <summary>
/// Emit an arithmetic operation with handling for unsigned and checked contexts
/// </summary>
/// <param name="op"></param>
/// <param name="ilg"></param>
/// <param name="services"></param>
private void EmitArithmeticOperation(BinaryArithmeticOperation op, FleeILGenerator ilg, IServiceProvider services)
{
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
bool unsigned = IsUnsignedForArithmetic(MyLeftChild.ResultType) & IsUnsignedForArithmetic(MyRightChild.ResultType);
bool integral = Utility.IsIntegralType(MyLeftChild.ResultType) & Utility.IsIntegralType(MyRightChild.ResultType);
bool emitOverflow = integral & options.Checked;
EmitChildWithConvert(MyLeftChild, this.ResultType, ilg, services);
if (this.IsOptimizablePower == false)
{
EmitChildWithConvert(MyRightChild, this.ResultType, ilg, services);
}
switch (op)
{
case BinaryArithmeticOperation.Add:
if (emitOverflow == true)
{
if (unsigned == true)
{
ilg.Emit(OpCodes.Add_Ovf_Un);
}
else
{
ilg.Emit(OpCodes.Add_Ovf);
}
}
else
{
ilg.Emit(OpCodes.Add);
}
break;
case BinaryArithmeticOperation.Subtract:
if (emitOverflow == true)
{
if (unsigned == true)
{
ilg.Emit(OpCodes.Sub_Ovf_Un);
}
else
{
ilg.Emit(OpCodes.Sub_Ovf);
}
}
else
{
ilg.Emit(OpCodes.Sub);
}
break;
case BinaryArithmeticOperation.Multiply:
this.EmitMultiply(ilg, emitOverflow, unsigned);
break;
case BinaryArithmeticOperation.Divide:
if (unsigned == true)
{
ilg.Emit(OpCodes.Div_Un);
}
else
{
ilg.Emit(OpCodes.Div);
}
break;
case BinaryArithmeticOperation.Mod:
if (unsigned == true)
{
ilg.Emit(OpCodes.Rem_Un);
}
else
{
ilg.Emit(OpCodes.Rem);
}
break;
case BinaryArithmeticOperation.Power:
this.EmitPower(ilg, emitOverflow, unsigned);
break;
default:
Debug.Fail("Unknown op type");
break;
}
}
private void EmitPower(FleeILGenerator ilg, bool emitOverflow, bool unsigned)
{
if (this.IsOptimizablePower == true)
{
this.EmitOptimizedPower(ilg, emitOverflow, unsigned);
}
else
{
ilg.Emit(OpCodes.Call, _ourPowerMethodInfo);
}
}
private void EmitOptimizedPower(FleeILGenerator ilg, bool emitOverflow, bool unsigned)
{
Int32LiteralElement right = (Int32LiteralElement)MyRightChild;
if (right.Value == 0)
{
ilg.Emit(OpCodes.Pop);
IntegralLiteralElement.EmitLoad(1, ilg);
ImplicitConverter.EmitImplicitNumericConvert(typeof(Int32), MyLeftChild.ResultType, ilg);
return;
}
if (right.Value == 1)
{
return;
}
// Start at 1 since left operand has already been emited once
for (int i = 1; i <= right.Value - 1; i++)
{
ilg.Emit(OpCodes.Dup);
}
for (int i = 1; i <= right.Value - 1; i++)
{
this.EmitMultiply(ilg, emitOverflow, unsigned);
}
}
private void EmitMultiply(FleeILGenerator ilg, bool emitOverflow, bool unsigned)
{
if (emitOverflow == true)
{
if (unsigned == true)
{
ilg.Emit(OpCodes.Mul_Ovf_Un);
}
else
{
ilg.Emit(OpCodes.Mul_Ovf);
}
}
else
{
ilg.Emit(OpCodes.Mul);
}
}
/// <summary>
/// Emit a string concatenation
/// </summary>
/// <param name="ilg"></param>
/// <param name="services"></param>
private void EmitStringConcat(FleeILGenerator ilg, IServiceProvider services)
{
Type argType = default(Type);
System.Reflection.MethodInfo concatMethodInfo = default(System.Reflection.MethodInfo);
// Pick the most specific concat method
if (this.AreBothChildrenOfType(typeof(string)) == true)
{
concatMethodInfo = _ourStringConcatMethodInfo;
argType = typeof(string);
}
else
{
Debug.Assert(this.IsEitherChildOfType(typeof(string)), "one child must be a string");
concatMethodInfo = _ourObjectConcatMethodInfo;
argType = typeof(object);
}
// Emit the operands and call the function
MyLeftChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, argType, ilg);
MyRightChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, argType, ilg);
ilg.Emit(OpCodes.Call, concatMethodInfo);
}
private bool IsOptimizablePower
{
get
{
if (_myOperation != BinaryArithmeticOperation.Power || !(MyRightChild is Int32LiteralElement))
{
return false;
}
Int32LiteralElement right = (Int32LiteralElement)MyRightChild;
return right?.Value >= 0;
}
}
}
}

View File

@@ -0,0 +1,160 @@
using System.Collections;
using System.Diagnostics;
using System.Reflection.Emit;
using System.Reflection;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.Base
{
[Obsolete("Base class for expression elements that operate on two child elements")]
internal abstract class BinaryExpressionElement : ExpressionElement
{
protected ExpressionElement MyLeftChild;
protected ExpressionElement MyRightChild;
private Type _myResultType;
protected BinaryExpressionElement()
{
}
/// <summary>
/// Converts a list of binary elements into a binary tree
/// </summary>
/// <param name="childValues"></param>
/// <param name="elementType"></param>
/// <returns></returns>
public static BinaryExpressionElement CreateElement(IList childValues, Type elementType)
{
BinaryExpressionElement firstElement = (BinaryExpressionElement)Activator.CreateInstance(elementType);
firstElement.Configure((ExpressionElement)childValues[0], (ExpressionElement)childValues[2], childValues[1]);
BinaryExpressionElement lastElement = firstElement;
for (int i = 3; i <= childValues.Count - 1; i += 2)
{
BinaryExpressionElement element = (BinaryExpressionElement)Activator.CreateInstance(elementType);
element.Configure(lastElement, (ExpressionElement)childValues[i + 1], childValues[i]);
lastElement = element;
}
return lastElement;
}
protected abstract void GetOperation(object operation);
protected void ValidateInternal(object op)
{
_myResultType = this.GetResultType(MyLeftChild.ResultType, MyRightChild.ResultType);
if (_myResultType == null)
{
this.ThrowOperandTypeMismatch(op, MyLeftChild.ResultType, MyRightChild.ResultType);
}
}
protected MethodInfo GetOverloadedBinaryOperator(string name, object operation)
{
Type leftType = MyLeftChild.ResultType;
Type rightType = MyRightChild.ResultType;
BinaryOperatorBinder binder = new BinaryOperatorBinder(leftType, rightType);
// If both arguments are of the same type, pick either as the owner type
if (object.ReferenceEquals(leftType, rightType))
{
return Utility.GetOverloadedOperator(name, leftType, binder, leftType, rightType);
}
// Get the operator for both types
MethodInfo leftMethod = default(MethodInfo);
MethodInfo rightMethod = default(MethodInfo);
leftMethod = Utility.GetOverloadedOperator(name, leftType, binder, leftType, rightType);
rightMethod = Utility.GetOverloadedOperator(name, rightType, binder, leftType, rightType);
// Pick the right one
if (leftMethod == null & rightMethod == null)
{
// No operator defined for either
return null;
}
else if (leftMethod == null)
{
return rightMethod;
}
else if (rightMethod == null)
{
return leftMethod;
}
else if (object.ReferenceEquals(leftMethod, rightMethod))
{
// same operator for both (most likely defined in a common base class)
return leftMethod;
}
else
{
// Ambiguous call
base.ThrowAmbiguousCallException(leftType, rightType, operation);
return null;
}
}
protected void EmitOverloadedOperatorCall(MethodInfo method, FleeILGenerator ilg, IServiceProvider services)
{
ParameterInfo[] @params = method.GetParameters();
ParameterInfo pLeft = @params[0];
ParameterInfo pRight = @params[1];
EmitChildWithConvert(MyLeftChild, pLeft.ParameterType, ilg, services);
EmitChildWithConvert(MyRightChild, pRight.ParameterType, ilg, services);
ilg.Emit(OpCodes.Call, method);
}
protected void ThrowOperandTypeMismatch(object operation, Type leftType, Type rightType)
{
base.ThrowCompileException(CompileErrorResourceKeys.OperationNotDefinedForTypes, CompileExceptionReason.TypeMismatch, operation, leftType.Name, rightType.Name);
}
protected abstract Type GetResultType(Type leftType, Type rightType);
protected static void EmitChildWithConvert(ExpressionElement child, Type resultType, FleeILGenerator ilg, IServiceProvider services)
{
child.Emit(ilg, services);
bool converted = ImplicitConverter.EmitImplicitConvert(child.ResultType, resultType, ilg);
Debug.Assert(converted, "convert failed");
}
protected bool AreBothChildrenOfType(Type target)
{
return IsChildOfType(MyLeftChild, target) & IsChildOfType(MyRightChild, target);
}
protected bool IsEitherChildOfType(Type target)
{
return IsChildOfType(MyLeftChild, target) || IsChildOfType(MyRightChild, target);
}
protected static bool IsChildOfType(ExpressionElement child, Type t)
{
return object.ReferenceEquals(child.ResultType, t);
}
/// <summary>
/// Set the left and right operands, get the operation, and get the result type
/// </summary>
/// <param name="leftChild"></param>
/// <param name="rightChild"></param>
/// <param name="op"></param>
private void Configure(ExpressionElement leftChild, ExpressionElement rightChild, object op)
{
MyLeftChild = leftChild;
MyRightChild = rightChild;
this.GetOperation(op);
this.ValidateInternal(op);
}
public sealed override System.Type ResultType => _myResultType;
}
}

View File

@@ -0,0 +1,55 @@
using System.Diagnostics;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.Base
{
internal abstract class ExpressionElement
{
internal ExpressionElement()
{
}
/// <summary>
/// // All expression elements must be able to emit their IL
/// </summary>
/// <param name="ilg"></param>
/// <param name="services"></param>
public abstract void Emit(FleeILGenerator ilg, IServiceProvider services);
/// <summary>
/// All expression elements must expose the Type they evaluate to
/// </summary>
public abstract Type ResultType { get; }
public override string ToString()
{
return this.Name;
}
protected void ThrowCompileException(string messageKey, CompileExceptionReason reason, params object[] arguments)
{
string messageTemplate = FleeResourceManager.Instance.GetCompileErrorString(messageKey);
string message = string.Format(messageTemplate, arguments);
message = string.Concat(this.Name, ": ", message);
throw new ExpressionCompileException(message, reason);
}
protected void ThrowAmbiguousCallException(Type leftType, Type rightType, object operation)
{
this.ThrowCompileException(CompileErrorResourceKeys.AmbiguousOverloadedOperator, CompileExceptionReason.AmbiguousMatch, leftType.Name, rightType.Name, operation);
}
protected string Name
{
get
{
string key = this.GetType().Name;
string value = FleeResourceManager.Instance.GetElementNameString(key);
Debug.Assert(value != null, $"Element name for '{key}' not in resource file");
return value;
}
}
}
}

View File

@@ -0,0 +1,118 @@
using System.Diagnostics;
using System.Globalization;
using Flee.ExpressionElements.Literals.Integral;
namespace Flee.ExpressionElements.Base.Literals
{
internal abstract class IntegralLiteralElement : LiteralElement
{
protected IntegralLiteralElement()
{
}
/// <summary>
/// Attempt to find the first type of integer that a number can fit into
/// </summary>
/// <param name="image"></param>
/// <param name="isHex"></param>
/// <param name="negated"></param>
/// <param name="services"></param>
/// <returns></returns>
public static LiteralElement Create(string image, bool isHex, bool negated, IServiceProvider services)
{
StringComparison comparison = StringComparison.OrdinalIgnoreCase;
if (isHex == false)
{
// Create a real element if required
LiteralElement realElement = RealLiteralElement.CreateFromInteger(image, services);
if ((realElement != null))
{
return realElement;
}
}
bool hasUSuffix = image.EndsWith("u", comparison) & !image.EndsWith("lu", comparison);
bool hasLSuffix = image.EndsWith("l", comparison) & !image.EndsWith("ul", comparison);
bool hasUlSuffix = image.EndsWith("ul", comparison) | image.EndsWith("lu", comparison);
bool hasSuffix = hasUSuffix | hasLSuffix | hasUlSuffix;
LiteralElement constant = default(LiteralElement);
System.Globalization.NumberStyles numStyles = NumberStyles.Integer;
if (isHex == true)
{
numStyles = NumberStyles.AllowHexSpecifier;
image = image.Remove(0, 2);
}
if (hasSuffix == false)
{
// If the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong.
constant = Int32LiteralElement.TryCreate(image, isHex, negated);
if ((constant != null))
{
return constant;
}
constant = UInt32LiteralElement.TryCreate(image, numStyles);
if ((constant != null))
{
return constant;
}
constant = Int64LiteralElement.TryCreate(image, isHex, negated);
if ((constant != null))
{
return constant;
}
return new UInt64LiteralElement(image, numStyles);
}
else if (hasUSuffix == true)
{
image = image.Remove(image.Length - 1);
// If the literal is suffixed by U or u, it has the first of these types in which its value can be represented: uint, ulong.
constant = UInt32LiteralElement.TryCreate(image, numStyles);
if ((constant != null))
{
return constant;
}
else
{
return new UInt64LiteralElement(image, numStyles);
}
}
else if (hasLSuffix == true)
{
// If the literal is suffixed by L or l, it has the first of these types in which its value can be represented: long, ulong.
image = image.Remove(image.Length - 1);
constant = Int64LiteralElement.TryCreate(image, isHex, negated);
if ((constant != null))
{
return constant;
}
else
{
return new UInt64LiteralElement(image, numStyles);
}
}
else
{
// If the literal is suffixed by UL, Ul, uL, ul, LU, Lu, lU, or lu, it is of type ulong.
Debug.Assert(hasUlSuffix == true, "expecting ul suffix");
image = image.Remove(image.Length - 2);
return new UInt64LiteralElement(image, numStyles);
}
}
}
}

View File

@@ -0,0 +1,107 @@
using System.Diagnostics;
using System.Reflection.Emit;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.Base.Literals
{
internal abstract class LiteralElement : ExpressionElement
{
protected void OnParseOverflow(string image)
{
base.ThrowCompileException(CompileErrorResourceKeys.ValueNotRepresentableInType, CompileExceptionReason.ConstantOverflow, image, this.ResultType.Name);
}
public static void EmitLoad(Int32 value, FleeILGenerator ilg)
{
if (value >= -1 & value <= 8)
{
EmitSuperShort(value, ilg);
}
else if (value >= sbyte.MinValue & value <= sbyte.MaxValue)
{
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(value));
}
else
{
ilg.Emit(OpCodes.Ldc_I4, value);
}
}
protected static void EmitLoad(Int64 value, FleeILGenerator ilg)
{
if (value >= Int32.MinValue & value <= Int32.MaxValue)
{
EmitLoad(Convert.ToInt32(value), ilg);
ilg.Emit(OpCodes.Conv_I8);
}
else if (value >= 0 & value <= UInt32.MaxValue)
{
ilg.Emit(OpCodes.Ldc_I4, unchecked((int)Convert.ToUInt32(value)));
ilg.Emit(OpCodes.Conv_U8);
}
else
{
ilg.Emit(OpCodes.Ldc_I8, value);
}
}
protected static void EmitLoad(bool value, FleeILGenerator ilg)
{
if (value == true)
{
ilg.Emit(OpCodes.Ldc_I4_1);
}
else
{
ilg.Emit(OpCodes.Ldc_I4_0);
}
}
private static void EmitSuperShort(Int32 value, FleeILGenerator ilg)
{
OpCode ldcOpcode = default(OpCode);
switch (value)
{
case 0:
ldcOpcode = OpCodes.Ldc_I4_0;
break;
case 1:
ldcOpcode = OpCodes.Ldc_I4_1;
break;
case 2:
ldcOpcode = OpCodes.Ldc_I4_2;
break;
case 3:
ldcOpcode = OpCodes.Ldc_I4_3;
break;
case 4:
ldcOpcode = OpCodes.Ldc_I4_4;
break;
case 5:
ldcOpcode = OpCodes.Ldc_I4_5;
break;
case 6:
ldcOpcode = OpCodes.Ldc_I4_6;
break;
case 7:
ldcOpcode = OpCodes.Ldc_I4_7;
break;
case 8:
ldcOpcode = OpCodes.Ldc_I4_8;
break;
case -1:
ldcOpcode = OpCodes.Ldc_I4_M1;
break;
default:
Debug.Assert(false, "value out of range");
break;
}
ilg.Emit(ldcOpcode);
}
}
}

View File

@@ -0,0 +1,130 @@
using System.Diagnostics;
using Flee.ExpressionElements.Literals.Real;
using Flee.PublicTypes;
namespace Flee.ExpressionElements.Base.Literals
{
internal abstract class RealLiteralElement : LiteralElement
{
protected RealLiteralElement()
{
}
public static LiteralElement CreateFromInteger(string image, IServiceProvider services)
{
LiteralElement element = default(LiteralElement);
element = CreateSingle(image, services);
if ((element != null))
{
return element;
}
element = CreateDecimal(image, services);
if ((element != null))
{
return element;
}
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
// Convert to a double if option is set
if (options.IntegersAsDoubles == true)
{
return DoubleLiteralElement.Parse(image, services);
}
return null;
}
public static LiteralElement Create(string image, IServiceProvider services)
{
LiteralElement element = default(LiteralElement);
element = CreateSingle(image, services);
if ((element != null))
{
return element;
}
element = CreateDecimal(image, services);
if ((element != null))
{
return element;
}
element = CreateDouble(image, services);
if ((element != null))
{
return element;
}
element = CreateImplicitReal(image, services);
return element;
}
private static LiteralElement CreateImplicitReal(string image, IServiceProvider services)
{
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
RealLiteralDataType realType = options.RealLiteralDataType;
switch (realType)
{
case RealLiteralDataType.Double:
return DoubleLiteralElement.Parse(image, services);
case RealLiteralDataType.Single:
return SingleLiteralElement.Parse(image, services);
case RealLiteralDataType.Decimal:
return DecimalLiteralElement.Parse(image, services);
default:
Debug.Fail("Unknown value");
return null;
}
}
private static DoubleLiteralElement CreateDouble(string image, IServiceProvider services)
{
if (image.EndsWith("d", StringComparison.OrdinalIgnoreCase) == true)
{
image = image.Remove(image.Length - 1);
return DoubleLiteralElement.Parse(image, services);
}
else
{
return null;
}
}
private static SingleLiteralElement CreateSingle(string image, IServiceProvider services)
{
if (image.EndsWith("f", StringComparison.OrdinalIgnoreCase) == true)
{
image = image.Remove(image.Length - 1);
return SingleLiteralElement.Parse(image, services);
}
else
{
return null;
}
}
private static DecimalLiteralElement CreateDecimal(string image, IServiceProvider services)
{
if (image.EndsWith("m", StringComparison.OrdinalIgnoreCase) == true)
{
image = image.Remove(image.Length - 1);
return DecimalLiteralElement.Parse(image, services);
}
else
{
return null;
}
}
}
}

View File

@@ -0,0 +1,358 @@
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.Base
{
internal abstract class MemberElement : ExpressionElement
{
protected string MyName;
protected MemberElement MyPrevious;
protected MemberElement MyNext;
protected IServiceProvider MyServices;
protected ExpressionOptions MyOptions;
protected ExpressionContext MyContext;
protected ImportBase MyImport;
public const BindingFlags BindFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
protected MemberElement()
{
}
public void Link(MemberElement nextElement)
{
MyNext = nextElement;
if ((nextElement != null))
{
nextElement.MyPrevious = this;
}
}
public void Resolve(IServiceProvider services)
{
MyServices = services;
MyOptions = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
MyContext = (ExpressionContext)services.GetService(typeof(ExpressionContext));
this.ResolveInternal();
this.Validate();
}
public void SetImport(ImportBase import)
{
MyImport = import;
}
protected abstract void ResolveInternal();
public abstract bool IsStatic { get; }
public abstract bool IsExtensionMethod { get; }
protected abstract bool IsPublic { get; }
protected virtual void Validate()
{
if (MyPrevious == null)
{
return;
}
if (this.IsStatic == true && this.SupportsStatic == false && IsExtensionMethod == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.StaticMemberCannotBeAccessedWithInstanceReference, CompileExceptionReason.TypeMismatch, MyName);
}
else if (this.IsStatic == false && this.SupportsInstance == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.ReferenceToNonSharedMemberRequiresObjectReference, CompileExceptionReason.TypeMismatch, MyName);
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
if ((MyPrevious != null))
{
MyPrevious.Emit(ilg, services);
}
}
protected static void EmitLoadVariables(FleeILGenerator ilg)
{
ilg.Emit(OpCodes.Ldarg_2);
}
/// <summary>
/// Handles a call emit for static, instance methods of reference/value types
/// </summary>
/// <param name="mi"></param>
/// <param name="ilg"></param>
protected void EmitMethodCall(MethodInfo mi, FleeILGenerator ilg)
{
EmitMethodCall(this.ResultType, this.NextRequiresAddress, mi, ilg);
}
protected static void EmitMethodCall(Type resultType, bool nextRequiresAddress, MethodInfo mi, FleeILGenerator ilg)
{
if (mi.GetType().IsValueType == false)
{
EmitReferenceTypeMethodCall(mi, ilg);
}
else
{
EmitValueTypeMethodCall(mi, ilg);
}
if (resultType.IsValueType & nextRequiresAddress)
{
EmitValueTypeLoadAddress(ilg, resultType);
}
}
protected static bool IsGetTypeMethod(MethodInfo mi)
{
MethodInfo miGetType = typeof(object).GetMethod("gettype", BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
return mi.MethodHandle.Equals(miGetType.MethodHandle);
}
/// <summary>
/// Emit a function call for a value type
/// </summary>
/// <param name="mi"></param>
/// <param name="ilg"></param>
private static void EmitValueTypeMethodCall(MethodInfo mi, FleeILGenerator ilg)
{
if (mi.IsStatic == true)
{
ilg.Emit(OpCodes.Call, mi);
}
else if ((!object.ReferenceEquals(mi.DeclaringType, mi.ReflectedType)))
{
// Method is not defined on the value type
if (IsGetTypeMethod(mi) == true)
{
// Special GetType method which requires a box
ilg.Emit(OpCodes.Box, mi.ReflectedType);
ilg.Emit(OpCodes.Call, mi);
}
else
{
// Equals, GetHashCode, and ToString methods on the base
ilg.Emit(OpCodes.Constrained, mi.ReflectedType);
ilg.Emit(OpCodes.Callvirt, mi);
}
}
else
{
// Call value type's implementation
ilg.Emit(OpCodes.Call, mi);
}
}
private static void EmitReferenceTypeMethodCall(MethodInfo mi, FleeILGenerator ilg)
{
if (mi.IsStatic == true)
{
ilg.Emit(OpCodes.Call, mi);
}
else
{
ilg.Emit(OpCodes.Callvirt, mi);
}
}
protected static void EmitValueTypeLoadAddress(FleeILGenerator ilg, Type targetType)
{
int index = ilg.GetTempLocalIndex(targetType);
Utility.EmitStoreLocal(ilg, index);
ilg.Emit(OpCodes.Ldloca_S, Convert.ToByte(index));
}
protected void EmitLoadOwner(FleeILGenerator ilg)
{
ilg.Emit(OpCodes.Ldarg_0);
Type ownerType = MyOptions.OwnerType;
if (ownerType.IsValueType == false)
{
return;
}
ilg.Emit(OpCodes.Unbox, ownerType);
ilg.Emit(OpCodes.Ldobj, ownerType);
// Emit usual stuff for value types but use the owner type as the target
if (this.RequiresAddress == true)
{
EmitValueTypeLoadAddress(ilg, ownerType);
}
}
/// <summary>
/// Determine if a field, property, or method is public
/// </summary>
/// <param name="member"></param>
/// <returns></returns>
private static bool IsMemberPublic(MemberInfo member)
{
FieldInfo fi = member as FieldInfo;
if ((fi != null))
{
return fi.IsPublic;
}
PropertyInfo pi = member as PropertyInfo;
if ((pi != null))
{
MethodInfo pmi = pi.GetGetMethod(true);
return pmi.IsPublic;
}
MethodInfo mi = member as MethodInfo;
if ((mi != null))
{
return mi.IsPublic;
}
Debug.Assert(false, "unknown member type");
return false;
}
protected MemberInfo[] GetAccessibleMembers(MemberInfo[] members)
{
List<MemberInfo> accessible = new List<MemberInfo>();
// Keep all members that are accessible
foreach (MemberInfo mi in members)
{
if (this.IsMemberAccessible(mi) == true)
{
accessible.Add(mi);
}
}
return accessible.ToArray();
}
protected static bool IsOwnerMemberAccessible(MemberInfo member, ExpressionOptions options)
{
bool accessAllowed = false;
// Get the allowed access defined in the options
if (IsMemberPublic(member) == true)
{
accessAllowed = (options.OwnerMemberAccess & BindingFlags.Public) != 0;
}
else
{
accessAllowed = (options.OwnerMemberAccess & BindingFlags.NonPublic) != 0;
}
// See if the member has our access attribute defined
ExpressionOwnerMemberAccessAttribute attr = (ExpressionOwnerMemberAccessAttribute)Attribute.GetCustomAttribute(member, typeof(ExpressionOwnerMemberAccessAttribute));
if (attr == null)
{
// No, so return the access level
return accessAllowed;
}
else
{
// Member has our access attribute defined; use its access value instead
return attr.AllowAccess;
}
}
public bool IsMemberAccessible(MemberInfo member)
{
if (MyOptions.IsOwnerType(member.ReflectedType) == true)
{
return IsOwnerMemberAccessible(member, MyOptions);
}
else
{
return IsMemberPublic(member);
}
}
protected MemberInfo[] GetMembers(MemberTypes targets)
{
if (MyPrevious == null)
{
// Do we have a namespace?
if (MyImport == null)
{
// Get all members in the default namespace
return this.GetDefaultNamespaceMembers(MyName, targets);
}
else
{
return MyImport.FindMembers(MyName, targets);
}
}
else
{
// We are not the first element; find all members with our name on the type of the previous member
// We are not the first element; find all members with our name on the type of the previous member
var foundMembers = MyPrevious.TargetType.FindMembers(targets, BindFlags, MyOptions.MemberFilter, MyName);
var importedMembers = MyContext.Imports.RootImport.FindMembers(MyName, targets);
if (foundMembers.Length == 0) //If no members found search in root import
return importedMembers;
MemberInfo[] allMembers = new MemberInfo[foundMembers.Length + importedMembers.Length];
foundMembers.CopyTo(allMembers, 0);
importedMembers.CopyTo(allMembers, foundMembers.Length);
return allMembers;
}
}
/// <summary>
/// Find members in the default namespace
/// </summary>
/// <param name="name"></param>
/// <param name="memberType"></param>
/// <returns></returns>
protected MemberInfo[] GetDefaultNamespaceMembers(string name, MemberTypes memberType)
{
// Search the owner first
MemberInfo[] members = MyContext.Imports.FindOwnerMembers(name, memberType);
// Keep only the accessible members
members = this.GetAccessibleMembers(members);
//Also search imports
var importedMembers = MyContext.Imports.RootImport.FindMembers(name, memberType);
//if no members, just return imports
if (members.Length == 0)
return importedMembers;
//combine members and imports
MemberInfo[] allMembers = new MemberInfo[members.Length + importedMembers.Length];
members.CopyTo(allMembers, 0);
importedMembers.CopyTo(allMembers, members.Length);
return allMembers;
}
protected static bool IsElementPublic(MemberElement e)
{
return e.IsPublic;
}
public string MemberName => MyName;
protected bool NextRequiresAddress => MyNext != null && MyNext.RequiresAddress;
protected virtual bool RequiresAddress => false;
protected virtual bool SupportsInstance => true;
protected virtual bool SupportsStatic => false;
public System.Type TargetType => this.ResultType;
}
}

View File

@@ -0,0 +1,28 @@
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.Base
{
internal abstract class UnaryElement : ExpressionElement
{
protected ExpressionElement MyChild;
private Type _myResultType;
public void SetChild(ExpressionElement child)
{
MyChild = child;
_myResultType = this.GetResultType(child.ResultType);
if (_myResultType == null)
{
base.ThrowCompileException(CompileErrorResourceKeys.OperationNotDefinedForType, CompileExceptionReason.TypeMismatch, MyChild.ResultType.Name);
}
}
protected abstract Type GetResultType(Type childType);
public override System.Type ResultType => _myResultType;
}
}

513
ExpressionElements/Cast.cs Normal file
View File

@@ -0,0 +1,513 @@
using System.Diagnostics;
using System.Reflection.Emit;
using System.Reflection;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements
{
internal class CastElement : ExpressionElement
{
private readonly ExpressionElement _myCastExpression;
private readonly Type _myDestType;
public CastElement(ExpressionElement castExpression, string[] destTypeParts, bool isArray, IServiceProvider services)
{
_myCastExpression = castExpression;
_myDestType = GetDestType(destTypeParts, services);
if (_myDestType == null)
{
base.ThrowCompileException(CompileErrorResourceKeys.CouldNotResolveType, CompileExceptionReason.UndefinedName, GetDestTypeString(destTypeParts, isArray));
}
if (isArray == true)
{
_myDestType = _myDestType.MakeArrayType();
}
if (this.IsValidCast(_myCastExpression.ResultType, _myDestType) == false)
{
this.ThrowInvalidCastException();
}
}
private static string GetDestTypeString(string[] parts, bool isArray)
{
string s = string.Join(".", parts);
if (isArray == true)
{
s = s + "[]";
}
return s;
}
/// <summary>
/// Resolve the type we are casting to
/// </summary>
/// <param name="destTypeParts"></param>
/// <param name="services"></param>
/// <returns></returns>
private static Type GetDestType(string[] destTypeParts, IServiceProvider services)
{
ExpressionContext context = (ExpressionContext)services.GetService(typeof(ExpressionContext));
Type t = null;
// Try to find a builtin type with the name
if (destTypeParts.Length == 1)
{
t = ExpressionImports.GetBuiltinType(destTypeParts[0]);
}
if ((t != null))
{
return t;
}
// Try to find the type in an import
t = context.Imports.FindType(destTypeParts);
if ((t != null))
{
return t;
}
return null;
}
private bool IsValidCast(Type sourceType, Type destType)
{
if (object.ReferenceEquals(sourceType, destType))
{
// Identity cast always succeeds
return true;
}
else if (destType.IsAssignableFrom(sourceType) == true)
{
// Cast is already implicitly valid
return true;
}
else if (ImplicitConverter.EmitImplicitConvert(sourceType, destType, null) == true)
{
// Cast is already implicitly valid
return true;
}
else if (IsCastableNumericType(sourceType) & IsCastableNumericType(destType))
{
// Explicit cast of numeric types always succeeds
return true;
}
else if (sourceType.IsEnum == true | destType.IsEnum == true)
{
return this.IsValidExplicitEnumCast(sourceType, destType);
}
else if ((this.GetExplictOverloadedOperator(sourceType, destType) != null))
{
// Overloaded explict cast exists
return true;
}
if (sourceType.IsValueType == true)
{
// If we get here then the cast always fails since we are either casting one value type to another
// or a value type to an invalid reference type
return false;
}
else
{
if (destType.IsValueType == true)
{
// Reference type to value type
// Can only succeed if the reference type is a base of the value type or
// it is one of the interfaces the value type implements
Type[] interfaces = destType.GetInterfaces();
return IsBaseType(destType, sourceType) == true | System.Array.IndexOf(interfaces, sourceType) != -1;
}
else
{
// Reference type to reference type
return this.IsValidExplicitReferenceCast(sourceType, destType);
}
}
}
private MethodInfo GetExplictOverloadedOperator(Type sourceType, Type destType)
{
ExplicitOperatorMethodBinder binder = new ExplicitOperatorMethodBinder(destType, sourceType);
// Look for an operator on the source type and dest types
MethodInfo miSource = Utility.GetOverloadedOperator("Explicit", sourceType, binder, sourceType);
MethodInfo miDest = Utility.GetOverloadedOperator("Explicit", destType, binder, sourceType);
if (miSource == null & miDest == null)
{
return null;
}
else if (miSource == null)
{
return miDest;
}
else if (miDest == null)
{
return miSource;
}
else
{
base.ThrowAmbiguousCallException(sourceType, destType, "Explicit");
return null;
}
}
private bool IsValidExplicitEnumCast(Type sourceType, Type destType)
{
sourceType = GetUnderlyingEnumType(sourceType);
destType = GetUnderlyingEnumType(destType);
return this.IsValidCast(sourceType, destType);
}
private bool IsValidExplicitReferenceCast(Type sourceType, Type destType)
{
Debug.Assert(sourceType.IsValueType == false & destType.IsValueType == false, "expecting reference types");
if (object.ReferenceEquals(sourceType, typeof(object)))
{
// From object to any other reference-type
return true;
}
else if (sourceType.IsArray == true & destType.IsArray == true)
{
// From an array-type S with an element type SE to an array-type T with an element type TE,
// provided all of the following are true:
// S and T have the same number of dimensions
if (sourceType.GetArrayRank() != destType.GetArrayRank())
{
return false;
}
else
{
Type SE = sourceType.GetElementType();
Type TE = destType.GetElementType();
// Both SE and TE are reference-types
if (SE.IsValueType == true | TE.IsValueType == true)
{
return false;
}
else
{
// An explicit reference conversion exists from SE to TE
return this.IsValidExplicitReferenceCast(SE, TE);
}
}
}
else if (sourceType.IsClass == true & destType.IsClass == true)
{
// From any class-type S to any class-type T, provided S is a base class of T
return IsBaseType(destType, sourceType);
}
else if (sourceType.IsClass == true & destType.IsInterface == true)
{
// From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T
return sourceType.IsSealed == false & ImplementsInterface(sourceType, destType) == false;
}
else if (sourceType.IsInterface == true & destType.IsClass == true)
{
// From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.
return destType.IsSealed == false | ImplementsInterface(destType, sourceType) == true;
}
else if (sourceType.IsInterface == true & destType.IsInterface == true)
{
// From any interface-type S to any interface-type T, provided S is not derived from T
return ImplementsInterface(sourceType, destType) == false;
}
else
{
Debug.Assert(false, "unknown explicit cast");
}
return false;
}
private static bool IsBaseType(Type target, Type potentialBase)
{
Type current = target;
while ((current != null))
{
if (object.ReferenceEquals(current, potentialBase))
{
return true;
}
current = current.BaseType;
}
return false;
}
private static bool ImplementsInterface(Type target, Type interfaceType)
{
Type[] interfaces = target.GetInterfaces();
return System.Array.IndexOf(interfaces, interfaceType) != -1;
}
private void ThrowInvalidCastException()
{
base.ThrowCompileException(CompileErrorResourceKeys.CannotConvertType, CompileExceptionReason.InvalidExplicitCast, _myCastExpression.ResultType.Name, _myDestType.Name);
}
private static bool IsCastableNumericType(Type t)
{
return t.IsPrimitive == true & (!object.ReferenceEquals(t, typeof(bool)));
}
private static Type GetUnderlyingEnumType(Type t)
{
if (t.IsEnum == true)
{
return System.Enum.GetUnderlyingType(t);
}
else
{
return t;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
_myCastExpression.Emit(ilg, services);
Type sourceType = _myCastExpression.ResultType;
Type destType = _myDestType;
this.EmitCast(ilg, sourceType, destType, services);
}
private void EmitCast(FleeILGenerator ilg, Type sourceType, Type destType, IServiceProvider services)
{
MethodInfo explicitOperator = this.GetExplictOverloadedOperator(sourceType, destType);
if (object.ReferenceEquals(sourceType, destType))
{
// Identity cast; do nothing
return;
}
else if ((explicitOperator != null))
{
ilg.Emit(OpCodes.Call, explicitOperator);
}
else if (sourceType.IsEnum == true | destType.IsEnum == true)
{
this.EmitEnumCast(ilg, sourceType, destType, services);
}
else if (ImplicitConverter.EmitImplicitConvert(sourceType, destType, ilg) == true)
{
// Implicit numeric cast; do nothing
return;
}
else if (IsCastableNumericType(sourceType) & IsCastableNumericType(destType))
{
// Explicit numeric cast
EmitExplicitNumericCast(ilg, sourceType, destType, services);
}
else if (sourceType.IsValueType == true)
{
Debug.Assert(destType.IsValueType == false, "expecting reference type");
ilg.Emit(OpCodes.Box, sourceType);
}
else
{
if (destType.IsValueType == true)
{
// Reference type to value type
ilg.Emit(OpCodes.Unbox_Any, destType);
}
else
{
// Reference type to reference type
if (destType.IsAssignableFrom(sourceType) == false)
{
// Only emit cast if it is an explicit cast
ilg.Emit(OpCodes.Castclass, destType);
}
}
}
}
private void EmitEnumCast(FleeILGenerator ilg, Type sourceType, Type destType, IServiceProvider services)
{
if (destType.IsValueType == false)
{
ilg.Emit(OpCodes.Box, sourceType);
}
else if (sourceType.IsValueType == false)
{
ilg.Emit(OpCodes.Unbox_Any, destType);
}
else
{
sourceType = GetUnderlyingEnumType(sourceType);
destType = GetUnderlyingEnumType(destType);
this.EmitCast(ilg, sourceType, destType, services);
}
}
private static void EmitExplicitNumericCast(FleeILGenerator ilg, Type sourceType, Type destType, IServiceProvider services)
{
TypeCode desttc = Type.GetTypeCode(destType);
TypeCode sourcetc = Type.GetTypeCode(sourceType);
bool unsigned = IsUnsignedType(sourceType);
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
bool @checked = options.Checked;
OpCode op = OpCodes.Nop;
switch (desttc)
{
case TypeCode.SByte:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_I1_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_I1;
}
else
{
op = OpCodes.Conv_I1;
}
break;
case TypeCode.Byte:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_U1_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_U1;
}
else
{
op = OpCodes.Conv_U1;
}
break;
case TypeCode.Int16:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_I2_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_I2;
}
else
{
op = OpCodes.Conv_I2;
}
break;
case TypeCode.UInt16:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_U2_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_U2;
}
else
{
op = OpCodes.Conv_U2;
}
break;
case TypeCode.Int32:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_I4_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_I4;
}
else if (sourcetc != TypeCode.UInt32)
{
// Don't need to emit a convert for this case since, to the CLR, it is the same data type
op = OpCodes.Conv_I4;
}
break;
case TypeCode.UInt32:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_U4_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_U4;
}
else if (sourcetc != TypeCode.Int32)
{
op = OpCodes.Conv_U4;
}
break;
case TypeCode.Int64:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_I8_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_I8;
}
else if (sourcetc != TypeCode.UInt64)
{
op = OpCodes.Conv_I8;
}
break;
case TypeCode.UInt64:
if (unsigned == true & @checked == true)
{
op = OpCodes.Conv_Ovf_U8_Un;
}
else if (@checked == true)
{
op = OpCodes.Conv_Ovf_U8;
}
else if (sourcetc != TypeCode.Int64)
{
op = OpCodes.Conv_U8;
}
break;
case TypeCode.Single:
op = OpCodes.Conv_R4;
break;
default:
Debug.Assert(false, "Unknown cast dest type");
break;
}
if (op.Equals(OpCodes.Nop) == false)
{
ilg.Emit(op);
}
}
private static bool IsUnsignedType(Type t)
{
TypeCode tc = Type.GetTypeCode(t);
switch (tc)
{
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
default:
return false;
}
}
public override System.Type ResultType => _myDestType;
}
}

View File

@@ -0,0 +1,279 @@
using System.Diagnostics;
using System.Reflection.Emit;
using System.Reflection;
using Flee.ExpressionElements.Base;
using Flee.ExpressionElements.Literals.Integral;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.ExpressionElements
{
internal class CompareElement : BinaryExpressionElement
{
private LogicalCompareOperation _myOperation;
public CompareElement()
{
}
public void Initialize(ExpressionElement leftChild, ExpressionElement rightChild, LogicalCompareOperation op)
{
MyLeftChild = leftChild;
MyRightChild = rightChild;
_myOperation = op;
}
public void Validate()
{
this.ValidateInternal(_myOperation);
}
protected override void GetOperation(object operation)
{
_myOperation = (LogicalCompareOperation)operation;
}
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
{
Type binaryResultType = ImplicitConverter.GetBinaryResultType(leftType, rightType);
MethodInfo overloadedOperator = this.GetOverloadedCompareOperator();
bool isEqualityOp = IsOpTypeEqualOrNotEqual(_myOperation);
// Use our string equality instead of overloaded operator
if (object.ReferenceEquals(leftType, typeof(string)) & object.ReferenceEquals(rightType, typeof(string)) & isEqualityOp == true)
{
// String equality
return typeof(bool);
}
else if ((overloadedOperator != null))
{
return overloadedOperator.ReturnType;
}
else if ((binaryResultType != null))
{
// Comparison of numeric operands
return typeof(bool);
}
else if (object.ReferenceEquals(leftType, typeof(bool)) & object.ReferenceEquals(rightType, typeof(bool)) & isEqualityOp == true)
{
// Boolean equality
return typeof(bool);
}
else if (this.AreBothChildrenReferenceTypes() == true & isEqualityOp == true)
{
// Comparison of reference types
return typeof(bool);
}
else if (this.AreBothChildrenSameEnum() == true)
{
return typeof(bool);
}
else
{
// Invalid operands
return null;
}
}
private MethodInfo GetOverloadedCompareOperator()
{
string name = GetCompareOperatorName(_myOperation);
return base.GetOverloadedBinaryOperator(name, _myOperation);
}
private static string GetCompareOperatorName(LogicalCompareOperation op)
{
switch (op)
{
case LogicalCompareOperation.Equal:
return "Equality";
case LogicalCompareOperation.NotEqual:
return "Inequality";
case LogicalCompareOperation.GreaterThan:
return "GreaterThan";
case LogicalCompareOperation.LessThan:
return "LessThan";
case LogicalCompareOperation.GreaterThanOrEqual:
return "GreaterThanOrEqual";
case LogicalCompareOperation.LessThanOrEqual:
return "LessThanOrEqual";
default:
Debug.Assert(false, "unknown compare type");
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
Type binaryResultType = ImplicitConverter.GetBinaryResultType(MyLeftChild.ResultType, MyRightChild.ResultType);
MethodInfo overloadedOperator = this.GetOverloadedCompareOperator();
if (this.AreBothChildrenOfType(typeof(string)))
{
// String equality
MyLeftChild.Emit(ilg, services);
MyRightChild.Emit(ilg, services);
EmitStringEquality(ilg, _myOperation, services);
}
else if ((overloadedOperator != null))
{
base.EmitOverloadedOperatorCall(overloadedOperator, ilg, services);
}
else if ((binaryResultType != null))
{
// Emit a compare of numeric operands
EmitChildWithConvert(MyLeftChild, binaryResultType, ilg, services);
EmitChildWithConvert(MyRightChild, binaryResultType, ilg, services);
EmitCompareOperation(ilg, _myOperation);
}
else if (this.AreBothChildrenOfType(typeof(bool)))
{
// Boolean equality
this.EmitRegular(ilg, services);
}
else if (this.AreBothChildrenReferenceTypes() == true)
{
// Reference equality
this.EmitRegular(ilg, services);
}
else if (MyLeftChild.ResultType.IsEnum == true & MyRightChild.ResultType.IsEnum == true)
{
this.EmitRegular(ilg, services);
}
else
{
Debug.Fail("unknown operand types");
}
}
private void EmitRegular(FleeILGenerator ilg, IServiceProvider services)
{
MyLeftChild.Emit(ilg, services);
MyRightChild.Emit(ilg, services);
this.EmitCompareOperation(ilg, _myOperation);
}
private static void EmitStringEquality(FleeILGenerator ilg, LogicalCompareOperation op, IServiceProvider services)
{
// Get the StringComparison from the options
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
Int32LiteralElement ic = new Int32LiteralElement((int)options.StringComparison);
ic.Emit(ilg, services);
// and emit the method call
System.Reflection.MethodInfo mi = typeof(string).GetMethod("Equals", new Type[] { typeof(string), typeof(string), typeof(StringComparison) }, null);
ilg.Emit(OpCodes.Call, mi);
if (op == LogicalCompareOperation.NotEqual)
{
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
}
}
private static bool IsOpTypeEqualOrNotEqual(LogicalCompareOperation op)
{
return op == LogicalCompareOperation.Equal | op == LogicalCompareOperation.NotEqual;
}
private bool AreBothChildrenReferenceTypes()
{
return MyLeftChild.ResultType.IsValueType == false & MyRightChild.ResultType.IsValueType == false;
}
private bool AreBothChildrenSameEnum()
{
return MyLeftChild.ResultType.IsEnum == true && object.ReferenceEquals(MyLeftChild.ResultType, MyRightChild.ResultType);
}
/// <summary>
/// Emit the actual compare
/// </summary>
/// <param name="ilg"></param>
/// <param name="op"></param>
private void EmitCompareOperation(FleeILGenerator ilg, LogicalCompareOperation op)
{
OpCode ltOpcode = this.GetCompareGTLTOpcode(false);
OpCode gtOpcode = this.GetCompareGTLTOpcode(true);
switch (op)
{
case LogicalCompareOperation.Equal:
ilg.Emit(OpCodes.Ceq);
break;
case LogicalCompareOperation.LessThan:
ilg.Emit(ltOpcode);
break;
case LogicalCompareOperation.GreaterThan:
ilg.Emit(gtOpcode);
break;
case LogicalCompareOperation.NotEqual:
ilg.Emit(OpCodes.Ceq);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
break;
case LogicalCompareOperation.LessThanOrEqual:
ilg.Emit(gtOpcode);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
break;
case LogicalCompareOperation.GreaterThanOrEqual:
ilg.Emit(ltOpcode);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
break;
default:
Debug.Fail("Unknown op type");
break;
}
}
/// <summary>
/// Get the correct greater/less than opcode
/// </summary>
/// <param name="greaterThan"></param>
/// <returns></returns>
private OpCode GetCompareGTLTOpcode(bool greaterThan)
{
Type leftType = MyLeftChild.ResultType;
if (object.ReferenceEquals(leftType, MyRightChild.ResultType))
{
if (object.ReferenceEquals(leftType, typeof(UInt32)) | object.ReferenceEquals(leftType, typeof(UInt64)))
{
if (greaterThan == true)
{
return OpCodes.Cgt_Un;
}
else
{
return OpCodes.Clt_Un;
}
}
else
{
return GetCompareOpcode(greaterThan);
}
}
else
{
return GetCompareOpcode(greaterThan);
}
}
private static OpCode GetCompareOpcode(bool greaterThan)
{
if (greaterThan == true)
{
return OpCodes.Cgt;
}
else
{
return OpCodes.Clt;
}
}
}
}

View File

@@ -0,0 +1,75 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements
{
internal class ConditionalElement : ExpressionElement
{
private readonly ExpressionElement _myCondition;
private readonly ExpressionElement _myWhenTrue;
private readonly ExpressionElement _myWhenFalse;
private readonly Type _myResultType;
public ConditionalElement(ExpressionElement condition, ExpressionElement whenTrue, ExpressionElement whenFalse)
{
_myCondition = condition;
_myWhenTrue = whenTrue;
_myWhenFalse = whenFalse;
if ((!object.ReferenceEquals(_myCondition.ResultType, typeof(bool))))
{
base.ThrowCompileException(CompileErrorResourceKeys.FirstArgNotBoolean, CompileExceptionReason.TypeMismatch);
}
// The result type is the type that is common to the true/false operands
if (ImplicitConverter.EmitImplicitConvert(_myWhenFalse.ResultType, _myWhenTrue.ResultType, null) == true)
{
_myResultType = _myWhenTrue.ResultType;
}
else if (ImplicitConverter.EmitImplicitConvert(_myWhenTrue.ResultType, _myWhenFalse.ResultType, null) == true)
{
_myResultType = _myWhenFalse.ResultType;
}
else
{
base.ThrowCompileException(CompileErrorResourceKeys.NeitherArgIsConvertibleToTheOther, CompileExceptionReason.TypeMismatch, _myWhenTrue.ResultType.Name, _myWhenFalse.ResultType.Name);
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
this.EmitConditional(ilg, services);
}
private void EmitConditional(FleeILGenerator ilg, IServiceProvider services)
{
Label falseLabel = ilg.DefineLabel();
Label endLabel = ilg.DefineLabel();
// Emit the condition
_myCondition.Emit(ilg, services);
// On false go to the false operand
ilg.EmitBranchFalse(falseLabel);
// Emit the true operand
_myWhenTrue.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(_myWhenTrue.ResultType, _myResultType, ilg);
// Jump to end
ilg.EmitBranch(endLabel);
ilg.MarkLabel(falseLabel);
// Emit the false operand
_myWhenFalse.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(_myWhenFalse.ResultType, _myResultType, ilg);
// Fall through to end
ilg.MarkLabel(endLabel);
}
public override System.Type ResultType => _myResultType;
}
}

195
ExpressionElements/In.cs Normal file
View File

@@ -0,0 +1,195 @@
using System.Collections;
using System.Reflection.Emit;
using System.Reflection;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements
{
internal class InElement : ExpressionElement
{
// Element we will search for
private ExpressionElement MyOperand;
// Elements we will compare against
private List<ExpressionElement> MyArguments;
// Collection to look in
private ExpressionElement MyTargetCollectionElement;
// Type of the collection
private Type MyTargetCollectionType;
// Initialize for searching a list of values
public InElement(ExpressionElement operand, IList listElements)
{
MyOperand = operand;
ExpressionElement[] arr = new ExpressionElement[listElements.Count];
listElements.CopyTo(arr, 0);
MyArguments = new List<ExpressionElement>(arr);
this.ResolveForListSearch();
}
// Initialize for searching a collection
public InElement(ExpressionElement operand, ExpressionElement targetCollection)
{
MyOperand = operand;
MyTargetCollectionElement = targetCollection;
this.ResolveForCollectionSearch();
}
private void ResolveForListSearch()
{
CompareElement ce = new CompareElement();
// Validate that our operand is comparable to all elements in the list
foreach (ExpressionElement argumentElement in MyArguments)
{
ce.Initialize(MyOperand, argumentElement, LogicalCompareOperation.Equal);
ce.Validate();
}
}
private void ResolveForCollectionSearch()
{
// Try to find a collection type
MyTargetCollectionType = this.GetTargetCollectionType();
if (MyTargetCollectionType == null)
{
base.ThrowCompileException(CompileErrorResourceKeys.SearchArgIsNotKnownCollectionType, CompileExceptionReason.TypeMismatch, MyTargetCollectionElement.ResultType.Name);
}
// Validate that the operand type is compatible with the collection
MethodInfo mi = this.GetCollectionContainsMethod();
ParameterInfo p1 = mi.GetParameters()[0];
if (ImplicitConverter.EmitImplicitConvert(MyOperand.ResultType, p1.ParameterType, null) == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.OperandNotConvertibleToCollectionType, CompileExceptionReason.TypeMismatch, MyOperand.ResultType.Name, p1.ParameterType.Name);
}
}
private Type GetTargetCollectionType()
{
Type collType = MyTargetCollectionElement.ResultType;
// Try to see if the collection is a generic ICollection or IDictionary
Type[] interfaces = collType.GetInterfaces();
foreach (Type interfaceType in interfaces)
{
if (interfaceType.IsGenericType == false)
{
continue;
}
Type genericTypeDef = interfaceType.GetGenericTypeDefinition();
if (object.ReferenceEquals(genericTypeDef, typeof(ICollection<>)) | object.ReferenceEquals(genericTypeDef, typeof(IDictionary<,>)))
{
return interfaceType;
}
}
// Try to see if it is a regular IList or IDictionary
if (typeof(IList<>).IsAssignableFrom(collType) == true)
{
return typeof(IList<>);
}
else if (typeof(IDictionary<,>).IsAssignableFrom(collType) == true)
{
return typeof(IDictionary<,>);
}
// Not a known collection type
return null;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
if ((MyTargetCollectionType != null))
{
this.EmitCollectionIn(ilg, services);
}
else
{
// Do the real emit
this.EmitListIn(ilg, services);
}
}
private void EmitCollectionIn(FleeILGenerator ilg, IServiceProvider services)
{
// Get the contains method
MethodInfo mi = this.GetCollectionContainsMethod();
ParameterInfo p1 = mi.GetParameters()[0];
// Load the collection
MyTargetCollectionElement.Emit(ilg, services);
// Load the argument
MyOperand.Emit(ilg, services);
// Do an implicit convert if necessary
ImplicitConverter.EmitImplicitConvert(MyOperand.ResultType, p1.ParameterType, ilg);
// Call the contains method
ilg.Emit(OpCodes.Callvirt, mi);
}
private MethodInfo GetCollectionContainsMethod()
{
string methodName = "Contains";
if (MyTargetCollectionType.IsGenericType == true && object.ReferenceEquals(MyTargetCollectionType.GetGenericTypeDefinition(), typeof(IDictionary<,>)))
{
methodName = "ContainsKey";
}
return MyTargetCollectionType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
}
private void EmitListIn(FleeILGenerator ilg, IServiceProvider services)
{
CompareElement ce = new CompareElement();
Label endLabel = ilg.DefineLabel();
Label trueTerminal = ilg.DefineLabel();
// Cache the operand since we will be comparing against it a lot
LocalBuilder lb = ilg.DeclareLocal(MyOperand.ResultType);
int targetIndex = lb.LocalIndex;
MyOperand.Emit(ilg, services);
Utility.EmitStoreLocal(ilg, targetIndex);
// Wrap our operand in a local shim
LocalBasedElement targetShim = new LocalBasedElement(MyOperand, targetIndex);
// Emit the compares
foreach (ExpressionElement argumentElement in MyArguments)
{
ce.Initialize(targetShim, argumentElement, LogicalCompareOperation.Equal);
ce.Emit(ilg, services);
EmitBranchToTrueTerminal(ilg, trueTerminal);
}
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Br_S, endLabel);
ilg.MarkLabel(trueTerminal);
ilg.Emit(OpCodes.Ldc_I4_1);
ilg.MarkLabel(endLabel);
}
private static void EmitBranchToTrueTerminal(FleeILGenerator ilg, Label trueTerminal)
{
ilg.EmitBranchTrue(trueTerminal);
}
public override System.Type ResultType => typeof(bool);
}
}

View File

@@ -0,0 +1,22 @@
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals
{
internal class BooleanLiteralElement : LiteralElement
{
private readonly bool _myValue;
public BooleanLiteralElement(bool value)
{
_myValue = value;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
EmitLoad(_myValue, ilg);
}
public override System.Type ResultType => typeof(bool);
}
}

View File

@@ -0,0 +1,24 @@
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals
{
internal class CharLiteralElement : LiteralElement
{
private readonly char _myValue;
public CharLiteralElement(char value)
{
_myValue = value;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
int intValue = Convert.ToInt32(_myValue);
EmitLoad(intValue, ilg);
}
public override System.Type ResultType => typeof(char);
}
}

View File

@@ -0,0 +1,42 @@
using System.Reflection;
using System.Reflection.Emit;
using System.Globalization;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.Literals
{
internal class DateTimeLiteralElement : LiteralElement
{
private DateTime _myValue;
public DateTimeLiteralElement(string image, ExpressionContext context)
{
ExpressionParserOptions options = context.ParserOptions;
if (DateTime.TryParseExact(image, options.DateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out _myValue) == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.CannotParseType, CompileExceptionReason.InvalidFormat, typeof(DateTime).Name);
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
int index = ilg.GetTempLocalIndex(typeof(DateTime));
Utility.EmitLoadLocalAddress(ilg, index);
LiteralElement.EmitLoad(_myValue.Ticks, ilg);
ConstructorInfo ci = typeof(DateTime).GetConstructor(new Type[] { typeof(long) });
ilg.Emit(OpCodes.Call, ci);
Utility.EmitLoadLocal(ilg, index);
}
public override System.Type ResultType => typeof(DateTime);
}
}

View File

@@ -0,0 +1,84 @@
using System.Globalization;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals.Integral
{
internal class Int32LiteralElement : IntegralLiteralElement
{
private Int32 _myValue;
private const string MinValue = "2147483648";
private readonly bool _myIsMinValue;
public Int32LiteralElement(Int32 value)
{
_myValue = value;
}
private Int32LiteralElement()
{
_myIsMinValue = true;
}
public static Int32LiteralElement TryCreate(string image, bool isHex, bool negated)
{
if (negated == true & image == MinValue)
{
return new Int32LiteralElement();
}
else if (isHex == true)
{
Int32 value = default(Int32);
// Since Int32.TryParse will succeed for a string like 0xFFFFFFFF we have to do some special handling
if (Int32.TryParse(image, NumberStyles.AllowHexSpecifier, null, out value) == false)
{
return null;
}
else if (value >= 0 & value <= Int32.MaxValue)
{
return new Int32LiteralElement(value);
}
else
{
return null;
}
}
else
{
Int32 value = default(Int32);
if (Int32.TryParse(image,out value) == true)
{
return new Int32LiteralElement(value);
}
else
{
return null;
}
}
}
public void Negate()
{
if (_myIsMinValue == true)
{
_myValue = Int32.MinValue;
}
else
{
_myValue = -_myValue;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
EmitLoad(_myValue, ilg);
}
public override System.Type ResultType => typeof(Int32);
public int Value => _myValue;
}
}

View File

@@ -0,0 +1,82 @@
using System.Globalization;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals.Integral
{
internal class Int64LiteralElement : IntegralLiteralElement
{
private Int64 _myValue;
private const string MinValue = "9223372036854775808";
private readonly bool _myIsMinValue;
public Int64LiteralElement(Int64 value)
{
_myValue = value;
}
private Int64LiteralElement()
{
_myIsMinValue = true;
}
public static Int64LiteralElement TryCreate(string image, bool isHex, bool negated)
{
if (negated == true & image == MinValue)
{
return new Int64LiteralElement();
}
else if (isHex == true)
{
Int64 value = default(Int64);
if (Int64.TryParse(image, NumberStyles.AllowHexSpecifier, null, out value) == false)
{
return null;
}
else if (value >= 0 & value <= Int64.MaxValue)
{
return new Int64LiteralElement(value);
}
else
{
return null;
}
}
else
{
Int64 value = default(Int64);
if (Int64.TryParse(image, out value) == true)
{
return new Int64LiteralElement(value);
}
else
{
return null;
}
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
EmitLoad(_myValue, ilg);
}
public void Negate()
{
if (_myIsMinValue == true)
{
_myValue = Int64.MinValue;
}
else
{
_myValue = -_myValue;
}
}
public override System.Type ResultType => typeof(Int64);
}
}

View File

@@ -0,0 +1,34 @@
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals.Integral
{
internal class UInt32LiteralElement : IntegralLiteralElement
{
private readonly UInt32 _myValue;
public UInt32LiteralElement(UInt32 value)
{
_myValue = value;
}
public static UInt32LiteralElement TryCreate(string image, System.Globalization.NumberStyles ns)
{
UInt32 value = default(UInt32);
if (UInt32.TryParse(image, ns, null, out value) == true)
{
return new UInt32LiteralElement(value);
}
else
{
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
EmitLoad(Convert.ToInt32(_myValue), ilg);
}
public override System.Type ResultType => typeof(UInt32);
}
}

View File

@@ -0,0 +1,34 @@
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals.Integral
{
internal class UInt64LiteralElement : IntegralLiteralElement
{
private readonly UInt64 _myValue;
public UInt64LiteralElement(string image, System.Globalization.NumberStyles ns)
{
try
{
_myValue = UInt64.Parse(image, ns);
}
catch (OverflowException ex)
{
base.OnParseOverflow(image);
}
}
public UInt64LiteralElement(UInt64 value)
{
_myValue = value;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
EmitLoad(Convert.ToInt64(_myValue), ilg);
}
public override System.Type ResultType => typeof(UInt64);
}
}

View File

@@ -0,0 +1,16 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals
{
internal class NullLiteralElement : LiteralElement
{
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
ilg.Emit(OpCodes.Ldnull);
}
public override System.Type ResultType => typeof(Null);
}
}

View File

@@ -0,0 +1,76 @@
using System.Reflection.Emit;
using System.Reflection;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.ExpressionElements.Literals.Real
{
internal class DecimalLiteralElement : RealLiteralElement
{
private static readonly ConstructorInfo OurConstructorInfo = GetConstructor();
private readonly decimal _myValue;
private DecimalLiteralElement()
{
}
public DecimalLiteralElement(decimal value)
{
_myValue = value;
}
private static ConstructorInfo GetConstructor()
{
Type[] types = {
typeof(Int32),
typeof(Int32),
typeof(Int32),
typeof(bool),
typeof(byte)
};
return typeof(decimal).GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, CallingConventions.Any, types, null);
}
public static DecimalLiteralElement Parse(string image, IServiceProvider services)
{
ExpressionParserOptions options = (ExpressionParserOptions)services.GetService(typeof(ExpressionParserOptions));
DecimalLiteralElement element = new DecimalLiteralElement();
try
{
decimal value = options.ParseDecimal(image);
return new DecimalLiteralElement(value);
}
catch (OverflowException ex)
{
element.OnParseOverflow(image);
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
int index = ilg.GetTempLocalIndex(typeof(decimal));
Utility.EmitLoadLocalAddress(ilg, index);
int[] bits = decimal.GetBits(_myValue);
EmitLoad(bits[0], ilg);
EmitLoad(bits[1], ilg);
EmitLoad(bits[2], ilg);
int flags = bits[3];
EmitLoad((flags >> 31) == -1, ilg);
EmitLoad(flags >> 16, ilg);
ilg.Emit(OpCodes.Call, OurConstructorInfo);
Utility.EmitLoadLocal(ilg, index);
}
public override System.Type ResultType => typeof(decimal);
}
}

View File

@@ -0,0 +1,46 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.ExpressionElements.Literals.Real
{
internal class DoubleLiteralElement : RealLiteralElement
{
private readonly double _myValue;
private DoubleLiteralElement()
{
}
public DoubleLiteralElement(double value)
{
_myValue = value;
}
public static DoubleLiteralElement Parse(string image, IServiceProvider services)
{
ExpressionParserOptions options = (ExpressionParserOptions)services.GetService(typeof(ExpressionParserOptions));
DoubleLiteralElement element = new DoubleLiteralElement();
try
{
double value = options.ParseDouble(image);
return new DoubleLiteralElement(value);
}
catch (OverflowException ex)
{
element.OnParseOverflow(image);
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
ilg.Emit(OpCodes.Ldc_R8, _myValue);
}
public override System.Type ResultType => typeof(double);
}
}

View File

@@ -0,0 +1,45 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.ExpressionElements.Literals.Real
{
internal class SingleLiteralElement : RealLiteralElement
{
private readonly float _myValue;
private SingleLiteralElement()
{
}
public SingleLiteralElement(float value)
{
_myValue = value;
}
public static SingleLiteralElement Parse(string image, IServiceProvider services)
{
ExpressionParserOptions options = (ExpressionParserOptions)services.GetService(typeof(ExpressionParserOptions));
SingleLiteralElement element = new SingleLiteralElement();
try
{
float value = options.ParseSingle(image);
return new SingleLiteralElement(value);
}
catch (OverflowException ex)
{
element.OnParseOverflow(image);
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
ilg.Emit(OpCodes.Ldc_R4, _myValue);
}
public override System.Type ResultType => typeof(float);
}
}

View File

@@ -0,0 +1,23 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.Literals
{
internal class StringLiteralElement : LiteralElement
{
private readonly string _myValue;
public StringLiteralElement(string value)
{
_myValue = value;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
ilg.Emit(OpCodes.Ldstr, _myValue);
}
public override System.Type ResultType => typeof(string);
}
}

View File

@@ -0,0 +1,38 @@
using System.Reflection;
using System.Reflection.Emit;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.Literals
{
internal class TimeSpanLiteralElement : LiteralElement
{
private TimeSpan _myValue;
public TimeSpanLiteralElement(string image)
{
if (TimeSpan.TryParse(image, out _myValue) == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.CannotParseType, CompileExceptionReason.InvalidFormat, typeof(TimeSpan).Name);
}
}
public override void Emit(FleeILGenerator ilg, System.IServiceProvider services)
{
int index = ilg.GetTempLocalIndex(typeof(TimeSpan));
Utility.EmitLoadLocalAddress(ilg, index);
LiteralElement.EmitLoad(_myValue.Ticks, ilg);
ConstructorInfo ci = typeof(TimeSpan).GetConstructor(new Type[] { typeof(long) });
ilg.Emit(OpCodes.Call, ci);
Utility.EmitLoadLocal(ilg, index);
}
public override System.Type ResultType => typeof(TimeSpan);
}
}

View File

@@ -0,0 +1,348 @@
using System.Collections;
using System.Diagnostics;
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.LogicalBitwise
{
internal class AndOrElement : BinaryExpressionElement
{
private AndOrOperation _myOperation;
private static readonly object OurTrueTerminalKey = new object();
private static readonly object OurFalseTerminalKey = new object();
private static readonly object OurEndLabelKey = new object();
public void New()
{
}
protected override void GetOperation(object operation)
{
_myOperation = (AndOrOperation)operation;
}
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
{
Type bitwiseOpType = Utility.GetBitwiseOpType(leftType, rightType);
if ((bitwiseOpType != null))
{
return bitwiseOpType;
}
else if (this.AreBothChildrenOfType(typeof(bool)))
{
return typeof(bool);
}
else
{
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
Type resultType = this.ResultType;
if (object.ReferenceEquals(resultType, typeof(bool)))
{
this.DoEmitLogical(ilg, services);
}
else
{
MyLeftChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, resultType, ilg);
MyRightChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, resultType, ilg);
EmitBitwiseOperation(ilg, _myOperation);
}
}
private static void EmitBitwiseOperation(FleeILGenerator ilg, AndOrOperation op)
{
switch (op)
{
case AndOrOperation.And:
ilg.Emit(OpCodes.And);
break;
case AndOrOperation.Or:
ilg.Emit(OpCodes.Or);
break;
default:
Debug.Fail("Unknown op type");
break;
}
}
private void DoEmitLogical(FleeILGenerator ilg, IServiceProvider services)
{
// We have to do a 'fake' emit so we can get the positions of the labels
ShortCircuitInfo info = new ShortCircuitInfo();
// Do the real emit
this.EmitLogical(ilg, info, services);
}
/// <summary>
/// Emit a short-circuited logical operation sequence
/// The idea: Store all the leaf operands in a stack with the leftmost at the top and rightmost at the bottom.
/// For each operand, emit it and try to find an end point for when it short-circuits. This means we go up through
/// the stack of operators (ignoring siblings) until we find a different operation (then emit a branch to its right operand)
/// or we reach the root (emit a branch to a true/false).
/// Repeat the process for all operands and then emit the true/false/last operand end cases.
/// </summary>
/// <param name="ilg"></param>
/// <param name="info"></param>
/// <param name="services"></param>
private void EmitLogical(FleeILGenerator ilg, ShortCircuitInfo info, IServiceProvider services)
{
// We always have an end label
Label endLabel = ilg.DefineLabel();
// Populate our data structures
this.PopulateData(info);
// Emit the sequence
EmitLogicalShortCircuit(ilg, info, services);
// Get the last operand
ExpressionElement terminalOperand = (ExpressionElement)info.Operands.Pop();
// Emit it
EmitOperand(terminalOperand, info, ilg, services);
// only 1-3 opcodes, always a short branch
ilg.EmitBranch(endLabel);
// Emit our true/false terminals
EmitTerminals(info, ilg, endLabel);
// Mark the end
ilg.MarkLabel(endLabel);
}
/// <summary>
/// Emit a sequence of and/or expressions with short-circuiting
/// </summary>
/// <param name="ilg"></param>
/// <param name="info"></param>
/// <param name="services"></param>
private static void EmitLogicalShortCircuit(FleeILGenerator ilg, ShortCircuitInfo info, IServiceProvider services)
{
while (info.Operators.Count != 0)
{
// Get the operator
AndOrElement op = (AndOrElement)info.Operators.Pop();
// Get the left operand
ExpressionElement leftOperand = (ExpressionElement)info.Operands.Pop();
// Emit the left
EmitOperand(leftOperand, info, ilg, services);
// Get the label for the short-circuit case
Label l = GetShortCircuitLabel(op, info, ilg);
// Emit the branch
EmitBranch(op, ilg, l, info);
}
}
private static void EmitBranch(AndOrElement op, FleeILGenerator ilg, Label target, ShortCircuitInfo info)
{
// Get the branch opcode
if (op._myOperation == AndOrOperation.And)
ilg.EmitBranchFalse(target);
else
ilg.EmitBranchTrue(target);
}
/// <summary>
/// Get the label for a short-circuit
/// </summary>
/// <param name="current"></param>
/// <param name="info"></param>
/// <param name="ilg"></param>
/// <returns></returns>
private static Label GetShortCircuitLabel(AndOrElement current, ShortCircuitInfo info, FleeILGenerator ilg)
{
// We modify the given stacks so we need to clone them
Stack cloneOperands = (Stack)info.Operands.Clone();
Stack cloneOperators = (Stack)info.Operators.Clone();
// Pop all siblings
current.PopRightChild(cloneOperands, cloneOperators);
// Go until we run out of operators
while (cloneOperators.Count > 0)
{
// Get the top operator
AndOrElement top = (AndOrElement)cloneOperators.Pop();
// Is is a different operation?
if (top._myOperation != current._myOperation)
{
// Yes, so return a label to its right operand
object nextOperand = cloneOperands.Pop();
return GetLabel(nextOperand, ilg, info);
}
else
{
// No, so keep going up the stack
top.PopRightChild(cloneOperands, cloneOperators);
}
}
// We've reached the end of the stack so return the label for the appropriate true/false terminal
if (current._myOperation == AndOrOperation.And)
{
return GetLabel(OurFalseTerminalKey, ilg, info);
}
else
{
return GetLabel(OurTrueTerminalKey, ilg, info);
}
}
private void PopRightChild(Stack operands, Stack operators)
{
AndOrElement andOrChild = MyRightChild as AndOrElement;
// What kind of child do we have?
if ((andOrChild != null))
{
// Another and/or expression so recurse
andOrChild.Pop(operands, operators);
}
else
{
// A terminal so pop it off the operands stack
operands.Pop();
}
}
/// <summary>
/// Recursively pop operators and operands
/// </summary>
/// <param name="operands"></param>
/// <param name="operators"></param>
private void Pop(Stack operands, Stack operators)
{
operators.Pop();
AndOrElement andOrChild = MyLeftChild as AndOrElement;
if (andOrChild == null)
{
operands.Pop();
}
else
{
andOrChild.Pop(operands, operators);
}
andOrChild = MyRightChild as AndOrElement;
if (andOrChild == null)
{
operands.Pop();
}
else
{
andOrChild.Pop(operands, operators);
}
}
private static void EmitOperand(ExpressionElement operand, ShortCircuitInfo info, FleeILGenerator ilg, IServiceProvider services)
{
// Is this operand the target of a label?
if (info.HasLabel(operand) == true)
{
// Yes, so mark it
Label leftLabel = info.FindLabel(operand);
ilg.MarkLabel(leftLabel);
}
// Emit the operand
operand.Emit(ilg, services);
}
/// <summary>
/// Emit the end cases for a short-circuit
/// </summary>
/// <param name="info"></param>
/// <param name="ilg"></param>
/// <param name="endLabel"></param>
private static void EmitTerminals(ShortCircuitInfo info, FleeILGenerator ilg, Label endLabel)
{
// Emit the false case if it was used
if (info.HasLabel(OurFalseTerminalKey) == true)
{
Label falseLabel = info.FindLabel(OurFalseTerminalKey);
// Mark the label and note its position
ilg.MarkLabel(falseLabel);
ilg.Emit(OpCodes.Ldc_I4_0);
// If we also have a true terminal, then skip over it
if (info.HasLabel(OurTrueTerminalKey) == true)
{
// only 1-3 opcodes, always a short branch
ilg.Emit(OpCodes.Br_S, endLabel);
}
}
// Emit the true case if it was used
if (info.HasLabel(OurTrueTerminalKey) == true)
{
Label trueLabel = info.FindLabel(OurTrueTerminalKey);
// Mark the label and note its position
ilg.MarkLabel(trueLabel);
ilg.Emit(OpCodes.Ldc_I4_1);
}
}
private static Label GetLabel(object key, FleeILGenerator ilg, ShortCircuitInfo info)
{
if (info.HasLabel(key))
return info.FindLabel(key);
return info.AddLabel(key, ilg.DefineLabel());
}
/// <summary>
/// Visit the nodes of the tree (right then left) and populate some data structures
/// </summary>
/// <param name="info"></param>
private void PopulateData(ShortCircuitInfo info)
{
// Is our right child a leaf or another And/Or expression?
AndOrElement andOrChild = MyRightChild as AndOrElement;
if (andOrChild == null)
{
// Leaf so push it on the stack
info.Operands.Push(MyRightChild);
}
else
{
// Another And/Or expression so recurse
andOrChild.PopulateData(info);
}
// Add ourselves as an operator
info.Operators.Push(this);
// Do the same thing for the left child
andOrChild = MyLeftChild as AndOrElement;
if (andOrChild == null)
{
info.Operands.Push(MyLeftChild);
}
else
{
andOrChild.PopulateData(info);
}
}
}
}

View File

@@ -0,0 +1,46 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.LogicalBitwise
{
internal class NotElement : UnaryElement
{
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
if (object.ReferenceEquals(MyChild.ResultType, typeof(bool)))
{
this.EmitLogical(ilg, services);
}
else
{
MyChild.Emit(ilg, services);
ilg.Emit(OpCodes.Not);
}
}
private void EmitLogical(FleeILGenerator ilg, IServiceProvider services)
{
MyChild.Emit(ilg, services);
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ceq);
}
protected override System.Type GetResultType(System.Type childType)
{
if (object.ReferenceEquals(childType, typeof(bool)))
{
return typeof(bool);
}
else if (Utility.IsIntegralType(childType) == true)
{
return childType;
}
else
{
return null;
}
}
}
}

View File

@@ -0,0 +1,44 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.LogicalBitwise
{
internal class XorElement : BinaryExpressionElement
{
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
{
Type bitwiseType = Utility.GetBitwiseOpType(leftType, rightType);
if ((bitwiseType != null))
{
return bitwiseType;
}
else if (this.AreBothChildrenOfType(typeof(bool)) == true)
{
return typeof(bool);
}
else
{
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
Type resultType = this.ResultType;
MyLeftChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(MyLeftChild.ResultType, resultType, ilg);
MyRightChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(MyRightChild.ResultType, resultType, ilg);
ilg.Emit(OpCodes.Xor);
}
protected override void GetOperation(object operation)
{
}
}
}

View File

@@ -0,0 +1,59 @@
using System.Collections;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Encapsulates an argument list")]
internal class ArgumentList
{
private readonly IList<ExpressionElement> _myElements;
public ArgumentList(ICollection elements)
{
ExpressionElement[] arr = new ExpressionElement[elements.Count];
elements.CopyTo(arr, 0);
_myElements = arr;
}
private string[] GetArgumentTypeNames()
{
List<string> l = new List<string>();
foreach (ExpressionElement e in _myElements)
{
l.Add(e.ResultType.Name);
}
return l.ToArray();
}
public Type[] GetArgumentTypes()
{
List<Type> l = new List<Type>();
foreach (ExpressionElement e in _myElements)
{
l.Add(e.ResultType);
}
return l.ToArray();
}
public override string ToString()
{
string[] typeNames = this.GetArgumentTypeNames();
return Utility.FormatList(typeNames);
}
public ExpressionElement[] ToArray()
{
ExpressionElement[] arr = new ExpressionElement[_myElements.Count];
_myElements.CopyTo(arr, 0);
return arr;
}
public ExpressionElement this[int index] => _myElements[index];
public int Count => _myElements.Count;
}
}

View File

@@ -0,0 +1,409 @@
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.ExpressionElements.Base.Literals;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Represents a function call")]
internal class FunctionCallElement : MemberElement
{
private readonly ArgumentList _myArguments;
private readonly ICollection<MethodInfo> _myMethods;
private CustomMethodInfo _myTargetMethodInfo;
private Type _myOnDemandFunctionReturnType;
public FunctionCallElement(string name, ArgumentList arguments)
{
this.MyName = name;
_myArguments = arguments;
}
internal FunctionCallElement(string name, ICollection<MethodInfo> methods, ArgumentList arguments)
{
MyName = name;
_myArguments = arguments;
_myMethods = methods;
}
protected override void ResolveInternal()
{
// Get the types of our arguments
Type[] argTypes = _myArguments.GetArgumentTypes();
// Find all methods with our name on the type
ICollection<MethodInfo> methods = _myMethods;
if (methods == null)
{
// Convert member info to method info
MemberInfo[] arr = this.GetMembers(MemberTypes.Method);
MethodInfo[] arr2 = new MethodInfo[arr.Length];
Array.Copy(arr, arr2, arr.Length);
methods = arr2;
}
if (methods.Count > 0)
{
// More than one method exists with this name
this.BindToMethod(methods, MyPrevious, argTypes);
return;
}
// No methods with this name exist; try to bind to an on-demand function
_myOnDemandFunctionReturnType = MyContext.Variables.ResolveOnDemandFunction(MyName, argTypes);
if (_myOnDemandFunctionReturnType == null)
{
// Failed to bind to a function
this.ThrowFunctionNotFoundException(MyPrevious);
}
}
private void ThrowFunctionNotFoundException(MemberElement previous)
{
if (previous == null)
{
base.ThrowCompileException(CompileErrorResourceKeys.UndefinedFunction, CompileExceptionReason.UndefinedName, MyName, _myArguments);
}
else
{
base.ThrowCompileException(CompileErrorResourceKeys.UndefinedFunctionOnType, CompileExceptionReason.UndefinedName, MyName, _myArguments, previous.TargetType.Name);
}
}
private void ThrowNoAccessibleMethodsException(MemberElement previous)
{
if (previous == null)
{
base.ThrowCompileException(CompileErrorResourceKeys.NoAccessibleMatches, CompileExceptionReason.AccessDenied, MyName, _myArguments);
}
else
{
base.ThrowCompileException(CompileErrorResourceKeys.NoAccessibleMatchesOnType, CompileExceptionReason.AccessDenied, MyName, _myArguments, previous.TargetType.Name);
}
}
private void ThrowAmbiguousMethodCallException()
{
base.ThrowCompileException(CompileErrorResourceKeys.AmbiguousCallOfFunction, CompileExceptionReason.AmbiguousMatch, MyName, _myArguments);
}
/// <summary>
/// Try to find a match from a set of methods
/// </summary>
/// <param name="methods"></param>
/// <param name="previous"></param>
/// <param name="argTypes"></param>
private void BindToMethod(ICollection<MethodInfo> methods, MemberElement previous, Type[] argTypes)
{
List<CustomMethodInfo> customInfos = new List<CustomMethodInfo>();
// Wrap the MethodInfos in our custom class
foreach (MethodInfo mi in methods)
{
CustomMethodInfo cmi = new CustomMethodInfo(mi);
customInfos.Add(cmi);
}
// Discard any methods that cannot qualify as overloads
CustomMethodInfo[] arr = customInfos.ToArray();
customInfos.Clear();
foreach (CustomMethodInfo cmi in arr)
{
if (cmi.IsMatch(argTypes, MyPrevious, MyContext) == true)
{
customInfos.Add(cmi);
}
}
if (customInfos.Count == 0)
{
// We have no methods that can qualify as overloads; throw exception
this.ThrowFunctionNotFoundException(previous);
}
else
{
// At least one method matches our criteria; do our custom overload resolution
this.ResolveOverloads(customInfos.ToArray(), previous, argTypes);
}
}
/// <summary>
/// Find the best match from a set of overloaded methods
/// </summary>
/// <param name="infos"></param>
/// <param name="previous"></param>
/// <param name="argTypes"></param>
private void ResolveOverloads(CustomMethodInfo[] infos, MemberElement previous, Type[] argTypes)
{
// Compute a score for each candidate
foreach (CustomMethodInfo cmi in infos)
{
cmi.ComputeScore(argTypes);
}
// Sort array from best to worst matches
Array.Sort<CustomMethodInfo>(infos);
// Discard any matches that aren't accessible
infos = this.GetAccessibleInfos(infos);
// No accessible methods left
if (infos.Length == 0)
{
this.ThrowNoAccessibleMethodsException(previous);
}
// Handle case where we have more than one match with the same score
this.DetectAmbiguousMatches(infos);
// If we get here, then there is only one best match
_myTargetMethodInfo = infos[0];
}
private CustomMethodInfo[] GetAccessibleInfos(CustomMethodInfo[] infos)
{
List<CustomMethodInfo> accessible = new List<CustomMethodInfo>();
foreach (CustomMethodInfo cmi in infos)
{
if (cmi.IsAccessible(this) == true)
{
accessible.Add(cmi);
}
}
return accessible.ToArray();
}
/// <summary>
/// Handle case where we have overloads with the same score
/// </summary>
/// <param name="infos"></param>
private void DetectAmbiguousMatches(CustomMethodInfo[] infos)
{
List<CustomMethodInfo> sameScores = new List<CustomMethodInfo>();
CustomMethodInfo first = infos[0];
// Find all matches with the same score as the best match
foreach (CustomMethodInfo cmi in infos)
{
if (((IEquatable<CustomMethodInfo>)cmi).Equals(first) == true)
{
sameScores.Add(cmi);
}
}
// More than one accessible match with the same score exists
if (sameScores.Count > 1)
{
this.ThrowAmbiguousMethodCallException();
}
}
protected override void Validate()
{
base.Validate();
if ((_myOnDemandFunctionReturnType != null))
{
return;
}
// Any function reference in an expression must return a value
if (object.ReferenceEquals(this.Method.ReturnType, typeof(void)))
{
base.ThrowCompileException(CompileErrorResourceKeys.FunctionHasNoReturnValue, CompileExceptionReason.FunctionHasNoReturnValue, MyName);
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
base.Emit(ilg, services);
ExpressionElement[] elements = _myArguments.ToArray();
// If we are an on-demand function, then emit that and exit
if ((_myOnDemandFunctionReturnType != null))
{
this.EmitOnDemandFunction(elements, ilg, services);
return;
}
bool isOwnerMember = MyOptions.IsOwnerType(this.Method.ReflectedType);
// Load the owner if required
if (MyPrevious == null && isOwnerMember == true && this.IsStatic == false)
{
this.EmitLoadOwner(ilg);
}
this.EmitFunctionCall(this.NextRequiresAddress, ilg, services);
}
private void EmitOnDemandFunction(ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
{
// Load the variable collection
EmitLoadVariables(ilg);
// Load the function name
ilg.Emit(OpCodes.Ldstr, MyName);
// Load the arguments array
EmitElementArrayLoad(elements, typeof(object), ilg, services);
// Call the function to get the result
MethodInfo mi = VariableCollection.GetFunctionInvokeMethod(_myOnDemandFunctionReturnType);
this.EmitMethodCall(mi, ilg);
}
// Emit the arguments to a paramArray method call
private void EmitParamArrayArguments(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
{
// Get the fixed parameters
ParameterInfo[] fixedParameters = new ParameterInfo[_myTargetMethodInfo.MyFixedArgTypes.Length];
Array.Copy(parameters, fixedParameters, fixedParameters.Length);
// Get the corresponding fixed parameters
ExpressionElement[] fixedElements = new ExpressionElement[_myTargetMethodInfo.MyFixedArgTypes.Length];
Array.Copy(elements, fixedElements, fixedElements.Length);
// Emit the fixed arguments
this.EmitRegularFunctionInternal(fixedParameters, fixedElements, ilg, services);
// Get the paramArray arguments
ExpressionElement[] paramArrayElements = new ExpressionElement[elements.Length - fixedElements.Length];
Array.Copy(elements, fixedElements.Length, paramArrayElements, 0, paramArrayElements.Length);
// Emit them into an array
EmitElementArrayLoad(paramArrayElements, _myTargetMethodInfo.ParamArrayElementType, ilg, services);
}
/// <summary>
/// Emit elements into an array
/// </summary>
/// <param name="elements"></param>
/// <param name="arrayElementType"></param>
/// <param name="ilg"></param>
/// <param name="services"></param>
private static void EmitElementArrayLoad(ExpressionElement[] elements, Type arrayElementType, FleeILGenerator ilg, IServiceProvider services)
{
// Load the array length
LiteralElement.EmitLoad(elements.Length, ilg);
// Create the array
ilg.Emit(OpCodes.Newarr, arrayElementType);
// Store the new array in a unique local and remember the index
LocalBuilder local = ilg.DeclareLocal(arrayElementType.MakeArrayType());
int arrayLocalIndex = local.LocalIndex;
Utility.EmitStoreLocal(ilg, arrayLocalIndex);
for (int i = 0; i <= elements.Length - 1; i++)
{
// Load the array
Utility.EmitLoadLocal(ilg, arrayLocalIndex);
// Load the index
LiteralElement.EmitLoad(i, ilg);
// Emit the element (with any required conversions)
ExpressionElement element = elements[i];
element.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(element.ResultType, arrayElementType, ilg);
// Store it into the array
Utility.EmitArrayStore(ilg, arrayElementType);
}
// Load the array
Utility.EmitLoadLocal(ilg, arrayLocalIndex);
}
public void EmitFunctionCall(bool nextRequiresAddress, FleeILGenerator ilg, IServiceProvider services)
{
ParameterInfo[] parameters = this.Method.GetParameters();
ExpressionElement[] elements = _myArguments.ToArray();
// Emit either a regular or paramArray call
if (_myTargetMethodInfo.IsParamArray == false)
{
if (_myTargetMethodInfo.IsExtensionMethod == false)
this.EmitRegularFunctionInternal(parameters, elements, ilg, services);
else
this.EmitExtensionFunctionInternal(parameters, elements, ilg, services);
}
else
{
this.EmitParamArrayArguments(parameters, elements, ilg, services);
}
MemberElement.EmitMethodCall(this.ResultType, nextRequiresAddress, this.Method, ilg);
}
private void EmitExtensionFunctionInternal(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
{
Debug.Assert(parameters.Length == elements.Length + 1, "argument count mismatch");
if (MyPrevious == null) this.EmitLoadOwner(ilg);
//Emit each element and any required conversions to the actual parameter type
for (int i = 1; i <= parameters.Length - 1; i++)
{
ExpressionElement element = elements[i - 1];
ParameterInfo pi = parameters[i];
element.Emit(ilg, services);
bool success = ImplicitConverter.EmitImplicitConvert(element.ResultType, pi.ParameterType, ilg);
Debug.Assert(success, "conversion failed");
}
}
/// <summary>
/// Emit the arguments to a regular method call
/// </summary>
/// <param name="parameters"></param>
/// <param name="elements"></param>
/// <param name="ilg"></param>
/// <param name="services"></param>
private void EmitRegularFunctionInternal(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
{
Debug.Assert(parameters.Length == elements.Length, "argument count mismatch");
// Emit each element and any required conversions to the actual parameter type
for (int i = 0; i <= parameters.Length - 1; i++)
{
ExpressionElement element = elements[i];
ParameterInfo pi = parameters[i];
element.Emit(ilg, services);
bool success = ImplicitConverter.EmitImplicitConvert(element.ResultType, pi.ParameterType, ilg);
Debug.Assert(success, "conversion failed");
}
}
/// <summary>
/// The method info we will be calling
/// </summary>
private MethodInfo Method => _myTargetMethodInfo.Target;
public override Type ResultType
{
get
{
if ((_myOnDemandFunctionReturnType != null))
{
return _myOnDemandFunctionReturnType;
}
else
{
return this.Method.ReturnType;
}
}
}
protected override bool RequiresAddress => !IsGetTypeMethod(this.Method);
protected override bool IsPublic => this.Method.IsPublic;
public override bool IsStatic => this.Method.IsStatic;
public override bool IsExtensionMethod => this._myTargetMethodInfo.IsExtensionMethod;
}
}

View File

@@ -0,0 +1,492 @@
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.ComponentModel;
using Flee.CalcEngine.PublicTypes;
using Flee.ExpressionElements.Base;
using Flee.ExpressionElements.Base.Literals;
using Flee.ExpressionElements.Literals;
using Flee.ExpressionElements.Literals.Integral;
using Flee.ExpressionElements.Literals.Real;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Represents an identifier")]
internal class IdentifierElement : MemberElement
{
private FieldInfo _myField;
private PropertyInfo _myProperty;
private PropertyDescriptor _myPropertyDescriptor;
private Type _myVariableType;
private Type _myCalcEngineReferenceType;
public IdentifierElement(string name)
{
this.MyName = name;
}
protected override void ResolveInternal()
{
// Try to bind to a field or property
if (this.ResolveFieldProperty(MyPrevious) == true)
{
this.AddReferencedVariable(MyPrevious);
return;
}
// Try to find a variable with our name
_myVariableType = MyContext.Variables.GetVariableTypeInternal(MyName);
// Variables are only usable as the first element
if (MyPrevious == null && (_myVariableType != null))
{
this.AddReferencedVariable(MyPrevious);
return;
}
CalculationEngine ce = MyContext.CalculationEngine;
if ((ce != null))
{
ce.AddDependency(MyName, MyContext);
_myCalcEngineReferenceType = ce.ResolveTailType(MyName);
return;
}
if (MyPrevious == null)
{
base.ThrowCompileException(CompileErrorResourceKeys.NoIdentifierWithName, CompileExceptionReason.UndefinedName, MyName);
}
else
{
base.ThrowCompileException(CompileErrorResourceKeys.NoIdentifierWithNameOnType, CompileExceptionReason.UndefinedName, MyName, MyPrevious.TargetType.Name);
}
}
private bool ResolveFieldProperty(MemberElement previous)
{
MemberInfo[] members = this.GetMembers(MemberTypes.Field | MemberTypes.Property);
// Keep only the ones which are accessible
members = this.GetAccessibleMembers(members);
if (members.Length == 0)
{
// No accessible members; try to resolve a virtual property
return this.ResolveVirtualProperty(previous);
}
else if (members.Length > 1)
{
// More than one accessible member
if (previous == null)
{
base.ThrowCompileException(CompileErrorResourceKeys.IdentifierIsAmbiguous, CompileExceptionReason.AmbiguousMatch, MyName);
}
else
{
base.ThrowCompileException(CompileErrorResourceKeys.IdentifierIsAmbiguousOnType, CompileExceptionReason.AmbiguousMatch, MyName, previous.TargetType.Name);
}
}
else
{
// Only one member; bind to it
_myField = members[0] as FieldInfo;
if ((_myField != null))
{
return true;
}
// Assume it must be a property
_myProperty = (PropertyInfo)members[0];
return true;
}
return false;
}
private bool ResolveVirtualProperty(MemberElement previous)
{
if (previous == null)
{
// We can't use virtual properties if we are the first element
return false;
}
PropertyDescriptorCollection coll = TypeDescriptor.GetProperties(previous.ResultType);
_myPropertyDescriptor = coll.Find(MyName, true);
return (_myPropertyDescriptor != null);
}
private void AddReferencedVariable(MemberElement previous)
{
if ((previous != null))
{
return;
}
if ((_myVariableType != null) || MyOptions.IsOwnerType(this.MemberOwnerType) == true)
{
ExpressionInfo info = (ExpressionInfo)MyServices.GetService(typeof(ExpressionInfo));
info.AddReferencedVariable(MyName);
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
base.Emit(ilg, services);
this.EmitFirst(ilg);
if ((_myCalcEngineReferenceType != null))
{
this.EmitReferenceLoad(ilg);
}
else if ((_myVariableType != null))
{
this.EmitVariableLoad(ilg);
}
else if ((_myField != null))
{
this.EmitFieldLoad(_myField, ilg, services);
}
else if ((_myPropertyDescriptor != null))
{
this.EmitVirtualPropertyLoad(ilg);
}
else
{
this.EmitPropertyLoad(_myProperty, ilg);
}
}
private void EmitReferenceLoad(FleeILGenerator ilg)
{
ilg.Emit(OpCodes.Ldarg_1);
MyContext.CalculationEngine.EmitLoad(MyName, ilg);
}
private void EmitFirst(FleeILGenerator ilg)
{
if ((MyPrevious != null))
{
return;
}
bool isVariable = (_myVariableType != null);
if (isVariable == true)
{
// Load variables
EmitLoadVariables(ilg);
}
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true & this.IsStatic == false)
{
this.EmitLoadOwner(ilg);
}
}
private void EmitVariableLoad(FleeILGenerator ilg)
{
MethodInfo mi = VariableCollection.GetVariableLoadMethod(_myVariableType);
ilg.Emit(OpCodes.Ldstr, MyName);
this.EmitMethodCall(mi, ilg);
}
private void EmitFieldLoad(System.Reflection.FieldInfo fi, FleeILGenerator ilg, IServiceProvider services)
{
if (fi.IsLiteral == true)
{
EmitLiteral(fi, ilg, services);
}
else if (this.ResultType.IsValueType == true & this.NextRequiresAddress == true)
{
EmitLdfld(fi, true, ilg);
}
else
{
EmitLdfld(fi, false, ilg);
}
}
private static void EmitLdfld(System.Reflection.FieldInfo fi, bool indirect, FleeILGenerator ilg)
{
if (fi.IsStatic == true)
{
if (indirect == true)
{
ilg.Emit(OpCodes.Ldsflda, fi);
}
else
{
ilg.Emit(OpCodes.Ldsfld, fi);
}
}
else
{
if (indirect == true)
{
ilg.Emit(OpCodes.Ldflda, fi);
}
else
{
ilg.Emit(OpCodes.Ldfld, fi);
}
}
}
/// <summary>
/// Emit the load of a constant field. We can't emit a ldsfld/ldfld of a constant so we have to get its value
/// and then emit a ldc.
/// </summary>
/// <param name="fi"></param>
/// <param name="ilg"></param>
/// <param name="services"></param>
private static void EmitLiteral(System.Reflection.FieldInfo fi, FleeILGenerator ilg, IServiceProvider services)
{
object value = fi.GetValue(null);
Type t = value.GetType();
TypeCode code = Type.GetTypeCode(t);
LiteralElement elem = default(LiteralElement);
switch (code)
{
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
elem = new Int32LiteralElement(System.Convert.ToInt32(value));
break;
case TypeCode.UInt32:
elem = new UInt32LiteralElement((UInt32)value);
break;
case TypeCode.Int64:
elem = new Int64LiteralElement((Int64)value);
break;
case TypeCode.UInt64:
elem = new UInt64LiteralElement((UInt64)value);
break;
case TypeCode.Double:
elem = new DoubleLiteralElement((double)value);
break;
case TypeCode.Single:
elem = new SingleLiteralElement((float)value);
break;
case TypeCode.Boolean:
elem = new BooleanLiteralElement((bool)value);
break;
case TypeCode.String:
elem = new StringLiteralElement((string)value);
break;
default:
elem = null;
Debug.Fail("Unsupported constant type");
break;
}
elem.Emit(ilg, services);
}
private void EmitPropertyLoad(System.Reflection.PropertyInfo pi, FleeILGenerator ilg)
{
System.Reflection.MethodInfo getter = pi.GetGetMethod(true);
base.EmitMethodCall(getter, ilg);
}
/// <summary>
/// Load a PropertyDescriptor based property
/// </summary>
/// <param name="ilg"></param>
private void EmitVirtualPropertyLoad(FleeILGenerator ilg)
{
// The previous value is already on the top of the stack but we need it at the bottom
// Get a temporary local index
int index = ilg.GetTempLocalIndex(MyPrevious.ResultType);
// Store the previous value there
Utility.EmitStoreLocal(ilg, index);
// Load the variable collection
EmitLoadVariables(ilg);
// Load the property name
ilg.Emit(OpCodes.Ldstr, MyName);
// Load the previous value and convert it to object
Utility.EmitLoadLocal(ilg, index);
ImplicitConverter.EmitImplicitConvert(MyPrevious.ResultType, typeof(object), ilg);
// Call the method to get the actual value
MethodInfo mi = VariableCollection.GetVirtualPropertyLoadMethod(this.ResultType);
this.EmitMethodCall(mi, ilg);
}
private Type MemberOwnerType
{
get
{
if ((_myField != null))
{
return _myField.ReflectedType;
}
else if ((_myPropertyDescriptor != null))
{
return _myPropertyDescriptor.ComponentType;
}
else if ((_myProperty != null))
{
return _myProperty.ReflectedType;
}
else
{
return null;
}
}
}
public override System.Type ResultType
{
get
{
if ((_myCalcEngineReferenceType != null))
{
return _myCalcEngineReferenceType;
}
else if ((_myVariableType != null))
{
return _myVariableType;
}
else if ((_myPropertyDescriptor != null))
{
return _myPropertyDescriptor.PropertyType;
}
else if ((_myField != null))
{
return _myField.FieldType;
}
else
{
MethodInfo mi = _myProperty.GetGetMethod(true);
return mi.ReturnType;
}
}
}
protected override bool RequiresAddress => _myPropertyDescriptor == null;
protected override bool IsPublic
{
get
{
if ((_myVariableType != null) | (_myCalcEngineReferenceType != null))
{
return true;
}
else if ((_myVariableType != null))
{
return true;
}
else if ((_myPropertyDescriptor != null))
{
return true;
}
else if ((_myField != null))
{
return _myField.IsPublic;
}
else
{
MethodInfo mi = _myProperty.GetGetMethod(true);
return mi.IsPublic;
}
}
}
protected override bool SupportsStatic
{
get
{
if ((_myVariableType != null))
{
// Variables never support static
return false;
}
else if ((_myPropertyDescriptor != null))
{
// Neither do virtual properties
return false;
}
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true && MyPrevious == null)
{
// Owner members support static if we are the first element
return true;
}
else
{
// Support static if we are the first (ie: we are a static import)
return MyPrevious == null;
}
}
}
protected override bool SupportsInstance
{
get
{
if ((_myVariableType != null))
{
// Variables always support instance
return true;
}
else if ((_myPropertyDescriptor != null))
{
// So do virtual properties
return true;
}
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true && MyPrevious == null)
{
// Owner members support instance if we are the first element
return true;
}
else
{
// We always support instance if we are not the first element
return (MyPrevious != null);
}
}
}
public override bool IsStatic
{
get
{
if ((_myVariableType != null) | (_myCalcEngineReferenceType != null))
{
return false;
}
else if ((_myVariableType != null))
{
return false;
}
else if ((_myField != null))
{
return _myField.IsStatic;
}
else if ((_myPropertyDescriptor != null))
{
return false;
}
else
{
MethodInfo mi = _myProperty.GetGetMethod(true);
return mi.IsStatic;
}
}
}
public override bool IsExtensionMethod => false;
}
}

View File

@@ -0,0 +1,181 @@
using System.Reflection;
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.MemberElements
{
[Obsolete("Element representing an array index")]
internal class IndexerElement : MemberElement
{
private ExpressionElement _myIndexerElement;
private readonly ArgumentList _myIndexerElements;
public IndexerElement(ArgumentList indexer)
{
_myIndexerElements = indexer;
}
protected override void ResolveInternal()
{
// Are we are indexing on an array?
Type target = MyPrevious.TargetType;
// Yes, so setup for an array index
if (target.IsArray == true)
{
this.SetupArrayIndexer();
return;
}
// Not an array, so try to find an indexer on the type
if (this.FindIndexer(target) == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.TypeNotArrayAndHasNoIndexerOfType, CompileExceptionReason.TypeMismatch, target.Name, _myIndexerElements);
}
}
private void SetupArrayIndexer()
{
_myIndexerElement = _myIndexerElements[0];
if (_myIndexerElements.Count > 1)
{
base.ThrowCompileException(CompileErrorResourceKeys.MultiArrayIndexNotSupported, CompileExceptionReason.TypeMismatch);
}
else if (ImplicitConverter.EmitImplicitConvert(_myIndexerElement.ResultType, typeof(Int32), null) == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.ArrayIndexersMustBeOfType, CompileExceptionReason.TypeMismatch, typeof(Int32).Name);
}
}
private bool FindIndexer(Type targetType)
{
// Get the default members
MemberInfo[] members = targetType.GetDefaultMembers();
List<MethodInfo> methods = new List<MethodInfo>();
// Use the first one that's valid for our indexer type
foreach (MemberInfo mi in members)
{
PropertyInfo pi = mi as PropertyInfo;
if ((pi != null))
{
methods.Add(pi.GetGetMethod(true));
}
}
FunctionCallElement func = new FunctionCallElement("Indexer", methods.ToArray(), _myIndexerElements);
func.Resolve(MyServices);
_myIndexerElement = func;
return true;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
base.Emit(ilg, services);
if (this.IsArray == true)
{
this.EmitArrayLoad(ilg, services);
}
else
{
this.EmitIndexer(ilg, services);
}
}
private void EmitArrayLoad(FleeILGenerator ilg, IServiceProvider services)
{
_myIndexerElement.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(_myIndexerElement.ResultType, typeof(Int32), ilg);
Type elementType = this.ResultType;
if (elementType.IsValueType == false)
{
// Simple reference load
ilg.Emit(OpCodes.Ldelem_Ref);
}
else
{
this.EmitValueTypeArrayLoad(ilg, elementType);
}
}
private void EmitValueTypeArrayLoad(FleeILGenerator ilg, Type elementType)
{
if (this.NextRequiresAddress == true)
{
ilg.Emit(OpCodes.Ldelema, elementType);
}
else
{
Utility.EmitArrayLoad(ilg, elementType);
}
}
private void EmitIndexer(FleeILGenerator ilg, IServiceProvider services)
{
FunctionCallElement func = (FunctionCallElement)_myIndexerElement;
func.EmitFunctionCall(this.NextRequiresAddress, ilg, services);
}
private Type ArrayType
{
get
{
if (this.IsArray == true)
{
return MyPrevious.TargetType;
}
else
{
return null;
}
}
}
private bool IsArray => MyPrevious.TargetType.IsArray;
protected override bool RequiresAddress => this.IsArray == false;
public override System.Type ResultType
{
get
{
if (this.IsArray == true)
{
return this.ArrayType.GetElementType();
}
else
{
return _myIndexerElement.ResultType;
}
}
}
protected override bool IsPublic
{
get
{
if (this.IsArray == true)
{
return true;
}
else
{
return IsElementPublic((MemberElement)_myIndexerElement);
}
}
}
public override bool IsStatic => false;
public override bool IsExtensionMethod => false;
}
}

View File

@@ -0,0 +1,120 @@
using System.Collections;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements.MemberElements
{
internal class InvocationListElement : ExpressionElement
{
private readonly MemberElement _myTail;
public InvocationListElement(IList elements, IServiceProvider services)
{
this.HandleFirstElement(elements, services);
LinkElements(elements);
Resolve(elements, services);
_myTail = (MemberElement)elements[elements.Count - 1];
}
/// <summary>
/// Arrange elements as a linked list
/// </summary>
/// <param name="elements"></param>
private static void LinkElements(IList elements)
{
for (int i = 0; i <= elements.Count - 1; i++)
{
MemberElement current = (MemberElement)elements[i];
MemberElement nextElement = null;
if (i + 1 < elements.Count)
{
nextElement = (MemberElement)elements[i + 1];
}
current.Link(nextElement);
}
}
private void HandleFirstElement(IList elements, IServiceProvider services)
{
ExpressionElement first = (ExpressionElement)elements[0];
// If the first element is not a member element, then we assume it is an expression and replace it with the correct member element
if (!(first is MemberElement))
{
ExpressionMemberElement actualFirst = new ExpressionMemberElement(first);
elements[0] = actualFirst;
}
else
{
this.ResolveNamespaces(elements, services);
}
}
private void ResolveNamespaces(IList elements, IServiceProvider services)
{
ExpressionContext context = (ExpressionContext)services.GetService(typeof(ExpressionContext));
ImportBase currentImport = context.Imports.RootImport;
while (true)
{
string name = GetName(elements);
if (name == null)
{
break; // TODO: might not be correct. Was : Exit While
}
ImportBase import = currentImport.FindImport(name);
if (import == null)
{
break; // TODO: might not be correct. Was : Exit While
}
currentImport = import;
elements.RemoveAt(0);
if (elements.Count > 0)
{
MemberElement newFirst = (MemberElement)elements[0];
newFirst.SetImport(currentImport);
}
}
if (elements.Count == 0)
{
base.ThrowCompileException(CompileErrorResourceKeys.NamespaceCannotBeUsedAsType, CompileExceptionReason.TypeMismatch, currentImport.Name);
}
}
private static string GetName(IList elements)
{
if (elements.Count == 0)
{
return null;
}
// Is the first member a field/property element?
var fpe = elements[0] as IdentifierElement;
return fpe?.MemberName;
}
private static void Resolve(IList elements, IServiceProvider services)
{
foreach (MemberElement element in elements)
{
element.Resolve(services);
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
_myTail.Emit(ilg, services);
}
public override System.Type ResultType => _myTail.ResultType;
}
}

View File

@@ -0,0 +1,38 @@
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
namespace Flee.ExpressionElements.MemberElements
{
internal class ExpressionMemberElement : MemberElement
{
private readonly ExpressionElement _myElement;
public ExpressionMemberElement(ExpressionElement element)
{
_myElement = element;
}
protected override void ResolveInternal()
{
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
base.Emit(ilg, services);
_myElement.Emit(ilg, services);
if (_myElement.ResultType.IsValueType == true)
{
EmitValueTypeLoadAddress(ilg, this.ResultType);
}
}
protected override bool SupportsInstance => true;
protected override bool IsPublic => true;
public override bool IsStatic => false;
public override bool IsExtensionMethod => false;
public override System.Type ResultType => _myElement.ResultType;
}
}

View File

@@ -0,0 +1,56 @@
using System.Reflection.Emit;
using System.Reflection;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
namespace Flee.ExpressionElements
{
internal class NegateElement : UnaryElement
{
public NegateElement()
{
}
protected override System.Type GetResultType(System.Type childType)
{
TypeCode tc = Type.GetTypeCode(childType);
MethodInfo mi = Utility.GetSimpleOverloadedOperator("UnaryNegation", childType, null);
if ((mi != null))
{
return mi.ReturnType;
}
switch (tc)
{
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Int32:
case TypeCode.Int64:
return childType;
case TypeCode.UInt32:
return typeof(Int64);
default:
return null;
}
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
Type resultType = this.ResultType;
MyChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(MyChild.ResultType, resultType, ilg);
MethodInfo mi = Utility.GetSimpleOverloadedOperator("UnaryNegation", resultType, null);
if (mi == null)
{
ilg.Emit(OpCodes.Neg);
}
else
{
ilg.Emit(OpCodes.Call, mi);
}
}
}
}

View File

@@ -0,0 +1,45 @@
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
using Flee.PublicTypes;
using Flee.Resources;
namespace Flee.ExpressionElements
{
internal class RootExpressionElement : ExpressionElement
{
private readonly ExpressionElement _myChild;
private readonly Type _myResultType;
public RootExpressionElement(ExpressionElement child, Type resultType)
{
_myChild = child;
_myResultType = resultType;
this.Validate();
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
_myChild.Emit(ilg, services);
ImplicitConverter.EmitImplicitConvert(_myChild.ResultType, _myResultType, ilg);
ExpressionOptions options = (ExpressionOptions)services.GetService(typeof(ExpressionOptions));
if (options.IsGeneric == false)
{
ImplicitConverter.EmitImplicitConvert(_myResultType, typeof(object), ilg);
}
ilg.Emit(OpCodes.Ret);
}
private void Validate()
{
if (ImplicitConverter.EmitImplicitConvert(_myChild.ResultType, _myResultType, null) == false)
{
base.ThrowCompileException(CompileErrorResourceKeys.CannotConvertTypeToExpressionResult, CompileExceptionReason.TypeMismatch, _myChild.ResultType.Name, _myResultType.Name);
}
}
public override System.Type ResultType => typeof(object);
}
}

136
ExpressionElements/Shift.cs Normal file
View File

@@ -0,0 +1,136 @@
using System.Diagnostics;
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.InternalTypes;
namespace Flee.ExpressionElements
{
internal class ShiftElement : BinaryExpressionElement
{
private ShiftOperation _myOperation;
public ShiftElement()
{
}
protected override System.Type GetResultType(System.Type leftType, System.Type rightType)
{
// Right argument (shift count) must be convertible to int32
if (ImplicitConverter.EmitImplicitNumericConvert(rightType, typeof(Int32), null) == false)
{
return null;
}
// Left argument must be an integer type
if (Utility.IsIntegralType(leftType) == false)
{
return null;
}
TypeCode tc = Type.GetTypeCode(leftType);
switch (tc)
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
return typeof(Int32);
case TypeCode.UInt32:
return typeof(UInt32);
case TypeCode.Int64:
return typeof(Int64);
case TypeCode.UInt64:
return typeof(UInt64);
default:
Debug.Assert(false, "unknown left shift operand");
return null;
}
}
protected override void GetOperation(object operation)
{
_myOperation = (ShiftOperation)operation;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
MyLeftChild.Emit(ilg, services);
this.EmitShiftCount(ilg, services);
this.EmitShift(ilg);
}
// If the shift count is greater than the number of bits in the number, the result is undefined.
// So we play it safe and force the shift count to 32/64 bits by ANDing it with the appropriate mask.
private void EmitShiftCount(FleeILGenerator ilg, IServiceProvider services)
{
MyRightChild.Emit(ilg, services);
TypeCode tc = Type.GetTypeCode(MyLeftChild.ResultType);
switch (tc)
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(0x1f));
break;
case TypeCode.Int64:
case TypeCode.UInt64:
ilg.Emit(OpCodes.Ldc_I4_S, Convert.ToSByte(0x3f));
break;
default:
Debug.Assert(false, "unknown left shift operand");
break;
}
ilg.Emit(OpCodes.And);
}
private void EmitShift(FleeILGenerator ilg)
{
TypeCode tc = Type.GetTypeCode(MyLeftChild.ResultType);
OpCode op = default(OpCode);
switch (tc)
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.Int64:
// Signed operand, emit a left shift or arithmetic right shift
if (_myOperation == ShiftOperation.LeftShift)
{
op = OpCodes.Shl;
}
else
{
op = OpCodes.Shr;
}
break;
case TypeCode.UInt32:
case TypeCode.UInt64:
// Unsigned operand, emit left shift or logical right shift
if (_myOperation == ShiftOperation.LeftShift)
{
op = OpCodes.Shl;
}
else
{
op = OpCodes.Shr_Un;
}
break;
default:
Debug.Assert(false, "unknown left shift operand");
break;
}
ilg.Emit(op);
}
}
}

View File

@@ -0,0 +1,285 @@
using System.Reflection.Emit;
namespace Flee.InternalTypes
{
[Obsolete("Manages branch information and allows us to determine if we should emit a short or long branch")]
internal class BranchManager
{
private readonly IList<BranchInfo> MyBranchInfos;
public BranchManager()
{
MyBranchInfos = new List<BranchInfo>();
}
/// <summary>
/// check if any long branches exist
/// </summary>
/// <returns></returns>
public bool HasLongBranches()
{
foreach (BranchInfo bi in MyBranchInfos)
{
if (bi.ComputeIsLongBranch()) return true;
}
return false;
}
/// <summary>
/// Determine whether to use short or long branches.
/// This advances the ilg offset with No-op to adjust
/// for the long branches needed.
/// </summary>
/// <remarks></remarks>
public bool ComputeBranches()
{
//
// we need to iterate in reverse order of the
// starting location, as branch between our
// branch could push our branch to a long branch.
//
for( var idx=MyBranchInfos.Count-1; idx >= 0; idx--)
{
var bi = MyBranchInfos[idx];
// count long branches between
int longBranchesBetween = 0;
for( var ii=idx+1; ii < MyBranchInfos.Count; ii++)
{
var bi2 = MyBranchInfos[ii];
if (bi2.IsBetween(bi) && bi2.ComputeIsLongBranch())
++longBranchesBetween;
}
// Adjust the branch as necessary
bi.AdjustForLongBranchesBetween(longBranchesBetween);
}
int longBranchCount = 0;
// Adjust the start location of each branch
foreach (BranchInfo bi in MyBranchInfos)
{
// Save the short/long branch type
bi.BakeIsLongBranch();
// Adjust the start location as necessary
bi.AdjustForLongBranches(longBranchCount);
// Keep a tally of the number of long branches
longBranchCount += Convert.ToInt32(bi.IsLongBranch);
}
return (longBranchCount > 0);
}
/// <summary>
/// Determine if a branch from a point to a label will be long
/// </summary>
/// <param name="ilg"></param>
/// <returns></returns>
/// <remarks></remarks>
public bool IsLongBranch(FleeILGenerator ilg)
{
ILLocation startLoc = new ILLocation(ilg.Length);
foreach (var bi in MyBranchInfos)
{
if (bi.Equals(startLoc))
return bi.IsLongBranch;
}
// we don't really know since this branch didn't exist.
// we could throw an exceptio but
// do a long branch to be safe.
return true;
}
/// <summary>
/// Add a branch from a location to a target label
/// </summary>
/// <param name="ilg"></param>
/// <param name="target"></param>
/// <remarks></remarks>
public void AddBranch(FleeILGenerator ilg, Label target)
{
ILLocation startLoc = new ILLocation(ilg.Length);
BranchInfo bi = new BranchInfo(startLoc, target);
// branches will be sorted in order
MyBranchInfos.Add(bi);
}
/// <summary>
/// Set the position for a label
/// </summary>
/// <param name="ilg"></param>
/// <param name="target"></param>
/// <remarks></remarks>
public void MarkLabel(FleeILGenerator ilg, Label target)
{
int pos = ilg.Length;
foreach (BranchInfo bi in MyBranchInfos)
{
bi.Mark(target, pos);
}
}
public override string ToString()
{
string[] arr = new string[MyBranchInfos.Count];
for (int i = 0; i <= MyBranchInfos.Count - 1; i++)
{
arr[i] = MyBranchInfos[i].ToString();
}
return string.Join(System.Environment.NewLine, arr);
}
}
[Obsolete("Represents a location in an IL stream")]
internal class ILLocation : IEquatable<ILLocation>, IComparable<ILLocation>
{
private int _myPosition;
/// <summary>
/// ' Long branch is 5 bytes; short branch is 2; so we adjust by the difference
/// </summary>
private const int LongBranchAdjust = 3;
/// <summary>
/// Length of the Br_s opcode
/// </summary>
private const int BrSLength = 2;
public ILLocation()
{
}
public ILLocation(int position)
{
_myPosition = position;
}
public void SetPosition(int position)
{
_myPosition = position;
}
/// <summary>
/// Adjust our position by a certain amount of long branches
/// </summary>
/// <param name="longBranchCount"></param>
/// <remarks></remarks>
public void AdjustForLongBranch(int longBranchCount)
{
_myPosition += longBranchCount * LongBranchAdjust;
}
/// <summary>
/// Determine if this branch is long
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
/// <remarks></remarks>
public bool IsLongBranch(ILLocation target)
{
// The branch offset is relative to the instruction *after* the branch so we add 2 (length of a br_s) to our position
return Utility.IsLongBranch(_myPosition + BrSLength, target._myPosition);
}
public bool Equals1(ILLocation other)
{
return _myPosition == other._myPosition;
}
bool System.IEquatable<ILLocation>.Equals(ILLocation other)
{
return Equals1(other);
}
public override string ToString()
{
return _myPosition.ToString("x");
}
public int CompareTo(ILLocation other)
{
return _myPosition.CompareTo(other._myPosition);
}
}
[Obsolete("Represents a branch from a start location to an end location")]
internal class BranchInfo
{
private readonly ILLocation _myStart;
private readonly ILLocation _myEnd;
private Label _myLabel;
private bool _myIsLongBranch;
public BranchInfo(ILLocation startLocation, Label endLabel)
{
_myStart = startLocation;
_myLabel = endLabel;
_myEnd = new ILLocation();
}
public void AdjustForLongBranches(int longBranchCount)
{
_myStart.AdjustForLongBranch(longBranchCount);
// end not necessarily needed once we determine
// if this is long, but keep it accurate anyway.
_myEnd.AdjustForLongBranch(longBranchCount);
}
public void BakeIsLongBranch()
{
_myIsLongBranch = this.ComputeIsLongBranch();
}
public void AdjustForLongBranchesBetween(int betweenLongBranchCount)
{
_myEnd.AdjustForLongBranch(betweenLongBranchCount);
}
public bool IsBetween(BranchInfo other)
{
return _myStart.CompareTo(other._myStart) > 0 && _myStart.CompareTo(other._myEnd) < 0;
}
public bool ComputeIsLongBranch()
{
return _myStart.IsLongBranch(_myEnd);
}
public void Mark(Label target, int position)
{
if (_myLabel.Equals(target) == true)
{
_myEnd.SetPosition(position);
}
}
/// <summary>
/// We only need to compare the start point. Can only have a single
/// brach from the exact address, so if label doesn't match we have
/// bigger problems.
/// </summary>
/// <param name="other"></param>
/// <returns></returns>
public bool Equals(ILLocation start)
{
return _myStart.Equals1(start);
}
public override string ToString()
{
return $"{_myStart} -> {_myEnd} (L={_myStart.IsLongBranch(_myEnd)})";
}
public bool IsLongBranch => _myIsLongBranch;
}
}

215
InternalTypes/Expression.cs Normal file
View File

@@ -0,0 +1,215 @@
using System.ComponentModel.Design;
using System.Reflection.Emit;
using System.Reflection;
using Flee.ExpressionElements;
using Flee.ExpressionElements.Base;
using Flee.PublicTypes;
using Flee.Resources;
using IDynamicExpression = Flee.PublicTypes.IDynamicExpression;
namespace Flee.InternalTypes
{
internal class Expression<T> : IExpression, IDynamicExpression, IGenericExpression<T>
{
private readonly string _myExpression;
private ExpressionContext _myContext;
private ExpressionOptions _myOptions;
private readonly ExpressionInfo _myInfo;
private ExpressionEvaluator<T> _myEvaluator;
private object _myOwner;
private const string EmitAssemblyName = "FleeExpression";
private const string DynamicMethodName = "Flee Expression";
public Expression(string expression, ExpressionContext context, bool isGeneric)
{
Utility.AssertNotNull(expression, "expression");
_myExpression = expression;
_myOwner = context.ExpressionOwner;
_myContext = context;
if (context.NoClone == false)
{
_myContext = context.CloneInternal(false);
}
_myInfo = new ExpressionInfo();
this.SetupOptions(_myContext.Options, isGeneric);
_myContext.Imports.ImportOwner(_myOptions.OwnerType);
this.ValidateOwner(_myOwner);
this.Compile(expression, _myOptions);
_myContext.CalculationEngine?.FixTemporaryHead(this, _myContext, _myOptions.ResultType);
}
private void SetupOptions(ExpressionOptions options, bool isGeneric)
{
// Make sure we clone the options
_myOptions = options;
_myOptions.IsGeneric = isGeneric;
if (isGeneric)
{
_myOptions.ResultType = typeof(T);
}
_myOptions.SetOwnerType(_myOwner.GetType());
}
private void Compile(string expression, ExpressionOptions options)
{
// Add the services that will be used by elements during the compile
IServiceContainer services = new ServiceContainer();
this.AddServices(services);
// Parse and get the root element of the parse tree
ExpressionElement topElement = _myContext.Parse(expression, services);
if (options.ResultType == null)
{
options.ResultType = topElement.ResultType;
}
RootExpressionElement rootElement = new RootExpressionElement(topElement, options.ResultType);
DynamicMethod dm = this.CreateDynamicMethod();
FleeILGenerator ilg = new FleeILGenerator(dm.GetILGenerator());
// Emit the IL
rootElement.Emit(ilg, services);
if (ilg.NeedsSecondPass())
{
// second pass required due to long branches.
dm = this.CreateDynamicMethod();
ilg.PrepareSecondPass(dm.GetILGenerator());
rootElement.Emit(ilg, services);
}
ilg.ValidateLength();
// Emit to an assembly if required
if (options.EmitToAssembly == true)
{
EmitToAssembly(ilg, rootElement, services);
}
Type delegateType = typeof(ExpressionEvaluator<>).MakeGenericType(typeof(T));
_myEvaluator = (ExpressionEvaluator<T>)dm.CreateDelegate(delegateType);
}
private DynamicMethod CreateDynamicMethod()
{
// Create the dynamic method
Type[] parameterTypes = {
typeof(object),
typeof(ExpressionContext),
typeof(VariableCollection)
};
DynamicMethod dm = default(DynamicMethod);
dm = new DynamicMethod(DynamicMethodName, typeof(T), parameterTypes, _myOptions.OwnerType);
return dm;
}
private void AddServices(IServiceContainer dest)
{
dest.AddService(typeof(ExpressionOptions), _myOptions);
dest.AddService(typeof(ExpressionParserOptions), _myContext.ParserOptions);
dest.AddService(typeof(ExpressionContext), _myContext);
dest.AddService(typeof(IExpression), this);
dest.AddService(typeof(ExpressionInfo), _myInfo);
}
/// <summary>
/// Emit to an assembly. We've already computed long branches at this point,
/// so we emit as a second pass
/// </summary>
/// <param name="ilg"></param>
/// <param name="rootElement"></param>
/// <param name="services"></param>
private static void EmitToAssembly(FleeILGenerator ilg, ExpressionElement rootElement, IServiceContainer services)
{
AssemblyName assemblyName = new AssemblyName(EmitAssemblyName);
string assemblyFileName = string.Format("{0}.dll", EmitAssemblyName);
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyFileName);
MethodBuilder mb = moduleBuilder.DefineGlobalMethod("Evaluate", MethodAttributes.Public | MethodAttributes.Static, typeof(T), new Type[] {
typeof(object),typeof(ExpressionContext),typeof(VariableCollection)});
// already emitted once for local use,
ilg.PrepareSecondPass(mb.GetILGenerator());
rootElement.Emit(ilg, services);
moduleBuilder.CreateGlobalFunctions();
//assemblyBuilder.Save(assemblyFileName);
assemblyBuilder.CreateInstance(assemblyFileName);
}
private void ValidateOwner(object owner)
{
Utility.AssertNotNull(owner, "owner");
if (_myOptions.OwnerType.IsAssignableFrom(owner.GetType()) == false)
{
string msg = Utility.GetGeneralErrorMessage(GeneralErrorResourceKeys.NewOwnerTypeNotAssignableToCurrentOwner);
throw new ArgumentException(msg);
}
}
public object Evaluate()
{
return _myEvaluator(_myOwner, _myContext, _myContext.Variables);
}
public T EvaluateGeneric()
{
return _myEvaluator(_myOwner, _myContext, _myContext.Variables);
}
T IGenericExpression<T>.Evaluate()
{
return EvaluateGeneric();
}
public IExpression Clone()
{
Expression<T> copy = (Expression<T>)this.MemberwiseClone();
copy._myContext = _myContext.CloneInternal(true);
copy._myOptions = copy._myContext.Options;
return copy;
}
public override string ToString()
{
return _myExpression;
}
internal Type ResultType => _myOptions.ResultType;
public string Text => _myExpression;
public ExpressionInfo Info1 => _myInfo;
ExpressionInfo IExpression.Info => Info1;
public object Owner
{
get { return _myOwner; }
set
{
this.ValidateOwner(value);
_myOwner = value;
}
}
public ExpressionContext Context => _myContext;
}
}

View File

@@ -0,0 +1,269 @@
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
namespace Flee.InternalTypes
{
internal class FleeILGenerator
{
private ILGenerator _myIlGenerator;
private int _myLength;
private int _myLabelCount;
private readonly Dictionary<Type, LocalBuilder> _localBuilderTemp;
private int _myPass;
private int _brContext;
private BranchManager _bm;
public FleeILGenerator(ILGenerator ilg)
{
_myIlGenerator = ilg;
_localBuilderTemp = new Dictionary<Type, LocalBuilder>();
_myLength = 0;
_myPass = 1;
_bm = new BranchManager();
}
public int GetTempLocalIndex(Type localType)
{
LocalBuilder local = null;
if (_localBuilderTemp.TryGetValue(localType, out local) == false)
{
local = _myIlGenerator.DeclareLocal(localType);
_localBuilderTemp.Add(localType, local);
}
return local.LocalIndex;
}
/// <summary>
/// after first pass, check for long branches.
/// If any, we need to generate again.
/// </summary>
/// <returns></returns>
public bool NeedsSecondPass()
{
return _bm.HasLongBranches();
}
/// <summary>
/// need a new ILGenerator for 2nd pass. This can also
/// get called for a 3rd pass when emitting to assembly.
/// </summary>
/// <param name="ilg"></param>
public void PrepareSecondPass(ILGenerator ilg)
{
_bm.ComputeBranches();
_localBuilderTemp.Clear();
_myIlGenerator = ilg;
_myLength = 0;
_myPass++;
}
public void Emit(OpCode op)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op);
}
public void Emit(OpCode op, Type arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, ConstructorInfo arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, MethodInfo arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, FieldInfo arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, byte arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, sbyte arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, short arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, int arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, long arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, float arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, double arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, string arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void Emit(OpCode op, Label arg)
{
this.RecordOpcode(op);
_myIlGenerator.Emit(op, arg);
}
public void EmitBranch(Label arg)
{
if (_myPass == 1)
{
_bm.AddBranch(this, arg);
Emit(OpCodes.Br_S, arg);
}
else if (_bm.IsLongBranch(this) == false)
{
Emit(OpCodes.Br_S, arg);
}
else
{
Emit(OpCodes.Br, arg);
}
}
public void EmitBranchFalse(Label arg)
{
if (_myPass == 1)
{
_bm.AddBranch(this, arg);
Emit(OpCodes.Brfalse_S, arg);
}
else if (_bm.IsLongBranch(this) == false)
{
Emit(OpCodes.Brfalse_S, arg);
}
else
{
Emit(OpCodes.Brfalse, arg);
}
}
public void EmitBranchTrue(Label arg)
{
if (_myPass == 1)
{
_bm.AddBranch(this, arg);
Emit(OpCodes.Brtrue_S, arg);
}
else if (_bm.IsLongBranch(this) == false)
{
Emit(OpCodes.Brtrue_S, arg);
}
else
{
Emit(OpCodes.Brtrue, arg);
}
}
public void MarkLabel(Label lbl)
{
_myIlGenerator.MarkLabel(lbl);
_bm.MarkLabel(this, lbl);
}
public Label DefineLabel()
{
_myLabelCount += 1;
var label = _myIlGenerator.DefineLabel();
return label;
}
public LocalBuilder DeclareLocal(Type localType)
{
return _myIlGenerator.DeclareLocal(localType);
}
private void RecordOpcode(OpCode op)
{
//Trace.WriteLine(String.Format("{0:x}: {1}", MyLength, op.Name))
int operandLength = GetOpcodeOperandSize(op.OperandType);
_myLength += op.Size + operandLength;
}
private static int GetOpcodeOperandSize(OperandType operand)
{
switch (operand)
{
case OperandType.InlineNone:
return 0;
case OperandType.ShortInlineBrTarget:
case OperandType.ShortInlineI:
case OperandType.ShortInlineVar:
return 1;
case OperandType.InlineVar:
return 2;
case OperandType.InlineBrTarget:
case OperandType.InlineField:
case OperandType.InlineI:
case OperandType.InlineMethod:
case OperandType.InlineSig:
case OperandType.InlineString:
case OperandType.InlineTok:
case OperandType.InlineType:
case OperandType.ShortInlineR:
return 4;
case OperandType.InlineI8:
case OperandType.InlineR:
return 8;
default:
Debug.Fail("Unknown operand type");
break;
}
return 0;
}
[Conditional("DEBUG")]
public void ValidateLength()
{
Debug.Assert(this.Length == this.ILGeneratorLength, "ILGenerator length mismatch");
}
public int Length => _myLength;
public int LabelCount => _myLabelCount;
private int ILGeneratorLength => Utility.GetILGeneratorLength(_myIlGenerator);
}
}

View File

@@ -0,0 +1,573 @@
using System.Diagnostics;
using System.Reflection.Emit;
using System.Reflection;
namespace Flee.InternalTypes
{
internal class ImplicitConverter
{
/// <summary>
/// Table of results for binary operations using primitives
/// </summary>
private static readonly Type[,] OurBinaryResultTable;
/// <summary>
/// Primitive types we support
/// </summary>
private static readonly Type[] OurBinaryTypes;
static ImplicitConverter()
{
// Create a table with all the primitive types
Type[] types = {
typeof(char),
typeof(byte),
typeof(sbyte),
typeof(Int16),
typeof(UInt16),
typeof(Int32),
typeof(UInt32),
typeof(Int64),
typeof(UInt64),
typeof(float),
typeof(double)
};
OurBinaryTypes = types;
Type[,] table = new Type[types.Length, types.Length];
OurBinaryResultTable = table;
FillIdentities(types, table);
// Fill the table
AddEntry(typeof(UInt32), typeof(UInt64), typeof(UInt64));
AddEntry(typeof(Int32), typeof(Int64), typeof(Int64));
AddEntry(typeof(UInt32), typeof(Int64), typeof(Int64));
AddEntry(typeof(Int32), typeof(UInt32), typeof(Int64));
AddEntry(typeof(UInt32), typeof(float), typeof(float));
AddEntry(typeof(UInt32), typeof(double), typeof(double));
AddEntry(typeof(Int32), typeof(float), typeof(float));
AddEntry(typeof(Int32), typeof(double), typeof(double));
AddEntry(typeof(Int64), typeof(float), typeof(float));
AddEntry(typeof(Int64), typeof(double), typeof(double));
AddEntry(typeof(UInt64), typeof(float), typeof(float));
AddEntry(typeof(UInt64), typeof(double), typeof(double));
AddEntry(typeof(float), typeof(double), typeof(double));
// Byte
AddEntry(typeof(byte), typeof(byte), typeof(Int32));
AddEntry(typeof(byte), typeof(sbyte), typeof(Int32));
AddEntry(typeof(byte), typeof(Int16), typeof(Int32));
AddEntry(typeof(byte), typeof(UInt16), typeof(Int32));
AddEntry(typeof(byte), typeof(Int32), typeof(Int32));
AddEntry(typeof(byte), typeof(UInt32), typeof(UInt32));
AddEntry(typeof(byte), typeof(Int64), typeof(Int64));
AddEntry(typeof(byte), typeof(UInt64), typeof(UInt64));
AddEntry(typeof(byte), typeof(float), typeof(float));
AddEntry(typeof(byte), typeof(double), typeof(double));
// SByte
AddEntry(typeof(sbyte), typeof(sbyte), typeof(Int32));
AddEntry(typeof(sbyte), typeof(Int16), typeof(Int32));
AddEntry(typeof(sbyte), typeof(UInt16), typeof(Int32));
AddEntry(typeof(sbyte), typeof(Int32), typeof(Int32));
AddEntry(typeof(sbyte), typeof(UInt32), typeof(long));
AddEntry(typeof(sbyte), typeof(Int64), typeof(Int64));
//invalid -- AddEntry(GetType(SByte), GetType(UInt64), GetType(UInt64))
AddEntry(typeof(sbyte), typeof(float), typeof(float));
AddEntry(typeof(sbyte), typeof(double), typeof(double));
// int16
AddEntry(typeof(Int16), typeof(Int16), typeof(Int32));
AddEntry(typeof(Int16), typeof(UInt16), typeof(Int32));
AddEntry(typeof(Int16), typeof(Int32), typeof(Int32));
AddEntry(typeof(Int16), typeof(UInt32), typeof(long));
AddEntry(typeof(Int16), typeof(Int64), typeof(Int64));
//invalid -- AddEntry(GetType(Int16), GetType(UInt64), GetType(UInt64))
AddEntry(typeof(Int16), typeof(float), typeof(float));
AddEntry(typeof(Int16), typeof(double), typeof(double));
// Uint16
AddEntry(typeof(UInt16), typeof(UInt16), typeof(Int32));
AddEntry(typeof(UInt16), typeof(Int16), typeof(Int32));
AddEntry(typeof(UInt16), typeof(Int32), typeof(Int32));
AddEntry(typeof(UInt16), typeof(UInt32), typeof(UInt32));
AddEntry(typeof(UInt16), typeof(Int64), typeof(Int64));
AddEntry(typeof(UInt16), typeof(UInt64), typeof(UInt64));
AddEntry(typeof(UInt16), typeof(float), typeof(float));
AddEntry(typeof(UInt16), typeof(double), typeof(double));
// Char
AddEntry(typeof(char), typeof(char), typeof(Int32));
AddEntry(typeof(char), typeof(UInt16), typeof(UInt16));
AddEntry(typeof(char), typeof(Int32), typeof(Int32));
AddEntry(typeof(char), typeof(UInt32), typeof(UInt32));
AddEntry(typeof(char), typeof(Int64), typeof(Int64));
AddEntry(typeof(char), typeof(UInt64), typeof(UInt64));
AddEntry(typeof(char), typeof(float), typeof(float));
AddEntry(typeof(char), typeof(double), typeof(double));
}
private ImplicitConverter()
{
}
private static void FillIdentities(Type[] typeList, Type[,] table)
{
for (int i = 0; i <= typeList.Length - 1; i++)
{
Type t = typeList[i];
table[i, i] = t;
}
}
private static void AddEntry(Type t1, Type t2, Type result)
{
int index1 = GetTypeIndex(t1);
int index2 = GetTypeIndex(t2);
OurBinaryResultTable[index1, index2] = result;
OurBinaryResultTable[index2, index1] = result;
}
private static int GetTypeIndex(Type t)
{
return System.Array.IndexOf(OurBinaryTypes, t);
}
public static bool EmitImplicitConvert(Type sourceType, Type destType, FleeILGenerator ilg)
{
if (object.ReferenceEquals(sourceType, destType))
{
return true;
}
else if (EmitOverloadedImplicitConvert(sourceType, destType, ilg) == true)
{
return true;
}
else if (ImplicitConvertToReferenceType(sourceType, destType, ilg) == true)
{
return true;
}
else
{
return ImplicitConvertToValueType(sourceType, destType, ilg);
}
}
private static bool EmitOverloadedImplicitConvert(Type sourceType, Type destType, FleeILGenerator ilg)
{
// Look for an implicit operator on the destination type
MethodInfo mi = Utility.GetSimpleOverloadedOperator("Implicit", sourceType, destType);
if (mi == null)
{
// No match
return false;
}
if ((ilg != null))
{
ilg.Emit(OpCodes.Call, mi);
}
return true;
}
private static bool ImplicitConvertToReferenceType(Type sourceType, Type destType, FleeILGenerator ilg)
{
if (destType.IsValueType == true)
{
return false;
}
if (object.ReferenceEquals(sourceType, typeof(Null)))
{
// Null is always convertible to a reference type
return true;
}
if (destType.IsAssignableFrom(sourceType) == false)
{
return false;
}
if (sourceType.IsValueType == true)
{
if ((ilg != null))
{
ilg.Emit(OpCodes.Box, sourceType);
}
}
return true;
}
private static bool ImplicitConvertToValueType(Type sourceType, Type destType, FleeILGenerator ilg)
{
// We only handle value types
if (sourceType.IsValueType == false & destType.IsValueType == false)
{
return false;
}
// No implicit conversion to enum. Have to do this check here since calling GetTypeCode on an enum will return the typecode
// of the underlying type which screws us up.
if (sourceType.IsEnum == true | destType.IsEnum == true)
{
return false;
}
return EmitImplicitNumericConvert(sourceType, destType, ilg);
}
/// <summary>
///Emit an implicit conversion (if the ilg is not null) and returns a value that determines whether the implicit conversion
/// succeeded
/// </summary>
/// <param name="sourceType"></param>
/// <param name="destType"></param>
/// <param name="ilg"></param>
/// <returns></returns>
public static bool EmitImplicitNumericConvert(Type sourceType, Type destType, FleeILGenerator ilg)
{
TypeCode sourceTypeCode = Type.GetTypeCode(sourceType);
TypeCode destTypeCode = Type.GetTypeCode(destType);
switch (destTypeCode)
{
case TypeCode.Int16:
return ImplicitConvertToInt16(sourceTypeCode, ilg);
case TypeCode.UInt16:
return ImplicitConvertToUInt16(sourceTypeCode, ilg);
case TypeCode.Int32:
return ImplicitConvertToInt32(sourceTypeCode, ilg);
case TypeCode.UInt32:
return ImplicitConvertToUInt32(sourceTypeCode, ilg);
case TypeCode.Double:
return ImplicitConvertToDouble(sourceTypeCode, ilg);
case TypeCode.Single:
return ImplicitConvertToSingle(sourceTypeCode, ilg);
case TypeCode.Int64:
return ImplicitConvertToInt64(sourceTypeCode, ilg);
case TypeCode.UInt64:
return ImplicitConvertToUInt64(sourceTypeCode, ilg);
default:
return false;
}
}
private static bool ImplicitConvertToInt16(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
return true;
default:
return false;
}
}
private static bool ImplicitConvertToUInt16(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.UInt16:
return true;
default:
return false;
}
}
private static bool ImplicitConvertToInt32(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
return true;
default:
return false;
}
}
private static bool ImplicitConvertToUInt32(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.UInt32:
return true;
default:
return false;
}
}
private static bool ImplicitConvertToDouble(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.Char:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.Single:
case TypeCode.Int64:
EmitConvert(ilg, OpCodes.Conv_R8);
break;
case TypeCode.UInt32:
case TypeCode.UInt64:
EmitConvert(ilg, OpCodes.Conv_R_Un);
EmitConvert(ilg, OpCodes.Conv_R8);
break;
case TypeCode.Double:
break;
default:
return false;
}
return true;
}
private static bool ImplicitConvertToSingle(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.Int64:
EmitConvert(ilg, OpCodes.Conv_R4);
break;
case TypeCode.UInt32:
case TypeCode.UInt64:
EmitConvert(ilg, OpCodes.Conv_R_Un);
EmitConvert(ilg, OpCodes.Conv_R4);
break;
case TypeCode.Single:
break;
default:
return false;
}
return true;
}
private static bool ImplicitConvertToInt64(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
EmitConvert(ilg, OpCodes.Conv_I8);
break;
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
EmitConvert(ilg, OpCodes.Conv_U8);
break;
case TypeCode.Int64:
break;
default:
return false;
}
return true;
}
private static bool ImplicitConvertToUInt64(TypeCode sourceTypeCode, FleeILGenerator ilg)
{
switch (sourceTypeCode)
{
case TypeCode.Char:
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
EmitConvert(ilg, OpCodes.Conv_U8);
break;
case TypeCode.UInt64:
break;
default:
return false;
}
return true;
}
private static void EmitConvert(FleeILGenerator ilg, OpCode convertOpcode)
{
if ((ilg != null))
{
ilg.Emit(convertOpcode);
}
}
/// <summary>
/// Get the result type for a binary operation
/// </summary>
/// <param name="t1"></param>
/// <param name="t2"></param>
/// <returns></returns>
public static Type GetBinaryResultType(Type t1, Type t2)
{
int index1 = GetTypeIndex(t1);
int index2 = GetTypeIndex(t2);
if (index1 == -1 | index2 == -1)
{
return null;
}
else
{
return OurBinaryResultTable[index1, index2];
}
}
public static int GetImplicitConvertScore(Type sourceType, Type destType)
{
if (object.ReferenceEquals(sourceType, destType))
{
return 0;
}
if (object.ReferenceEquals(sourceType, typeof(Null)))
{
return GetInverseDistanceToObject(destType);
}
if (Utility.GetSimpleOverloadedOperator("Implicit", sourceType, destType) != null)
{
// Implicit operator conversion, score it at 1 so it's just above the minimum
return 1;
}
if (sourceType.IsValueType == true)
{
if (destType.IsValueType == true)
{
// Value type -> value type
int sourceScore = GetValueTypeImplicitConvertScore(sourceType);
int destScore = GetValueTypeImplicitConvertScore(destType);
return destScore - sourceScore;
}
else
{
// Value type -> reference type
return GetReferenceTypeImplicitConvertScore(sourceType, destType);
}
}
else
{
if (destType.IsValueType == true)
{
// Reference type -> value type
// Reference types can never be implicitly converted to value types
Debug.Fail("No implicit conversion from reference type to value type");
}
else
{
// Reference type -> reference type
return GetReferenceTypeImplicitConvertScore(sourceType, destType);
}
}
return 0;
}
private static int GetValueTypeImplicitConvertScore(Type t)
{
TypeCode tc = Type.GetTypeCode(t);
switch (tc)
{
case TypeCode.Byte:
return 1;
case TypeCode.SByte:
return 2;
case TypeCode.Char:
return 3;
case TypeCode.Int16:
return 4;
case TypeCode.UInt16:
return 5;
case TypeCode.Int32:
return 6;
case TypeCode.UInt32:
return 7;
case TypeCode.Int64:
return 8;
case TypeCode.UInt64:
return 9;
case TypeCode.Single:
return 10;
case TypeCode.Double:
return 11;
case TypeCode.Decimal:
return 11;
case TypeCode.Boolean:
return 12;
case TypeCode.DateTime:
return 13;
default:
Debug.Assert(false, "unknown value type");
return -1;
}
}
private static int GetReferenceTypeImplicitConvertScore(Type sourceType, Type destType)
{
if (destType.IsInterface == true)
{
return 100;
}
else
{
return GetInheritanceDistance(sourceType, destType);
}
}
private static int GetInheritanceDistance(Type sourceType, Type destType)
{
int count = 0;
Type current = sourceType;
while ((!object.ReferenceEquals(current, destType)))
{
count += 1;
current = current.BaseType;
}
return count * 1000;
}
private static int GetInverseDistanceToObject(Type t)
{
int score = 1000;
Type current = t.BaseType;
while ((current != null))
{
score -= 100;
current = current.BaseType;
}
return score;
}
}
}

View File

@@ -0,0 +1,562 @@
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using Flee.ExpressionElements.Base;
using Flee.PublicTypes;
namespace Flee.InternalTypes
{
internal enum BinaryArithmeticOperation
{
Add,
Subtract,
Multiply,
Divide,
Mod,
Power
}
internal enum LogicalCompareOperation
{
LessThan,
GreaterThan,
Equal,
NotEqual,
LessThanOrEqual,
GreaterThanOrEqual
}
internal enum AndOrOperation
{
And,
Or
}
internal enum ShiftOperation
{
LeftShift,
RightShift
}
internal delegate T ExpressionEvaluator<T>(object owner, ExpressionContext context, VariableCollection variables);
internal abstract class CustomBinder : Binder
{
public override System.Reflection.FieldInfo BindToField(System.Reflection.BindingFlags bindingAttr, System.Reflection.FieldInfo[] match, object value, System.Globalization.CultureInfo culture)
{
return null;
}
public System.Reflection.MethodBase BindToMethod(System.Reflection.BindingFlags bindingAttr, System.Reflection.MethodBase[] match, ref object[] args, System.Reflection.ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] names, ref object state)
{
return null;
}
public override object ChangeType(object value, System.Type type, System.Globalization.CultureInfo culture)
{
return null;
}
public override void ReorderArgumentArray(ref object[] args, object state)
{
}
public override System.Reflection.PropertyInfo SelectProperty(System.Reflection.BindingFlags bindingAttr, System.Reflection.PropertyInfo[] match, System.Type returnType, System.Type[] indexes, System.Reflection.ParameterModifier[] modifiers)
{
return null;
}
}
internal class ExplicitOperatorMethodBinder : CustomBinder
{
private readonly Type _myReturnType;
private readonly Type _myArgType;
private CustomBinder _customBinderImplementation;
public ExplicitOperatorMethodBinder(Type returnType, Type argType)
{
_myReturnType = returnType;
_myArgType = argType;
}
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers,
CultureInfo culture, string[] names, out object state)
{
return _customBinderImplementation.BindToMethod(bindingAttr, match, ref args, modifiers, culture, names, out state);
}
public override System.Reflection.MethodBase SelectMethod(System.Reflection.BindingFlags bindingAttr, System.Reflection.MethodBase[] match, System.Type[] types, System.Reflection.ParameterModifier[] modifiers)
{
foreach (MethodInfo mi in match)
{
ParameterInfo[] parameters = mi.GetParameters();
ParameterInfo firstParameter = parameters[0];
if (object.ReferenceEquals(firstParameter.ParameterType, _myArgType) & object.ReferenceEquals(mi.ReturnType, _myReturnType))
{
return mi;
}
}
return null;
}
}
internal class BinaryOperatorBinder : CustomBinder
{
private readonly Type _myLeftType;
private readonly Type _myRightType;
private CustomBinder _customBinderImplementation;
public BinaryOperatorBinder(Type leftType, Type rightType)
{
_myLeftType = leftType;
_myRightType = rightType;
}
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers,
CultureInfo culture, string[] names, out object state)
{
return _customBinderImplementation.BindToMethod(bindingAttr, match, ref args, modifiers, culture, names, out state);
}
public override System.Reflection.MethodBase SelectMethod(System.Reflection.BindingFlags bindingAttr, System.Reflection.MethodBase[] match, System.Type[] types, System.Reflection.ParameterModifier[] modifiers)
{
foreach (MethodInfo mi in match)
{
ParameterInfo[] parameters = mi.GetParameters();
bool leftValid = ImplicitConverter.EmitImplicitConvert(_myLeftType, parameters[0].ParameterType, null);
bool rightValid = ImplicitConverter.EmitImplicitConvert(_myRightType, parameters[1].ParameterType, null);
if (leftValid == true & rightValid == true)
{
return mi;
}
}
return null;
}
}
internal class Null
{
}
internal class DefaultExpressionOwner
{
private static readonly DefaultExpressionOwner OurInstance = new DefaultExpressionOwner();
private DefaultExpressionOwner()
{
}
public static object Instance => OurInstance;
}
[Obsolete("Helper class to resolve overloads")]
internal class CustomMethodInfo : IComparable<CustomMethodInfo>, IEquatable<CustomMethodInfo>
{
/// <summary>
/// Method we are wrapping
/// </summary>
private readonly MethodInfo _myTarget;
/// <summary>
/// The rating of how close the method matches the given arguments (0 is best)
/// </summary>
private float _myScore;
public bool IsParamArray;
public Type[] MyFixedArgTypes;
public Type[] MyParamArrayArgTypes;
public bool IsExtensionMethod;
public Type ParamArrayElementType;
public CustomMethodInfo(MethodInfo target)
{
_myTarget = target;
}
public void ComputeScore(Type[] argTypes)
{
ParameterInfo[] @params = _myTarget.GetParameters();
if (@params.Length == 0)
{
_myScore = 0.0F;
}
else if (@params.Length == 1 && argTypes.Length == 0)//extension method without parameter support -> prefer members
{
_myScore = 0.1F;
}
else if (IsParamArray == true)
{
_myScore = this.ComputeScoreForParamArray(@params, argTypes);
}
else if (IsExtensionMethod == true)
{
_myScore = this.ComputeScoreExtensionMethodInternal(@params, argTypes);
}
else
{
_myScore = this.ComputeScoreInternal(@params, argTypes);
}
}
/// <summary>
/// Compute a score showing how close our method matches the given argument types (for extension methods)
/// </summary>
/// <param name="parameters"></param>
/// <param name="argTypes"></param>
/// <returns></returns>
private float ComputeScoreExtensionMethodInternal(ParameterInfo[] parameters, Type[] argTypes)
{
Debug.Assert(parameters.Length == argTypes.Length + 1);
int sum = 0;
for (int i = 0; i <= argTypes.Length - 1; i++)
{
sum += ImplicitConverter.GetImplicitConvertScore(argTypes[i], parameters[i + 1].ParameterType);
}
return sum;
}
/// <summary>
/// Compute a score showing how close our method matches the given argument types
/// </summary>
/// <param name="parameters"></param>
/// <param name="argTypes"></param>
/// <returns></returns>
private float ComputeScoreInternal(ParameterInfo[] parameters, Type[] argTypes)
{
// Our score is the average of the scores of each parameter. The lower the score, the better the match.
int sum = ComputeSum(parameters, argTypes);
return (float)sum / (float)argTypes.Length;
}
private static int ComputeSum(ParameterInfo[] parameters, Type[] argTypes)
{
Debug.Assert(parameters.Length == argTypes.Length);
int sum = 0;
for (int i = 0; i <= parameters.Length - 1; i++)
{
sum += ImplicitConverter.GetImplicitConvertScore(argTypes[i], parameters[i].ParameterType);
}
return sum;
}
private float ComputeScoreForParamArray(ParameterInfo[] parameters, Type[] argTypes)
{
ParameterInfo paramArrayParameter = parameters[parameters.Length - 1];
int fixedParameterCount = paramArrayParameter.Position;
ParameterInfo[] fixedParameters = new ParameterInfo[fixedParameterCount];
System.Array.Copy(parameters, fixedParameters, fixedParameterCount);
int fixedSum = ComputeSum(fixedParameters, MyFixedArgTypes);
Type paramArrayElementType = paramArrayParameter.ParameterType.GetElementType();
int paramArraySum = 0;
foreach (Type argType in MyParamArrayArgTypes)
{
paramArraySum += ImplicitConverter.GetImplicitConvertScore(argType, paramArrayElementType);
}
float score = 0;
if (argTypes.Length > 0)
{
score = (fixedSum + paramArraySum) / argTypes.Length;
}
else
{
score = 0;
}
// The param array score gets a slight penalty so that it scores worse than direct matches
return score + 1;
}
public bool IsAccessible(MemberElement owner)
{
return owner.IsMemberAccessible(_myTarget);
}
/// <summary>
/// Is the given MethodInfo usable as an overload?
/// </summary>
/// <param name="argTypes"></param>
/// <returns></returns>
public bool IsMatch(Type[] argTypes, MemberElement previous, ExpressionContext context)
{
ParameterInfo[] parameters = _myTarget.GetParameters();
// If there are no parameters and no arguments were passed, then we are a match.
if (parameters.Length == 0 & argTypes.Length == 0)
{
return true;
}
// If there are no parameters but there are arguments, we cannot be a match
if (parameters.Length == 0 & argTypes.Length > 0)
{
return false;
}
// Is the last parameter a paramArray?
ParameterInfo lastParam = parameters[parameters.Length - 1];
if (lastParam.IsDefined(typeof(ParamArrayAttribute), false) == false)
{
//Extension method support
if (parameters.Length == argTypes.Length + 1)
{
IsExtensionMethod = true;
return AreValidExtensionMethodArgumentsForParameters(argTypes, parameters, previous, context);
}
if ((parameters.Length != argTypes.Length))
{
// Not a paramArray and parameter and argument counts don't match
return false;
}
else
{
// Regular method call, do the test
return AreValidArgumentsForParameters(argTypes, parameters);
}
}
// At this point, we are dealing with a paramArray call
// If the parameter and argument counts are equal and there is an implicit conversion from one to the other, we are a match.
if (parameters.Length == argTypes.Length && AreValidArgumentsForParameters(argTypes, parameters) == true)
{
return true;
}
else if (this.IsParamArrayMatch(argTypes, parameters, lastParam) == true)
{
IsParamArray = true;
return true;
}
else
{
return false;
}
}
private bool IsParamArrayMatch(Type[] argTypes, ParameterInfo[] parameters, ParameterInfo paramArrayParameter)
{
// Get the count of arguments before the paramArray parameter
int fixedParameterCount = paramArrayParameter.Position;
Type[] fixedArgTypes = new Type[fixedParameterCount];
ParameterInfo[] fixedParameters = new ParameterInfo[fixedParameterCount];
// Get the argument types and parameters before the paramArray
System.Array.Copy(argTypes, fixedArgTypes, fixedParameterCount);
System.Array.Copy(parameters, fixedParameters, fixedParameterCount);
// If the fixed arguments don't match, we are not a match
if (AreValidArgumentsForParameters(fixedArgTypes, fixedParameters) == false)
{
return false;
}
// Get the type of the paramArray
ParamArrayElementType = paramArrayParameter.ParameterType.GetElementType();
// Get the types of the arguments passed to the paramArray
Type[] paramArrayArgTypes = new Type[argTypes.Length - fixedParameterCount];
System.Array.Copy(argTypes, fixedParameterCount, paramArrayArgTypes, 0, paramArrayArgTypes.Length);
// Check each argument
foreach (Type argType in paramArrayArgTypes)
{
if (ImplicitConverter.EmitImplicitConvert(argType, ParamArrayElementType, null) == false)
{
return false;
}
}
MyFixedArgTypes = fixedArgTypes;
MyParamArrayArgTypes = paramArrayArgTypes;
// They all match, so we are a match
return true;
}
private static bool AreValidExtensionMethodArgumentsForParameters(Type[] argTypes, ParameterInfo[] parameters, MemberElement previous, ExpressionContext context)
{
Debug.Assert(argTypes.Length + 1 == parameters.Length);
if (previous != null)
{
if (ImplicitConverter.EmitImplicitConvert(previous.ResultType, parameters[0].ParameterType, null) == false)
{
return false;
}
}
else if (context.ExpressionOwner != null)
{
if (ImplicitConverter.EmitImplicitConvert(context.ExpressionOwner.GetType(), parameters[0].ParameterType, null) == false)
return false;
}
else
return false;
//Match if every given argument is implicitly convertible to the method's corresponding parameter
for (int i = 0; i <= argTypes.Length - 1; i++)
{
if (ImplicitConverter.EmitImplicitConvert(argTypes[i], parameters[i + 1].ParameterType, null) == false)
{
return false;
}
}
return true;
}
private static bool AreValidArgumentsForParameters(Type[] argTypes, ParameterInfo[] parameters)
{
Debug.Assert(argTypes.Length == parameters.Length);
// Match if every given argument is implicitly convertible to the method's corresponding parameter
for (int i = 0; i <= argTypes.Length - 1; i++)
{
if (ImplicitConverter.EmitImplicitConvert(argTypes[i], parameters[i].ParameterType, null) == false)
{
return false;
}
}
return true;
}
public int CompareTo(CustomMethodInfo other)
{
return _myScore.CompareTo(other._myScore);
}
private bool Equals1(CustomMethodInfo other)
{
return _myScore == other._myScore;
}
bool System.IEquatable<CustomMethodInfo>.Equals(CustomMethodInfo other)
{
return Equals1(other);
}
public MethodInfo Target => _myTarget;
}
internal class ShortCircuitInfo
{
public Stack Operands;
public Stack Operators;
private Dictionary<object, Label> Labels;
public ShortCircuitInfo()
{
this.Operands = new Stack();
this.Operators = new Stack();
this.Labels = new Dictionary<object, Label>();
}
public void ClearTempState()
{
this.Operands.Clear();
this.Operators.Clear();
}
public Label AddLabel(object key, Label lbl)
{
Labels.Add(key, lbl);
return lbl;
}
public bool HasLabel(object key)
{
return Labels.ContainsKey(key);
}
public Label FindLabel(object key)
{
return Labels[key];
}
}
[Obsolete("Wraps an expression element so that it is loaded from a local slot")]
internal class LocalBasedElement : ExpressionElement
{
private readonly int _myIndex;
private readonly ExpressionElement _myTarget;
public LocalBasedElement(ExpressionElement target, int index)
{
_myTarget = target;
_myIndex = index;
}
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
{
Utility.EmitLoadLocal(ilg, _myIndex);
}
public override System.Type ResultType => _myTarget.ResultType;
}
[Obsolete("Helper class for storing strongly-typed properties")]
internal class PropertyDictionary
{
private readonly Dictionary<string, object> _myProperties;
public PropertyDictionary()
{
_myProperties = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
public PropertyDictionary Clone()
{
PropertyDictionary copy = new PropertyDictionary();
foreach (KeyValuePair<string, object> pair in _myProperties)
{
copy.SetValue(pair.Key, pair.Value);
}
return copy;
}
public T GetValue<T>(string name)
{
object value = default(T);
if (_myProperties.TryGetValue(name, out value) == false)
{
Debug.Fail($"Unknown property '{name}'");
}
return (T)value;
}
public void SetToDefault<T>(string name)
{
T value = default(T);
this.SetValue(name, value);
}
public void SetValue(string name, object value)
{
_myProperties[name] = value;
}
public bool Contains(string name)
{
return _myProperties.ContainsKey(name);
}
}
}

350
InternalTypes/Utility.cs Normal file
View File

@@ -0,0 +1,350 @@
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using Flee.Resources;
namespace Flee.InternalTypes
{
[Obsolete("Holds various shared utility methods")]
internal class Utility
{
private Utility()
{
}
public static void AssertNotNull(object o, string paramName)
{
if (o == null)
{
throw new ArgumentNullException(paramName);
}
}
public static void EmitStoreLocal(FleeILGenerator ilg, int index)
{
if (index >= 0 & index <= 3)
{
switch (index)
{
case 0:
ilg.Emit(OpCodes.Stloc_0);
break;
case 1:
ilg.Emit(OpCodes.Stloc_1);
break;
case 2:
ilg.Emit(OpCodes.Stloc_2);
break;
case 3:
ilg.Emit(OpCodes.Stloc_3);
break;
}
}
else if (index < 256)
{
ilg.Emit(OpCodes.Stloc_S, Convert.ToByte(index));
}
else
{
Debug.Assert(index < 65535, "local index too large");
ilg.Emit(OpCodes.Stloc, unchecked((short)Convert.ToUInt16(index)));
}
}
public static void EmitLoadLocal(FleeILGenerator ilg, int index)
{
Debug.Assert(index >= 0, "Invalid index");
if (index >= 0 & index <= 3)
{
switch (index)
{
case 0:
ilg.Emit(OpCodes.Ldloc_0);
break;
case 1:
ilg.Emit(OpCodes.Ldloc_1);
break;
case 2:
ilg.Emit(OpCodes.Ldloc_2);
break;
case 3:
ilg.Emit(OpCodes.Ldloc_3);
break;
}
}
else if (index < 256)
{
ilg.Emit(OpCodes.Ldloc_S, Convert.ToByte(index));
}
else
{
Debug.Assert(index < 65535, "local index too large");
ilg.Emit(OpCodes.Ldloc, unchecked((short)Convert.ToUInt16(index)));
}
}
public static void EmitLoadLocalAddress(FleeILGenerator ilg, int index)
{
Debug.Assert(index >= 0, "Invalid index");
if (index <= byte.MaxValue)
{
ilg.Emit(OpCodes.Ldloca_S, Convert.ToByte(index));
}
else
{
ilg.Emit(OpCodes.Ldloca, index);
}
}
public static void EmitArrayLoad(FleeILGenerator ilg, Type elementType)
{
TypeCode tc = Type.GetTypeCode(elementType);
switch (tc)
{
case TypeCode.Byte:
ilg.Emit(OpCodes.Ldelem_U1);
break;
case TypeCode.SByte:
case TypeCode.Boolean:
ilg.Emit(OpCodes.Ldelem_I1);
break;
case TypeCode.Int16:
ilg.Emit(OpCodes.Ldelem_I2);
break;
case TypeCode.UInt16:
ilg.Emit(OpCodes.Ldelem_U2);
break;
case TypeCode.Int32:
ilg.Emit(OpCodes.Ldelem_I4);
break;
case TypeCode.UInt32:
ilg.Emit(OpCodes.Ldelem_U4);
break;
case TypeCode.Int64:
case TypeCode.UInt64:
ilg.Emit(OpCodes.Ldelem_I8);
break;
case TypeCode.Single:
ilg.Emit(OpCodes.Ldelem_R4);
break;
case TypeCode.Double:
ilg.Emit(OpCodes.Ldelem_R8);
break;
case TypeCode.Object:
case TypeCode.String:
ilg.Emit(OpCodes.Ldelem_Ref);
break;
default:
// Must be a non-primitive value type
ilg.Emit(OpCodes.Ldelema, elementType);
ilg.Emit(OpCodes.Ldobj, elementType);
return;
}
}
public static void EmitArrayStore(FleeILGenerator ilg, Type elementType)
{
TypeCode tc = Type.GetTypeCode(elementType);
switch (tc)
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Boolean:
ilg.Emit(OpCodes.Stelem_I1);
break;
case TypeCode.Int16:
case TypeCode.UInt16:
ilg.Emit(OpCodes.Stelem_I2);
break;
case TypeCode.Int32:
case TypeCode.UInt32:
ilg.Emit(OpCodes.Stelem_I4);
break;
case TypeCode.Int64:
case TypeCode.UInt64:
ilg.Emit(OpCodes.Stelem_I8);
break;
case TypeCode.Single:
ilg.Emit(OpCodes.Stelem_R4);
break;
case TypeCode.Double:
ilg.Emit(OpCodes.Stelem_R8);
break;
case TypeCode.Object:
case TypeCode.String:
ilg.Emit(OpCodes.Stelem_Ref);
break;
default:
// Must be a non-primitive value type
ilg.Emit(OpCodes.Stelem, elementType);
break;
}
}
public static bool IsIntegralType(Type t)
{
TypeCode tc = Type.GetTypeCode(t);
switch (tc)
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
return true;
default:
return false;
}
}
public static Type GetBitwiseOpType(Type leftType, Type rightType)
{
if (IsIntegralType(leftType) == false || IsIntegralType(rightType) == false)
{
return null;
}
else
{
return ImplicitConverter.GetBinaryResultType(leftType, rightType);
}
}
/// <summary>
/// Find a simple (unary) overloaded operator
/// </summary>
/// <param name="name">The name of the operator</param>
/// <param name="sourceType">The type to convert from</param>
/// <param name="destType">The type to convert to (can be null if it's not known beforehand)</param>
/// <returns>The operator's method or null of no match is found</returns>
public static MethodInfo GetSimpleOverloadedOperator(string name, Type sourceType, Type destType)
{
Hashtable data = new Hashtable();
data.Add("Name", string.Concat("op_", name));
data.Add("sourceType", sourceType);
data.Add("destType", destType);
const BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
// Look on the source type and its ancestors
MemberInfo[] members = new MemberInfo[0];
do
{
members = sourceType.FindMembers(MemberTypes.Method, flags, SimpleOverloadedOperatorFilter, data);
} while (members.Length == 0 && (sourceType = sourceType.BaseType) != null);
if (members.Length == 0 && destType != null)
{
// Look on the dest type and its ancestors
do
{
members = destType.FindMembers(MemberTypes.Method, flags, SimpleOverloadedOperatorFilter, data);
} while (members.Length == 0 && (destType = destType.BaseType) != null);
}
Debug.Assert(members.Length < 2, "Multiple overloaded operators found");
if (members.Length == 0)
{
// No match
return null;
}
else
{
return (MethodInfo)members[0];
}
}
/// <summary>
/// Matches simple overloaded operators
/// </summary>
/// <param name="member"></param>
/// <param name="value"></param>
/// <returns></returns>
/// <remarks></remarks>
private static bool SimpleOverloadedOperatorFilter(MemberInfo member, object value)
{
IDictionary data = (IDictionary)value;
MethodInfo method = (MethodInfo)member;
bool nameMatch = method.IsSpecialName == true && method.Name.Equals((string)data["Name"], StringComparison.OrdinalIgnoreCase);
if (nameMatch == false)
{
return false;
}
// destination type might not be known
Type destType = (Type)data["destType"];
if (destType != null)
{
bool returnTypeMatch = object.ReferenceEquals(destType, method.ReturnType);
if (returnTypeMatch == false)
{
return false;
}
}
ParameterInfo[] parameters = method.GetParameters();
bool argumentMatch = parameters.Length > 0 && parameters[0].ParameterType.IsAssignableFrom((Type)data["sourceType"]);
return argumentMatch;
}
public static MethodInfo GetOverloadedOperator(string name, Type sourceType, Binder binder, params Type[] argumentTypes)
{
name = string.Concat("op_", name);
MethodInfo mi = null;
do
{
mi = sourceType.GetMethod(name, BindingFlags.Public | BindingFlags.Static, binder, CallingConventions.Any, argumentTypes, null);
if (mi != null && mi.IsSpecialName == true)
{
return mi;
}
} while ((sourceType = sourceType.BaseType) != null);
return null;
}
public static int GetILGeneratorLength(ILGenerator ilg)
{
System.Reflection.FieldInfo fi = typeof(ILGenerator).GetField("m_length", BindingFlags.Instance | BindingFlags.NonPublic);
return (int)fi.GetValue(ilg);
}
public static bool IsLongBranch(int startPosition, int endPosition)
{
return (endPosition - startPosition) > sbyte.MaxValue;
}
public static string FormatList(string[] items)
{
string separator = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ListSeparator + " ";
return string.Join(separator, items);
}
public static string GetGeneralErrorMessage(string key, params object[] args)
{
string msg = FleeResourceManager.Instance.GetGeneralErrorString(key);
return string.Format(msg, args);
}
public static string GetCompileErrorMessage(string key, params object[] args)
{
string msg = FleeResourceManager.Instance.GetCompileErrorString(key);
return string.Format(msg, args);
}
}
}

View File

@@ -0,0 +1,100 @@
using Flee.PublicTypes;
namespace Flee.InternalTypes
{
internal interface IVariable
{
IVariable Clone();
Type VariableType { get; }
object ValueAsObject { get; set; }
}
internal interface IGenericVariable<T>
{
object GetValue();
}
internal class DynamicExpressionVariable<T> : IVariable, IGenericVariable<T>
{
private IDynamicExpression _myExpression;
public IVariable Clone()
{
DynamicExpressionVariable<T> copy = new DynamicExpressionVariable<T>();
copy._myExpression = _myExpression;
return copy;
}
public object GetValue()
{
return (T)_myExpression.Evaluate();
}
public object ValueAsObject
{
get { return _myExpression; }
set { _myExpression = value as IDynamicExpression; }
}
public System.Type VariableType => _myExpression.Context.Options.ResultType;
}
internal class GenericExpressionVariable<T> : IVariable, IGenericVariable<T>
{
private IGenericExpression<T> _myExpression;
public IVariable Clone()
{
GenericExpressionVariable<T> copy = new GenericExpressionVariable<T>();
copy._myExpression = _myExpression;
return copy;
}
public object GetValue()
{
return _myExpression.Evaluate();
}
public object ValueAsObject
{
get { return _myExpression; }
set { _myExpression = (IGenericExpression<T>)value; }
}
public System.Type VariableType => _myExpression.Context.Options.ResultType;
}
internal class GenericVariable<T> : IVariable, IGenericVariable<T>
{
public object MyValue;
public IVariable Clone()
{
GenericVariable<T> copy = new GenericVariable<T> { MyValue = MyValue };
return copy;
}
public object GetValue()
{
return MyValue;
}
public System.Type VariableType => typeof(T);
public object ValueAsObject
{
get { return MyValue; }
set
{
if (value == null)
{
MyValue = default(T);
}
else
{
MyValue = value;
}
}
}
}
}

View File

@@ -0,0 +1,60 @@
namespace Flee.Parsing
{
/**
* A regular expression alternative element. This element matches
* the longest alternative element.
*/
internal class AlternativeElement : Element
{
private readonly Element _elem1;
private readonly Element _elem2;
public AlternativeElement(Element first, Element second)
{
_elem1 = first;
_elem2 = second;
}
public override object Clone()
{
return new AlternativeElement(_elem1, _elem2);
}
public override int Match(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
int length = 0;
int length1 = -1;
int length2 = -1;
int skip1 = 0;
int skip2 = 0;
while (length >= 0 && skip1 + skip2 <= skip)
{
length1 = _elem1.Match(m, buffer, start, skip1);
length2 = _elem2.Match(m, buffer, start, skip2);
if (length1 >= length2)
{
length = length1;
skip1++;
}
else
{
length = length2;
skip2++;
}
}
return length;
}
public override void PrintTo(TextWriter output, string indent)
{
output.WriteLine(indent + "Alternative 1");
_elem1.PrintTo(output, indent + " ");
output.WriteLine(indent + "Alternative 2");
_elem2.PrintTo(output, indent + " ");
}
}
}

240
Parsing/Analyzer.cs Normal file
View File

@@ -0,0 +1,240 @@
using System.Collections;
namespace Flee.Parsing
{
[Obsolete("Creates a new parse tree analyzer.")]
internal class Analyzer
{
public Analyzer()
{
}
/// <summary>
/// Resets this analyzer when the parser is reset for another
///input stream.The default implementation of this method does
/// nothing.
/// </summary>
public virtual void Reset()
{
// Default implementation does nothing
}
public Node Analyze(Node node)
{
ParserLogException log = new ParserLogException();
node = Analyze(node, log);
if (log.Count > 0)
{
throw log;
}
return node;
}
private Node Analyze(Node node, ParserLogException log)
{
var errorCount = log.Count;
if (node is Production)
{
var prod = (Production)node;
prod = NewProduction(prod.Pattern);
try
{
Enter(prod);
}
catch (ParseException e)
{
log.AddError(e);
}
for (int i = 0; i < node.Count; i++)
{
try
{
Child(prod, Analyze(node[i], log));
}
catch (ParseException e)
{
log.AddError(e);
}
}
try
{
return Exit(prod);
}
catch (ParseException e)
{
if (errorCount == log.Count)
{
log.AddError(e);
}
}
}
else
{
node.Values.Clear();
try
{
Enter(node);
}
catch (ParseException e)
{
log.AddError(e);
}
try
{
return Exit(node);
}
catch (ParseException e)
{
if (errorCount == log.Count)
{
log.AddError(e);
}
}
}
return null;
}
public virtual Production NewProduction(ProductionPattern pattern)
{
return new Production(pattern);
}
public virtual void Enter(Node node)
{
}
public virtual Node Exit(Node node)
{
return node;
}
public virtual void Child(Production node, Node child)
{
node.AddChild(child);
}
protected Node GetChildAt(Node node, int pos)
{
if (node == null)
{
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"attempt to read 'null' parse tree node",
-1,
-1);
}
var child = node[pos];
if (child == null)
{
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"node '" + node.Name + "' has no child at " +
"position " + pos,
node.StartLine,
node.StartColumn);
}
return child;
}
protected Node GetChildWithId(Node node, int id)
{
if (node == null)
{
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"attempt to read 'null' parse tree node",
-1,
-1);
}
for (int i = 0; i < node.Count; i++)
{
var child = node[i];
if (child != null && child.Id == id)
{
return child;
}
}
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"node '" + node.Name + "' has no child with id " + id,
node.StartLine,
node.StartColumn);
}
protected object GetValue(Node node, int pos)
{
if (node == null)
{
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"attempt to read 'null' parse tree node",
-1,
-1);
}
var value = node.Values[pos];
if (value == null)
{
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"node '" + node.Name + "' has no value at " +
"position " + pos,
node.StartLine,
node.StartColumn);
}
return value;
}
protected int GetIntValue(Node node, int pos)
{
var value = GetValue(node, pos);
if (value is int)
{
return (int)value;
}
else
{
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"node '" + node.Name + "' has no integer value " +
"at position " + pos,
node.StartLine,
node.StartColumn);
}
}
protected string GetStringValue(Node node, int pos)
{
var value = GetValue(node, pos);
if (value is string)
{
return (string)value;
}
else
{
throw new ParseException(
ParseException.ErrorType.INTERNAL,
"node '" + node.Name + "' has no string value " +
"at position " + pos,
node.StartLine,
node.StartColumn);
}
}
protected ArrayList GetChildValues(Node node)
{
ArrayList result = new ArrayList();
for (int i = 0; i < node.Count; i++)
{
var child = node[i];
var values = child.Values;
if (values != null)
{
result.AddRange(values);
}
}
return result;
}
}
}

111
Parsing/Automaton.cs Normal file
View File

@@ -0,0 +1,111 @@
namespace Flee.Parsing
{
internal class Automaton
{
private object _value;
private readonly AutomatonTree _tree = new AutomatonTree();
public Automaton()
{
}
public void AddMatch(string str, bool caseInsensitive, object value)
{
if (str.Length == 0)
{
this._value = value;
}
else
{
var state = _tree.Find(str[0], caseInsensitive);
if (state == null)
{
state = new Automaton();
state.AddMatch(str.Substring(1), caseInsensitive, value);
_tree.Add(str[0], caseInsensitive, state);
}
else
{
state.AddMatch(str.Substring(1), caseInsensitive, value);
}
}
}
public object MatchFrom(LookAheadReader input, int pos, bool caseInsensitive)
{
object result = null;
Automaton state = null;
int c = 0;
c = input.Peek(pos);
if (_tree != null && c >= 0)
{
state = _tree.Find(Convert.ToChar(c), caseInsensitive);
if (state != null)
{
result = state.MatchFrom(input, pos + 1, caseInsensitive);
}
}
return result ?? _value;
}
}
// * An automaton state transition tree. This class contains a
// * binary search tree for the automaton transitions from one state
// * to another. All transitions are linked to a single character.
internal class AutomatonTree
{
private char _value;
private Automaton _state;
private AutomatonTree _left;
private AutomatonTree _right;
public AutomatonTree()
{
}
public Automaton Find(char c, bool lowerCase)
{
if (lowerCase)
{
c = Char.ToLower(c);
}
if (_value == (char)0 || _value == c)
{
return _state;
}
else if (_value > c)
{
return _left.Find(c, false);
}
else
{
return _right.Find(c, false);
}
}
public void Add(char c, bool lowerCase, Automaton state)
{
if (lowerCase)
{
c = Char.ToLower(c);
}
if (_value == (char)0)
{
this._value = c;
this._state = state;
this._left = new AutomatonTree();
this._right = new AutomatonTree();
}
else if (_value > c)
{
_left.Add(c, false, state);
}
else
{
_right.Add(c, false, state);
}
}
}
}

View File

@@ -0,0 +1,267 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
/**
* A regular expression character set element. This element
* matches a single character inside (or outside) a character set.
* The character set is user defined and may contain ranges of
* characters. The set may also be inverted, meaning that only
* characters not inside the set will be considered to match.
*/
internal class CharacterSetElement : Element
{
public static CharacterSetElement Dot = new CharacterSetElement(false);
public static CharacterSetElement Digit = new CharacterSetElement(false);
public static CharacterSetElement NonDigit = new CharacterSetElement(true);
public static CharacterSetElement Whitespace = new CharacterSetElement(false);
public static CharacterSetElement NonWhitespace = new CharacterSetElement(true);
public static CharacterSetElement Word = new CharacterSetElement(false);
public static CharacterSetElement NonWord = new CharacterSetElement(true);
private readonly bool _inverted;
private readonly ArrayList _contents = new ArrayList();
public CharacterSetElement(bool inverted)
{
this._inverted = inverted;
}
public void AddCharacter(char c)
{
_contents.Add(c);
}
public void AddCharacters(string str)
{
for (int i = 0; i < str.Length; i++)
{
AddCharacter(str[i]);
}
}
public void AddCharacters(StringElement elem)
{
AddCharacters(elem.GetString());
}
public void AddRange(char min, char max)
{
_contents.Add(new Range(min, max));
}
public void AddCharacterSet(CharacterSetElement elem)
{
_contents.Add(elem);
}
public override object Clone()
{
return this;
}
public override int Match(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
int c;
if (skip != 0)
{
return -1;
}
c = buffer.Peek(start);
if (c < 0)
{
m.SetReadEndOfString();
return -1;
}
if (m.IsCaseInsensitive())
{
c = (int)Char.ToLower((char)c);
}
return InSet((char)c) ? 1 : -1;
}
private bool InSet(char c)
{
if (this == Dot)
{
return InDotSet(c);
}
else if (this == Digit || this == NonDigit)
{
return InDigitSet(c) != _inverted;
}
else if (this == Whitespace || this == NonWhitespace)
{
return InWhitespaceSet(c) != _inverted;
}
else if (this == Word || this == NonWord)
{
return InWordSet(c) != _inverted;
}
else
{
return InUserSet(c) != _inverted;
}
}
private bool InDotSet(char c)
{
switch (c)
{
case '\n':
case '\r':
case '\u0085':
case '\u2028':
case '\u2029':
return false;
default:
return true;
}
}
private bool InDigitSet(char c)
{
return '0' <= c && c <= '9';
}
private bool InWhitespaceSet(char c)
{
switch (c)
{
case ' ':
case '\t':
case '\n':
case '\f':
case '\r':
case (char)11:
return true;
default:
return false;
}
}
private bool InWordSet(char c)
{
return ('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9')
|| c == '_';
}
private bool InUserSet(char value)
{
for (int i = 0; i < _contents.Count; i++)
{
var obj = _contents[i];
if (obj is char)
{
var c = (char)obj;
if (c == value)
{
return true;
}
}
else if (obj is Range)
{
var r = (Range)obj;
if (r.Inside(value))
{
return true;
}
}
else if (obj is CharacterSetElement)
{
var e = (CharacterSetElement)obj;
if (e.InSet(value))
{
return true;
}
}
}
return false;
}
public override void PrintTo(TextWriter output, string indent)
{
output.WriteLine(indent + ToString());
}
public override string ToString()
{
// Handle predefined character sets
if (this == Dot)
{
return ".";
}
else if (this == Digit)
{
return "\\d";
}
else if (this == NonDigit)
{
return "\\D";
}
else if (this == Whitespace)
{
return "\\s";
}
else if (this == NonWhitespace)
{
return "\\S";
}
else if (this == Word)
{
return "\\w";
}
else if (this == NonWord)
{
return "\\W";
}
// Handle user-defined character sets
var buffer = new StringBuilder();
if (_inverted)
{
buffer.Append("^[");
}
else
{
buffer.Append("[");
}
for (int i = 0; i < _contents.Count; i++)
{
buffer.Append(_contents[i]);
}
buffer.Append("]");
return buffer.ToString();
}
private class Range
{
private readonly char _min;
private readonly char _max;
public Range(char min, char max)
{
this._min = min;
this._max = max;
}
public bool Inside(char c)
{
return _min <= c && c <= _max;
}
public override string ToString()
{
return _min + "-" + _max;
}
}
}
}

58
Parsing/CombineElement.cs Normal file
View File

@@ -0,0 +1,58 @@
namespace Flee.Parsing
{
internal class CombineElement : Element
{
private readonly Element _elem1;
private readonly Element _elem2;
public CombineElement(Element first, Element second)
{
_elem1 = first;
_elem2 = second;
}
public override object Clone()
{
return new CombineElement(_elem1, _elem2);
}
public override int Match(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
int length1 = -1;
int length2 = 0;
int skip1 = 0;
int skip2 = 0;
while (skip >= 0)
{
length1 = _elem1.Match(m, buffer, start, skip1);
if (length1 < 0)
{
return -1;
}
length2 = _elem2.Match(m, buffer, start + length1, skip2);
if (length2 < 0)
{
skip1++;
skip2 = 0;
}
else
{
skip2++;
skip--;
}
}
return length1 + length2;
}
public override void PrintTo(TextWriter output, string indent)
{
_elem1.PrintTo(output, indent);
_elem2.PrintTo(output, indent);
}
}
}

View File

@@ -0,0 +1,596 @@
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using Flee.ExpressionElements;
using Flee.ExpressionElements.Base;
using Flee.ExpressionElements.Base.Literals;
using Flee.ExpressionElements.Literals;
using Flee.ExpressionElements.Literals.Integral;
using Flee.ExpressionElements.LogicalBitwise;
using Flee.ExpressionElements.MemberElements;
using Flee.InternalTypes;
using Flee.PublicTypes;
namespace Flee.Parsing
{
internal class FleeExpressionAnalyzer : ExpressionAnalyzer
{
private IServiceProvider _myServices;
private readonly Regex _myUnicodeEscapeRegex;
private readonly Regex _myRegularEscapeRegex;
private bool _myInUnaryNegate;
internal FleeExpressionAnalyzer()
{
_myUnicodeEscapeRegex = new Regex("\\\\u[0-9a-f]{4}", RegexOptions.IgnoreCase);
_myRegularEscapeRegex = new Regex("\\\\[\\\\\"'trn]", RegexOptions.IgnoreCase);
}
public void SetServices(IServiceProvider services)
{
_myServices = services;
}
public override void Reset()
{
_myServices = null;
}
public override Node ExitExpression(Production node)
{
this.AddFirstChildValue(node);
return node;
}
public override Node ExitExpressionGroup(Production node)
{
node.AddValues(this.GetChildValues(node));
return node;
}
public override Node ExitXorExpression(Production node)
{
this.AddBinaryOp(node, typeof(XorElement));
return node;
}
public override Node ExitOrExpression(Production node)
{
this.AddBinaryOp(node, typeof(AndOrElement));
return node;
}
public override Node ExitAndExpression(Production node)
{
this.AddBinaryOp(node, typeof(AndOrElement));
return node;
}
public override Node ExitNotExpression(Production node)
{
this.AddUnaryOp(node, typeof(NotElement));
return node;
}
public override Node ExitCompareExpression(Production node)
{
this.AddBinaryOp(node, typeof(CompareElement));
return node;
}
public override Node ExitShiftExpression(Production node)
{
this.AddBinaryOp(node, typeof(ShiftElement));
return node;
}
public override Node ExitAdditiveExpression(Production node)
{
this.AddBinaryOp(node, typeof(ArithmeticElement));
return node;
}
public override Node ExitMultiplicativeExpression(Production node)
{
this.AddBinaryOp(node, typeof(ArithmeticElement));
return node;
}
public override Node ExitPowerExpression(Production node)
{
this.AddBinaryOp(node, typeof(ArithmeticElement));
return node;
}
// Try to fold a negated constant int32. We have to do this so that parsing int32.MinValue will work
public override Node ExitNegateExpression(Production node)
{
IList childValues = this.GetChildValues(node);
// Get last child
ExpressionElement childElement = (ExpressionElement)childValues[childValues.Count - 1];
// Is it an signed integer constant?
if (object.ReferenceEquals(childElement.GetType(), typeof(Int32LiteralElement)) & childValues.Count == 2)
{
((Int32LiteralElement)childElement).Negate();
// Add it directly instead of the negate element since it will already be negated
node.AddValue(childElement);
}
else if (object.ReferenceEquals(childElement.GetType(), typeof(Int64LiteralElement)) & childValues.Count == 2)
{
((Int64LiteralElement)childElement).Negate();
// Add it directly instead of the negate element since it will already be negated
node.AddValue(childElement);
}
else
{
// No so just add a regular negate
this.AddUnaryOp(node, typeof(NegateElement));
}
return node;
}
public override Node ExitMemberExpression(Production node)
{
IList childValues = this.GetChildValues(node);
object first = childValues[0];
if (childValues.Count == 1 && !(first is MemberElement))
{
node.AddValue(first);
}
else
{
InvocationListElement list = new InvocationListElement(childValues, _myServices);
node.AddValue(list);
}
return node;
}
public override Node ExitIndexExpression(Production node)
{
IList childValues = this.GetChildValues(node);
ArgumentList args = new ArgumentList(childValues);
IndexerElement e = new IndexerElement(args);
node.AddValue(e);
return node;
}
public override Node ExitMemberAccessExpression(Production node)
{
node.AddValue(node.GetChildAt(1).GetValue(0));
return node;
}
public override Node ExitSpecialFunctionExpression(Production node)
{
this.AddFirstChildValue(node);
return node;
}
public override Node ExitIfExpression(Production node)
{
IList childValues = this.GetChildValues(node);
ConditionalElement op = new ConditionalElement((ExpressionElement)childValues[0], (ExpressionElement)childValues[1], (ExpressionElement)childValues[2]);
node.AddValue(op);
return node;
}
public override Node ExitInExpression(Production node)
{
IList childValues = this.GetChildValues(node);
if (childValues.Count == 1)
{
this.AddFirstChildValue(node);
return node;
}
ExpressionElement operand = (ExpressionElement)childValues[0];
childValues.RemoveAt(0);
object second = childValues[0];
InElement op = default(InElement);
if ((second) is IList)
{
op = new InElement(operand, (IList)second);
}
else
{
InvocationListElement il = new InvocationListElement(childValues, _myServices);
op = new InElement(operand, il);
}
node.AddValue(op);
return node;
}
public override Node ExitInTargetExpression(Production node)
{
this.AddFirstChildValue(node);
return node;
}
public override Node ExitInListTargetExpression(Production node)
{
IList childValues = this.GetChildValues(node);
node.AddValue(childValues);
return node;
}
public override Node ExitCastExpression(Production node)
{
IList childValues = this.GetChildValues(node);
string[] destTypeParts = (string[])childValues[1];
bool isArray = (bool)childValues[2];
CastElement op = new CastElement((ExpressionElement)childValues[0], destTypeParts, isArray, _myServices);
node.AddValue(op);
return node;
}
public override Node ExitCastTypeExpression(Production node)
{
IList childValues = this.GetChildValues(node);
List<string> parts = new List<string>();
foreach (string part in childValues)
{
parts.Add(part);
}
bool isArray = false;
if (parts[parts.Count - 1] == "[]")
{
isArray = true;
parts.RemoveAt(parts.Count - 1);
}
node.AddValue(parts.ToArray());
node.AddValue(isArray);
return node;
}
public override Node ExitMemberFunctionExpression(Production node)
{
this.AddFirstChildValue(node);
return node;
}
public override Node ExitFieldPropertyExpression(Production node)
{
//string name = ((Token)node.GetChildAt(0))?.Image;
string name = node.GetChildAt(0).GetValue(0).ToString();
IdentifierElement elem = new IdentifierElement(name);
node.AddValue(elem);
return node;
}
public override Node ExitFunctionCallExpression(Production node)
{
IList childValues = this.GetChildValues(node);
string name = (string)childValues[0];
childValues.RemoveAt(0);
ArgumentList args = new ArgumentList(childValues);
FunctionCallElement funcCall = new FunctionCallElement(name, args);
node.AddValue(funcCall);
return node;
}
public override Node ExitArgumentList(Production node)
{
IList childValues = this.GetChildValues(node);
node.AddValues((ArrayList)childValues);
return node;
}
public override Node ExitBasicExpression(Production node)
{
this.AddFirstChildValue(node);
return node;
}
public override Node ExitLiteralExpression(Production node)
{
this.AddFirstChildValue(node);
return node;
}
private void AddFirstChildValue(Production node)
{
node.AddValue(this.GetChildAt(node, 0).Values[0]);
}
private void AddUnaryOp(Production node, Type elementType)
{
IList childValues = this.GetChildValues(node);
if (childValues.Count == 2)
{
UnaryElement element = (UnaryElement)Activator.CreateInstance(elementType);
element.SetChild((ExpressionElement)childValues[1]);
node.AddValue(element);
}
else
{
node.AddValue(childValues[0]);
}
}
private void AddBinaryOp(Production node, Type elementType)
{
IList childValues = this.GetChildValues(node);
if (childValues.Count > 1)
{
BinaryExpressionElement e = BinaryExpressionElement.CreateElement(childValues, elementType);
node.AddValue(e);
}
else if (childValues.Count == 1)
{
node.AddValue(childValues[0]);
}
else
{
Debug.Assert(false, "wrong number of chilren");
}
}
public override Node ExitReal(Token node)
{
string image = node.Image;
LiteralElement element = RealLiteralElement.Create(image, _myServices);
node.AddValue(element);
return node;
}
public override Node ExitInteger(Token node)
{
LiteralElement element = IntegralLiteralElement.Create(node.Image, false, _myInUnaryNegate, _myServices);
node.AddValue(element);
return node;
}
public override Node ExitHexliteral(Token node)
{
LiteralElement element = IntegralLiteralElement.Create(node.Image, true, _myInUnaryNegate, _myServices);
node.AddValue(element);
return node;
}
public override Node ExitBooleanLiteralExpression(Production node)
{
this.AddFirstChildValue(node);
return node;
}
public override Node ExitTrue(Token node)
{
node.AddValue(new BooleanLiteralElement(true));
return node;
}
public override Node ExitFalse(Token node)
{
node.AddValue(new BooleanLiteralElement(false));
return node;
}
public override Node ExitStringLiteral(Token node)
{
string s = this.DoEscapes(node.Image);
StringLiteralElement element = new StringLiteralElement(s);
node.AddValue(element);
return node;
}
public override Node ExitCharLiteral(Token node)
{
string s = this.DoEscapes(node.Image);
node.AddValue(new CharLiteralElement(s[0]));
return node;
}
public override Node ExitDatetime(Token node)
{
ExpressionContext context = (ExpressionContext)_myServices.GetService(typeof(ExpressionContext));
string image = node.Image.Substring(1, node.Image.Length - 2);
DateTimeLiteralElement element = new DateTimeLiteralElement(image, context);
node.AddValue(element);
return node;
}
public override Node ExitTimespan(Token node)
{
string image = node.Image.Substring(2, node.Image.Length - 3);
TimeSpanLiteralElement element = new TimeSpanLiteralElement(image);
node.AddValue(element);
return node;
}
private string DoEscapes(string image)
{
// Remove outer quotes
image = image.Substring(1, image.Length - 2);
image = _myUnicodeEscapeRegex.Replace(image, UnicodeEscapeMatcher);
image = _myRegularEscapeRegex.Replace(image, RegularEscapeMatcher);
return image;
}
private string RegularEscapeMatcher(Match m)
{
string s = m.Value;
// Remove leading \
s = s.Remove(0, 1);
switch (s)
{
case "\\":
case "\"":
case "'":
return s;
case "t":
case "T":
return Convert.ToChar(9).ToString();
case "n":
case "N":
return Convert.ToChar(10).ToString();
case "r":
case "R":
return Convert.ToChar(13).ToString();
default:
Debug.Assert(false, "Unrecognized escape sequence");
return null;
}
}
private string UnicodeEscapeMatcher(Match m)
{
string s = m.Value;
// Remove \u
s = s.Remove(0, 2);
int code = int.Parse(s, NumberStyles.AllowHexSpecifier);
char c = Convert.ToChar(code);
return c.ToString();
}
public override Node ExitIdentifier(Token node)
{
node.AddValue(node.Image);
return node;
}
public override Node ExitNullLiteral(Token node)
{
node.AddValue(new NullLiteralElement());
return node;
}
public override Node ExitArrayBraces(Token node)
{
node.AddValue("[]");
return node;
}
public override Node ExitAdd(Token node)
{
node.AddValue(BinaryArithmeticOperation.Add);
return node;
}
public override Node ExitSub(Token node)
{
node.AddValue(BinaryArithmeticOperation.Subtract);
return node;
}
public override Node ExitMul(Token node)
{
node.AddValue(BinaryArithmeticOperation.Multiply);
return node;
}
public override Node ExitDiv(Token node)
{
node.AddValue(BinaryArithmeticOperation.Divide);
return node;
}
public override Node ExitMod(Token node)
{
node.AddValue(BinaryArithmeticOperation.Mod);
return node;
}
public override Node ExitPower(Token node)
{
node.AddValue(BinaryArithmeticOperation.Power);
return node;
}
public override Node ExitEq(Token node)
{
node.AddValue(LogicalCompareOperation.Equal);
return node;
}
public override Node ExitNe(Token node)
{
node.AddValue(LogicalCompareOperation.NotEqual);
return node;
}
public override Node ExitLt(Token node)
{
node.AddValue(LogicalCompareOperation.LessThan);
return node;
}
public override Node ExitGt(Token node)
{
node.AddValue(LogicalCompareOperation.GreaterThan);
return node;
}
public override Node ExitLte(Token node)
{
node.AddValue(LogicalCompareOperation.LessThanOrEqual);
return node;
}
public override Node ExitGte(Token node)
{
node.AddValue(LogicalCompareOperation.GreaterThanOrEqual);
return node;
}
public override Node ExitAnd(Token node)
{
node.AddValue(AndOrOperation.And);
return node;
}
public override Node ExitOr(Token node)
{
node.AddValue(AndOrOperation.Or);
return node;
}
public override Node ExitXor(Token node)
{
node.AddValue("Xor");
return node;
}
public override Node ExitNot(Token node)
{
node.AddValue(string.Empty);
return node;
}
public override Node ExitLeftShift(Token node)
{
node.AddValue(ShiftOperation.LeftShift);
return node;
}
public override Node ExitRightShift(Token node)
{
node.AddValue(ShiftOperation.RightShift);
return node;
}
public override void Child(Production node, Node child)
{
base.Child(node, child);
_myInUnaryNegate = node.Id == (int)ExpressionConstants.NEGATE_EXPRESSION & child.Id == (int)ExpressionConstants.SUB;
}
}
}

View File

@@ -0,0 +1,49 @@
using Flee.PublicTypes;
namespace Flee.Parsing
{
internal abstract class CustomTokenPattern : TokenPattern
{
protected CustomTokenPattern(int id, string name, PatternType type, string pattern) : base(id, name, type, pattern)
{
}
public void Initialize(int id, string name, PatternType type, string pattern, ExpressionContext context)
{
this.ComputeToken(id, name, type, pattern, context);
}
protected abstract void ComputeToken(int id, string name, PatternType type, string pattern, ExpressionContext context);
}
internal class RealPattern : CustomTokenPattern
{
public RealPattern(int id, string name, PatternType type, string pattern) : base(id, name, type, pattern)
{
}
protected override void ComputeToken(int id, string name, PatternType type, string pattern, ExpressionContext context)
{
ExpressionParserOptions options = context.ParserOptions;
char digitsBeforePattern = (options.RequireDigitsBeforeDecimalPoint ? '+' : '*');
pattern = string.Format(pattern, digitsBeforePattern, options.DecimalSeparator);
this.SetData(id, name, type, pattern);
}
}
internal class ArgumentSeparatorPattern : CustomTokenPattern
{
public ArgumentSeparatorPattern(int id, string name, PatternType type, string pattern) : base(id, name, type, pattern)
{
}
protected override void ComputeToken(int id, string name, PatternType type, string pattern, ExpressionContext context)
{
ExpressionParserOptions options = context.ParserOptions;
this.SetData(id, name, type, options.FunctionArgumentSeparator.ToString());
}
}
}

19
Parsing/Element.cs Normal file
View File

@@ -0,0 +1,19 @@
namespace Flee.Parsing
{
/**
* A regular expression element. This is the common base class for
* all regular expression elements, i.e. the parts of the regular
* expression.
*/
internal abstract class Element : ICloneable
{
public abstract object Clone();
public abstract int Match(Matcher m,
ReaderBuffer buffer,
int start,
int skip);
public abstract void PrintTo(TextWriter output, string indent);
}
}

133
Parsing/Expression.grammar Normal file
View File

@@ -0,0 +1,133 @@
%header%
DESCRIPTION = "A general expression grammar"
AUTHOR = "Eugene Ciloci"
VERSION = "1.0"
DATE = "May 2007"
GRAMMARTYPE = "LL"
CASESENSITIVE = "False"
LICENSE = "This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1
of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307, USA.
"
COPYRIGHT = "Copyright (c) 2007 Eugene Ciloci"
%tokens%
ADD = "+"
SUB = "-"
MUL = "*"
DIV = "/"
POWER = "^"
MOD = "%"
LEFT_PAREN = "("
RIGHT_PAREN = ")"
LEFT_BRACE = "["
RIGHT_BRACE = "]"
EQ = "="
LT = "<"
GT = ">"
LTE = "<="
GTE = ">="
NE = "<>"
AND = "AND"
OR = "OR"
XOR = "XOR"
NOT = "NOT"
IN = "in"
DOT = "."
ARGUMENT_SEPARATOR = ","
ARRAY_BRACES = "[]"
LEFT_SHIFT = "<<"
RIGHT_SHIFT = ">>"
WHITESPACE = <<\s+>> %ignore%
// Primitives
INTEGER = <<\d+(u|l|ul|lu)?>>
REAL = <<\d*\.\d+([e][+-]\d{1,3})?f?>>
STRING_LITERAL = <<"([^"\r\n\\]|\\u[0-9a-f]{4}|\\[\\"'trn])*">>
CHAR_LITERAL = <<'([^'\r\n\\]|\\u[0-9a-f]{4}|\\[\\"'trn])'>>
TRUE = "True"
FALSE = "False"
IDENTIFIER = <<[a-z_]\w*>>
HEX_LITERAL = <<0x[0-9a-f]+(u|l|ul|lu)?>>
NULL_LITERAL = "null"
TIMESPAN = <<##(\d+\.)?\d{2}:\d{2}(:\d{2}(\.\d{1,7})?)?#>>
DATETIME = <<#[^#]+#>>
// Special Functions
IF = "if"
CAST = "cast"
%productions%
Expression = XorExpression;
XorExpression = OrExpression {XOR OrExpression};
OrExpression = AndExpression {OR AndExpression};
AndExpression = NotExpression {AND NotExpression};
NotExpression = NOT? InExpression;
InExpression = CompareExpression [IN InTargetExpression];
InTargetExpression = FieldPropertyExpression | InListTargetExpression;
InListTargetExpression = "(" ArgumentList ")";
CompareExpression = ShiftExpression {("=" | ">" | "<" | ">=" | "<=" | "<>") ShiftExpression};
ShiftExpression = AdditiveExpression {("<<" | ">>") AdditiveExpression};
AdditiveExpression = MultiplicativeExpression {("+" | "-") MultiplicativeExpression};
MultiplicativeExpression = PowerExpression {("*" | "/" | "%") PowerExpression};
PowerExpression = NegateExpression {"^" NegateExpression};
NegateExpression = "-"? MemberExpression;
MemberExpression = BasicExpression {MemberAccessExpression | IndexExpression};
MemberAccessExpression = "." MemberFunctionExpression;
BasicExpression = LiteralExpression | ExpressionGroup | MemberFunctionExpression | SpecialFunctionExpression;
MemberFunctionExpression = FieldPropertyExpression | FunctionCallExpression;
FieldPropertyExpression = IDENTIFIER;
SpecialFunctionExpression = IfExpression | CastExpression;
IfExpression = IF "(" Expression "," Expression "," Expression ")";
CastExpression = CAST "(" Expression "," CastTypeExpression ")";
CastTypeExpression = IDENTIFIER {"." IDENTIFIER} ARRAY_BRACES?;
IndexExpression = "[" ArgumentList "]";
FunctionCallExpression = IDENTIFIER "(" ArgumentList? ")";
ArgumentList = Expression {"," Expression};
LiteralExpression = INTEGER | REAL | STRING_LITERAL | BooleanLiteralExpression | HEX_LITERAL | CHAR_LITERAL | NULL_LITERAL | DATETIME | TIMESPAN;
BooleanLiteralExpression = TRUE | FALSE;
ExpressionGroup = "(" Expression ")";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
namespace Flee.Parsing
{
///<remarks>
/// An enumeration with token and production node
///constants.</remarks>
internal enum ExpressionConstants
{
ADD = 1001,
SUB = 1002,
MUL = 1003,
DIV = 1004,
POWER = 1005,
MOD = 1006,
LEFT_PAREN = 1007,
RIGHT_PAREN = 1008,
LEFT_BRACE = 1009,
RIGHT_BRACE = 1010,
EQ = 1011,
LT = 1012,
GT = 1013,
LTE = 1014,
GTE = 1015,
NE = 1016,
AND = 1017,
OR = 1018,
XOR = 1019,
NOT = 1020,
IN = 1021,
DOT = 1022,
ARGUMENT_SEPARATOR = 1023,
ARRAY_BRACES = 1024,
LEFT_SHIFT = 1025,
RIGHT_SHIFT = 1026,
WHITESPACE = 1027,
INTEGER = 1028,
REAL = 1029,
STRING_LITERAL = 1030,
CHAR_LITERAL = 1031,
TRUE = 1032,
FALSE = 1033,
NULL_LITERAL = 1034,
HEX_LITERAL = 1035,
IDENTIFIER = 1036,
TIMESPAN = 1037,
DATETIME = 1038,
IF = 1039,
CAST = 1040,
EXPRESSION = 2001,
XOR_EXPRESSION = 2002,
OR_EXPRESSION = 2003,
AND_EXPRESSION = 2004,
NOT_EXPRESSION = 2005,
IN_EXPRESSION = 2006,
IN_TARGET_EXPRESSION = 2007,
IN_LIST_TARGET_EXPRESSION = 2008,
COMPARE_EXPRESSION = 2009,
SHIFT_EXPRESSION = 2010,
ADDITIVE_EXPRESSION = 2011,
MULTIPLICATIVE_EXPRESSION = 2012,
POWER_EXPRESSION = 2013,
NEGATE_EXPRESSION = 2014,
MEMBER_EXPRESSION = 2015,
MEMBER_ACCESS_EXPRESSION = 2016,
BASIC_EXPRESSION = 2017,
MEMBER_FUNCTION_EXPRESSION = 2018,
FIELD_PROPERTY_EXPRESSION = 2019,
SPECIAL_FUNCTION_EXPRESSION = 2020,
IF_EXPRESSION = 2021,
CAST_EXPRESSION = 2022,
CAST_TYPE_EXPRESSION = 2023,
INDEX_EXPRESSION = 2024,
FUNCTION_CALL_EXPRESSION = 2025,
ARGUMENT_LIST = 2026,
LITERAL_EXPRESSION = 2027,
BOOLEAN_LITERAL_EXPRESSION = 2028,
EXPRESSION_GROUP = 2029
}
}

460
Parsing/ExpressionParser.cs Normal file
View File

@@ -0,0 +1,460 @@
using Flee.PublicTypes;
namespace Flee.Parsing
{
/// <summary>
/// A token stream parser.
/// </summary>
internal class ExpressionParser : StackParser
{
private enum SynteticPatterns
{
SUBPRODUCTION_1 = 3001,
SUBPRODUCTION_2 = 3002,
SUBPRODUCTION_3 = 3003,
SUBPRODUCTION_4 = 3004,
SUBPRODUCTION_5 = 3005,
SUBPRODUCTION_6 = 3006,
SUBPRODUCTION_7 = 3007,
SUBPRODUCTION_8 = 3008,
SUBPRODUCTION_9 = 3009,
SUBPRODUCTION_10 = 3010,
SUBPRODUCTION_11 = 3011,
SUBPRODUCTION_12 = 3012,
SUBPRODUCTION_13 = 3013,
SUBPRODUCTION_14 = 3014,
SUBPRODUCTION_15 = 3015,
SUBPRODUCTION_16 = 3016
}
public ExpressionParser(TextReader input, Analyzer analyzer, ExpressionContext context) : base(new ExpressionTokenizer(input, context), analyzer)
{
CreatePatterns();
}
public ExpressionParser(TextReader input) : base(new ExpressionTokenizer(input))
{
CreatePatterns();
}
public ExpressionParser(TextReader input, Analyzer analyzer) : base(new ExpressionTokenizer(input), analyzer)
{
CreatePatterns();
}
private void CreatePatterns()
{
ProductionPattern pattern = default(ProductionPattern);
ProductionPatternAlternative alt = default(ProductionPatternAlternative);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.EXPRESSION), "Expression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.XOR_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.XOR_EXPRESSION), "XorExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.OR_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_1), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.OR_EXPRESSION), "OrExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.AND_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_2), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.AND_EXPRESSION), "AndExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.NOT_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_3), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.NOT_EXPRESSION), "NotExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.NOT), 0, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.IN_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.IN_EXPRESSION), "InExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.COMPARE_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_4), 0, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.IN_TARGET_EXPRESSION), "InTargetExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.FIELD_PROPERTY_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.IN_LIST_TARGET_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.IN_LIST_TARGET_EXPRESSION), "InListTargetExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.LEFT_PAREN), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.ARGUMENT_LIST), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.RIGHT_PAREN), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.COMPARE_EXPRESSION), "CompareExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.SHIFT_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_6), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.SHIFT_EXPRESSION), "ShiftExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.ADDITIVE_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_8), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.ADDITIVE_EXPRESSION), "AdditiveExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.MULTIPLICATIVE_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_10), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.MULTIPLICATIVE_EXPRESSION), "MultiplicativeExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.POWER_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_12), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.POWER_EXPRESSION), "PowerExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.NEGATE_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_13), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.NEGATE_EXPRESSION), "NegateExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.SUB), 0, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.MEMBER_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.MEMBER_EXPRESSION), "MemberExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.BASIC_EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_14), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.MEMBER_ACCESS_EXPRESSION), "MemberAccessExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.DOT), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.MEMBER_FUNCTION_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.BASIC_EXPRESSION), "BasicExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.LITERAL_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION_GROUP), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.MEMBER_FUNCTION_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.SPECIAL_FUNCTION_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.MEMBER_FUNCTION_EXPRESSION), "MemberFunctionExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.FIELD_PROPERTY_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.FUNCTION_CALL_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.FIELD_PROPERTY_EXPRESSION), "FieldPropertyExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.IDENTIFIER), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.SPECIAL_FUNCTION_EXPRESSION), "SpecialFunctionExpression");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.IF_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.CAST_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.IF_EXPRESSION), "IfExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.IF), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.LEFT_PAREN), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.ARGUMENT_SEPARATOR), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.ARGUMENT_SEPARATOR), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.RIGHT_PAREN), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.CAST_EXPRESSION), "CastExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.CAST), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.LEFT_PAREN), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.ARGUMENT_SEPARATOR), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.CAST_TYPE_EXPRESSION), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.RIGHT_PAREN), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.CAST_TYPE_EXPRESSION), "CastTypeExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.IDENTIFIER), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_15), 0, -1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.ARRAY_BRACES), 0, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.INDEX_EXPRESSION), "IndexExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.LEFT_BRACE), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.ARGUMENT_LIST), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.RIGHT_BRACE), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.FUNCTION_CALL_EXPRESSION), "FunctionCallExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.IDENTIFIER), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.LEFT_PAREN), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.ARGUMENT_LIST), 0, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.RIGHT_PAREN), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.ARGUMENT_LIST), "ArgumentList");
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION), 1, 1);
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_16), 0, -1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.LITERAL_EXPRESSION), "LiteralExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.INTEGER), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.REAL), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.STRING_LITERAL), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.BOOLEAN_LITERAL_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.HEX_LITERAL), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.CHAR_LITERAL), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.NULL_LITERAL), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.DATETIME), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.TIMESPAN), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.BOOLEAN_LITERAL_EXPRESSION), "BooleanLiteralExpression");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.TRUE), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.FALSE), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(ExpressionConstants.EXPRESSION_GROUP), "ExpressionGroup");
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.LEFT_PAREN), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.RIGHT_PAREN), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_1), "Subproduction1");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.XOR), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.OR_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_2), "Subproduction2");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.OR), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.AND_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_3), "Subproduction3");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.AND), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.NOT_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_4), "Subproduction4");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.IN), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.IN_TARGET_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_5), "Subproduction5");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.EQ), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.GT), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.LT), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.GTE), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.LTE), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.NE), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_6), "Subproduction6");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_5), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.SHIFT_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_7), "Subproduction7");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.LEFT_SHIFT), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.RIGHT_SHIFT), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_8), "Subproduction8");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_7), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.ADDITIVE_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_9), "Subproduction9");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.ADD), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.SUB), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_10), "Subproduction10");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_9), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.MULTIPLICATIVE_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_11), "Subproduction11");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.MUL), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.DIV), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.MOD), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_12), "Subproduction12");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_11), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.POWER_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_13), "Subproduction13");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.POWER), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.NEGATE_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_14), "Subproduction14");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.MEMBER_ACCESS_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
alt = new ProductionPatternAlternative();
alt.AddProduction(Convert.ToInt32(ExpressionConstants.INDEX_EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_15), "Subproduction15");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.DOT), 1, 1);
alt.AddToken(Convert.ToInt32(ExpressionConstants.IDENTIFIER), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
pattern = new ProductionPattern(Convert.ToInt32(SynteticPatterns.SUBPRODUCTION_16), "Subproduction16");
pattern.Synthetic = true;
alt = new ProductionPatternAlternative();
alt.AddToken(Convert.ToInt32(ExpressionConstants.ARGUMENT_SEPARATOR), 1, 1);
alt.AddProduction(Convert.ToInt32(ExpressionConstants.EXPRESSION), 1, 1);
pattern.AddAlternative(alt);
AddPattern(pattern);
}
}
}

View File

@@ -0,0 +1,153 @@
using Flee.PublicTypes;
namespace Flee.Parsing
{
/// <summary>
/// A character stream tokenizer.
/// </summary>
internal class ExpressionTokenizer : Tokenizer
{
private readonly ExpressionContext _myContext;
public ExpressionTokenizer(TextReader input, ExpressionContext context) : base(input, true)
{
_myContext = context;
CreatePatterns();
}
public ExpressionTokenizer(TextReader input) : base(input, true)
{
CreatePatterns();
}
private void CreatePatterns()
{
TokenPattern pattern = default(TokenPattern);
CustomTokenPattern customPattern = default(CustomTokenPattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.ADD), "ADD", TokenPattern.PatternType.STRING, "+");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.SUB), "SUB", TokenPattern.PatternType.STRING, "-");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.MUL), "MUL", TokenPattern.PatternType.STRING, "*");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.DIV), "DIV", TokenPattern.PatternType.STRING, "/");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.POWER), "POWER", TokenPattern.PatternType.STRING, "^");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.MOD), "MOD", TokenPattern.PatternType.STRING, "%");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.LEFT_PAREN), "LEFT_PAREN", TokenPattern.PatternType.STRING, "(");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.RIGHT_PAREN), "RIGHT_PAREN", TokenPattern.PatternType.STRING, ")");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.LEFT_BRACE), "LEFT_BRACE", TokenPattern.PatternType.STRING, "[");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.RIGHT_BRACE), "RIGHT_BRACE", TokenPattern.PatternType.STRING, "]");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.EQ), "EQ", TokenPattern.PatternType.STRING, "=");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.LT), "LT", TokenPattern.PatternType.STRING, "<");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.GT), "GT", TokenPattern.PatternType.STRING, ">");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.LTE), "LTE", TokenPattern.PatternType.STRING, "<=");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.GTE), "GTE", TokenPattern.PatternType.STRING, ">=");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.NE), "NE", TokenPattern.PatternType.STRING, "<>");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.AND), "AND", TokenPattern.PatternType.STRING, "AND");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.OR), "OR", TokenPattern.PatternType.STRING, "OR");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.XOR), "XOR", TokenPattern.PatternType.STRING, "XOR");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.NOT), "NOT", TokenPattern.PatternType.STRING, "NOT");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.IN), "IN", TokenPattern.PatternType.STRING, "in");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.DOT), "DOT", TokenPattern.PatternType.STRING, ".");
AddPattern(pattern);
customPattern = new ArgumentSeparatorPattern(Convert.ToInt32(ExpressionConstants.ARGUMENT_SEPARATOR), "ARGUMENT_SEPARATOR", TokenPattern.PatternType.STRING, ",");
customPattern.Initialize(Convert.ToInt32(ExpressionConstants.ARGUMENT_SEPARATOR), "ARGUMENT_SEPARATOR", TokenPattern.PatternType.STRING, ",", _myContext);
AddPattern(customPattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.ARRAY_BRACES), "ARRAY_BRACES", TokenPattern.PatternType.STRING, "[]");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.LEFT_SHIFT), "LEFT_SHIFT", TokenPattern.PatternType.STRING, "<<");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.RIGHT_SHIFT), "RIGHT_SHIFT", TokenPattern.PatternType.STRING, ">>");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.WHITESPACE), "WHITESPACE", TokenPattern.PatternType.REGEXP, "\\s+");
pattern.Ignore = true;
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.INTEGER), "INTEGER", TokenPattern.PatternType.REGEXP, "\\d+(u|l|ul|lu|f|m)?");
AddPattern(pattern);
customPattern = new RealPattern(Convert.ToInt32(ExpressionConstants.REAL), "REAL", TokenPattern.PatternType.REGEXP, "\\d{0}\\{1}\\d+([e][+-]\\d{{1,3}})?(d|f|m)?");
customPattern.Initialize(Convert.ToInt32(ExpressionConstants.REAL), "REAL", TokenPattern.PatternType.REGEXP, "\\d{0}\\{1}\\d+([e][+-]\\d{{1,3}})?(d|f|m)?", _myContext);
AddPattern(customPattern, false);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.STRING_LITERAL), "STRING_LITERAL", TokenPattern.PatternType.REGEXP, "\"([^\"\\r\\n\\\\]|\\\\u[0-9a-f]{4}|\\\\[\\\\\"'trn])*\"");
AddPattern(pattern, false);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.CHAR_LITERAL), "CHAR_LITERAL", TokenPattern.PatternType.REGEXP, "'([^'\\r\\n\\\\]|\\\\u[0-9a-f]{4}|\\\\[\\\\\"'trn])'");
AddPattern(pattern, false);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.TRUE), "TRUE", TokenPattern.PatternType.STRING, "True");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.FALSE), "FALSE", TokenPattern.PatternType.STRING, "False");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.IDENTIFIER), "IDENTIFIER", TokenPattern.PatternType.REGEXP, "[a-z_]\\w*");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.HEX_LITERAL), "HEX_LITERAL", TokenPattern.PatternType.REGEXP, "0x[0-9a-f]+(u|l|ul|lu)?");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.NULL_LITERAL), "NULL_LITERAL", TokenPattern.PatternType.STRING, "null");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.TIMESPAN), "TIMESPAN", TokenPattern.PatternType.REGEXP, "##(\\d+\\.)?\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,7})?)?#");
AddPattern(pattern, false);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.DATETIME), "DATETIME", TokenPattern.PatternType.REGEXP, "#[^#]+#");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.IF), "IF", TokenPattern.PatternType.STRING, "if");
AddPattern(pattern);
pattern = new TokenPattern(Convert.ToInt32(ExpressionConstants.CAST), "CAST", TokenPattern.PatternType.STRING, "cast");
AddPattern(pattern);
}
}
}

226
Parsing/LookAheadReader.cs Normal file
View File

@@ -0,0 +1,226 @@
namespace Flee.Parsing
{
// * A look-ahead character stream reader. This class provides the
// * functionalities of a buffered line-number reader, but with the
// * additional possibility of peeking an unlimited number of
// * characters ahead. When looking further and further ahead in the
// * character stream, the buffer is continously enlarged to contain
// * all the required characters from the current position an
// * onwards. This means that looking more characters ahead requires
// * more memory, and thus becomes unviable in the end.
internal class LookAheadReader : TextReader
{
private const int StreamBlockSize = 4096;
private const int BufferBlockSize = 1024;
private char[] _buffer = new char[StreamBlockSize];
private int _pos;
private int _length;
private TextReader _input = null;
private int _line = 1;
private int _column = 1;
public LookAheadReader(TextReader input) : base()
{
this._input = input;
}
public int LineNumber => _line;
public int ColumnNumber => _column;
public override int Read()
{
ReadAhead(1);
if (_pos >= _length)
{
return -1;
}
else
{
UpdateLineColumnNumbers(1);
return Convert.ToInt32(_buffer[System.Math.Max(System.Threading.Interlocked.Increment(ref _pos), _pos - 1)]);
}
}
public override int Read(char[] cbuf, int off, int len)
{
ReadAhead(len);
if (_pos >= _length)
{
return -1;
}
else
{
var count = _length - _pos;
if (count > len)
{
count = len;
}
UpdateLineColumnNumbers(count);
Array.Copy(_buffer, _pos, cbuf, off, count);
_pos += count;
return count;
}
}
public string ReadString(int len)
{
ReadAhead(len);
if (_pos >= _length)
{
return null;
}
else
{
var count = _length - _pos;
if (count > len)
{
count = len;
}
UpdateLineColumnNumbers(count);
var result = new string(_buffer, _pos, count);
_pos += count;
return result;
}
}
public override int Peek()
{
return Peek(0);
}
public int Peek(int off)
{
ReadAhead(off + 1);
if (_pos + off >= _length)
{
return -1;
}
else
{
return Convert.ToInt32(_buffer[_pos + off]);
}
}
public string PeekString(int off, int len)
{
ReadAhead(off + len + 1);
if (_pos + off >= _length)
{
return null;
}
else
{
var count = _length - (_pos + off);
if (count > len)
{
count = len;
}
return new string(_buffer, _pos + off, count);
}
}
public override void Close()
{
_buffer = null;
_pos = 0;
_length = 0;
if (_input != null)
{
_input.Close();
_input = null;
}
}
private void ReadAhead(int offset)
{
int size = 0;
int readSize = 0;
// Check for end of stream or already read characters
if (_input == null || _pos + offset < _length)
{
return;
}
// Remove old characters from buffer
if (_pos > BufferBlockSize)
{
Array.Copy(_buffer, _pos, _buffer, 0, _length - _pos);
_length -= _pos;
_pos = 0;
}
// Calculate number of characters to read
size = _pos + offset - _length + 1;
if (size % StreamBlockSize != 0)
{
size = (size / StreamBlockSize) * StreamBlockSize;
size += StreamBlockSize;
}
EnsureBufferCapacity(_length + size);
// Read characters
try
{
readSize = _input.Read(_buffer, _length, size);
}
catch (IOException e)
{
_input = null;
throw;
}
// Append characters to buffer
if (readSize > 0)
{
_length += readSize;
}
if (readSize < size)
{
try
{
_input.Close();
}
finally
{
_input = null;
}
}
}
private void EnsureBufferCapacity(int size)
{
char[] newbuf = null;
if (_buffer.Length >= size)
{
return;
}
if (size % BufferBlockSize != 0)
{
size = (size / BufferBlockSize) * BufferBlockSize;
size += BufferBlockSize;
}
newbuf = new char[size];
Array.Copy(_buffer, 0, newbuf, 0, _length);
_buffer = newbuf;
}
private void UpdateLineColumnNumbers(int offset)
{
for (int i = 0; i <= offset - 1; i++)
{
if (_buffer.Contains(_buffer[_pos + i]))
{
_line += 1;
_column = 1;
}
else
{
_column += 1;
}
}
}
}
}

589
Parsing/LookAheadSet.cs Normal file
View File

@@ -0,0 +1,589 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
/*
* A token look-ahead set. This class contains a set of token id
* sequences. All sequences in the set are limited in length, so
* that no single sequence is longer than a maximum value. This
* class also filters out duplicates. Each token sequence also
* contains a repeat flag, allowing the look-ahead set to contain
* information about possible infinite repetitions of certain
* sequences. That information is important when conflicts arise
* between two look-ahead sets, as such a conflict cannot be
* resolved if the conflicting sequences can be repeated (would
* cause infinite loop).
*/
internal class LookAheadSet
{
private readonly ArrayList _elements = new ArrayList();
private readonly int _maxLength;
public LookAheadSet(int maxLength)
{
this._maxLength = maxLength;
}
public LookAheadSet(int maxLength, LookAheadSet set)
: this(maxLength)
{
AddAll(set);
}
public int Size()
{
return _elements.Count;
}
public int GetMinLength()
{
int min = -1;
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
if (min < 0 || seq.Length() < min)
{
min = seq.Length();
}
}
return (min < 0) ? 0 : min;
}
public int GetMaxLength()
{
int max = 0;
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
if (seq.Length() > max)
{
max = seq.Length();
}
}
return max;
}
public int[] GetInitialTokens()
{
ArrayList list = new ArrayList();
int i;
for (i = 0; i < _elements.Count; i++)
{
var token = ((Sequence)_elements[i]).GetToken(0);
if (token != null && !list.Contains(token))
{
list.Add(token);
}
}
var result = new int[list.Count];
for (i = 0; i < list.Count; i++)
{
result[i] = (int)list[i];
}
return result;
}
public bool IsRepetitive()
{
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
if (seq.IsRepetitive())
{
return true;
}
}
return false;
}
public bool IsNext(Parser parser)
{
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
if (seq.IsNext(parser))
{
return true;
}
}
return false;
}
public bool IsNext(Parser parser, int length)
{
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
if (seq.IsNext(parser, length))
{
return true;
}
}
return false;
}
public bool IsOverlap(LookAheadSet set)
{
for (int i = 0; i < _elements.Count; i++)
{
if (set.IsOverlap((Sequence)_elements[i]))
{
return true;
}
}
return false;
}
private bool IsOverlap(Sequence seq)
{
for (int i = 0; i < _elements.Count; i++)
{
var elem = (Sequence)_elements[i];
if (seq.StartsWith(elem) || elem.StartsWith(seq))
{
return true;
}
}
return false;
}
private bool Contains(Sequence elem)
{
return FindSequence(elem) != null;
}
public bool Intersects(LookAheadSet set)
{
for (int i = 0; i < _elements.Count; i++)
{
if (set.Contains((Sequence)_elements[i]))
{
return true;
}
}
return false;
}
private Sequence FindSequence(Sequence elem)
{
for (int i = 0; i < _elements.Count; i++)
{
if (_elements[i].Equals(elem))
{
return (Sequence)_elements[i];
}
}
return null;
}
private void Add(Sequence seq)
{
if (seq.Length() > _maxLength)
{
seq = new Sequence(_maxLength, seq);
}
if (!Contains(seq))
{
_elements.Add(seq);
}
}
public void Add(int token)
{
Add(new Sequence(false, token));
}
public void AddAll(LookAheadSet set)
{
for (int i = 0; i < set._elements.Count; i++)
{
Add((Sequence)set._elements[i]);
}
}
public void AddEmpty()
{
Add(new Sequence());
}
private void Remove(Sequence seq)
{
_elements.Remove(seq);
}
public void RemoveAll(LookAheadSet set)
{
for (int i = 0; i < set._elements.Count; i++)
{
Remove((Sequence)set._elements[i]);
}
}
public LookAheadSet CreateNextSet(int token)
{
LookAheadSet result = new LookAheadSet(_maxLength - 1);
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
var value = seq.GetToken(0);
if (value != null && token == (int)value)
{
result.Add(seq.Subsequence(1));
}
}
return result;
}
public LookAheadSet CreateIntersection(LookAheadSet set)
{
LookAheadSet result = new LookAheadSet(_maxLength);
for (int i = 0; i < _elements.Count; i++)
{
var seq1 = (Sequence)_elements[i];
var seq2 = set.FindSequence(seq1);
if (seq2 != null && seq1.IsRepetitive())
{
result.Add(seq2);
}
else if (seq2 != null)
{
result.Add(seq1);
}
}
return result;
}
public LookAheadSet CreateCombination(LookAheadSet set)
{
LookAheadSet result = new LookAheadSet(_maxLength);
// Handle special cases
if (this.Size() <= 0)
{
return set;
}
else if (set.Size() <= 0)
{
return this;
}
// Create combinations
for (int i = 0; i < _elements.Count; i++)
{
var first = (Sequence)_elements[i];
if (first.Length() >= _maxLength)
{
result.Add(first);
}
else if (first.Length() <= 0)
{
result.AddAll(set);
}
else
{
for (int j = 0; j < set._elements.Count; j++)
{
var second = (Sequence)set._elements[j];
result.Add(first.Concat(_maxLength, second));
}
}
}
return result;
}
public LookAheadSet CreateOverlaps(LookAheadSet set)
{
LookAheadSet result = new LookAheadSet(_maxLength);
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
if (set.IsOverlap(seq))
{
result.Add(seq);
}
}
return result;
}
public LookAheadSet CreateFilter(LookAheadSet set)
{
LookAheadSet result = new LookAheadSet(_maxLength);
// Handle special cases
if (this.Size() <= 0 || set.Size() <= 0)
{
return this;
}
// Create combinations
for (int i = 0; i < _elements.Count; i++)
{
var first = (Sequence)_elements[i];
for (int j = 0; j < set._elements.Count; j++)
{
var second = (Sequence)set._elements[j];
if (first.StartsWith(second))
{
result.Add(first.Subsequence(second.Length()));
}
}
}
return result;
}
public LookAheadSet CreateRepetitive()
{
LookAheadSet result = new LookAheadSet(_maxLength);
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
if (seq.IsRepetitive())
{
result.Add(seq);
}
else
{
result.Add(new Sequence(true, seq));
}
}
return result;
}
public override string ToString()
{
return ToString(null);
}
public string ToString(Tokenizer tokenizer)
{
StringBuilder buffer = new StringBuilder();
buffer.Append("{");
for (int i = 0; i < _elements.Count; i++)
{
var seq = (Sequence)_elements[i];
buffer.Append("\n ");
buffer.Append(seq.ToString(tokenizer));
}
buffer.Append("\n}");
return buffer.ToString();
}
private class Sequence
{
private bool _repeat;
private readonly ArrayList _tokens;
public Sequence()
{
this._repeat = false;
this._tokens = new ArrayList(0);
}
public Sequence(bool repeat, int token)
{
_repeat = false;
_tokens = new ArrayList(1);
_tokens.Add(token);
}
public Sequence(int length, Sequence seq)
{
this._repeat = seq._repeat;
this._tokens = new ArrayList(length);
if (seq.Length() < length)
{
length = seq.Length();
}
for (int i = 0; i < length; i++)
{
_tokens.Add(seq._tokens[i]);
}
}
public Sequence(bool repeat, Sequence seq)
{
this._repeat = repeat;
this._tokens = seq._tokens;
}
public int Length()
{
return _tokens.Count;
}
public object GetToken(int pos)
{
if (pos >= 0 && pos < _tokens.Count)
{
return _tokens[pos];
}
else
{
return null;
}
}
public override bool Equals(object obj)
{
if (obj is Sequence)
{
return Equals((Sequence)obj);
}
else
{
return false;
}
}
public bool Equals(Sequence seq)
{
if (_tokens.Count != seq._tokens.Count)
{
return false;
}
for (int i = 0; i < _tokens.Count; i++)
{
if (!_tokens[i].Equals(seq._tokens[i]))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
return _tokens.Count.GetHashCode();
}
public bool StartsWith(Sequence seq)
{
if (Length() < seq.Length())
{
return false;
}
for (int i = 0; i < seq._tokens.Count; i++)
{
if (!_tokens[i].Equals(seq._tokens[i]))
{
return false;
}
}
return true;
}
public bool IsRepetitive()
{
return _repeat;
}
public bool IsNext(Parser parser)
{
for (int i = 0; i < _tokens.Count; i++)
{
var id = (int)_tokens[i];
var token = parser.PeekToken(i);
if (token == null || token.Id != id)
{
return false;
}
}
return true;
}
public bool IsNext(Parser parser, int length)
{
if (length > _tokens.Count)
{
length = _tokens.Count;
}
for (int i = 0; i < length; i++)
{
var id = (int)_tokens[i];
var token = parser.PeekToken(i);
if (token == null || token.Id != id)
{
return false;
}
}
return true;
}
public override string ToString()
{
return ToString(null);
}
public string ToString(Tokenizer tokenizer)
{
StringBuilder buffer = new StringBuilder();
if (tokenizer == null)
{
buffer.Append(_tokens.ToString());
}
else
{
buffer.Append("[");
for (int i = 0; i < _tokens.Count; i++)
{
var id = (int)_tokens[i];
var str = tokenizer.GetPatternDescription(id);
if (i > 0)
{
buffer.Append(" ");
}
buffer.Append(str);
}
buffer.Append("]");
}
if (_repeat)
{
buffer.Append(" *");
}
return buffer.ToString();
}
public Sequence Concat(int length, Sequence seq)
{
Sequence res = new Sequence(length, this);
if (seq._repeat)
{
res._repeat = true;
}
length -= this.Length();
if (length > seq.Length())
{
res._tokens.AddRange(seq._tokens);
}
else
{
for (int i = 0; i < length; i++)
{
res._tokens.Add(seq._tokens[i]);
}
}
return res;
}
public Sequence Subsequence(int start)
{
Sequence res = new Sequence(Length(), this);
while (start > 0 && res._tokens.Count > 0)
{
res._tokens.RemoveAt(0);
start--;
}
return res;
}
}
}
}

107
Parsing/Matcher.cs Normal file
View File

@@ -0,0 +1,107 @@
namespace Flee.Parsing
{
/**
* A regular expression string matcher. This class handles the
* matching of a specific string with a specific regular
* expression. It contains state information about the matching
* process, as for example the position of the latest match, and a
* number of flags that were set. This class is not thread-safe.
*/
internal class Matcher
{
private readonly Element _element;
private ReaderBuffer _buffer;
private readonly bool _ignoreCase;
private int _start;
private int _length;
private bool _endOfString;
internal Matcher(Element e, ReaderBuffer buffer, bool ignoreCase)
{
this._element = e;
this._buffer = buffer;
this._ignoreCase = ignoreCase;
this._start = 0;
Reset();
}
public bool IsCaseInsensitive()
{
return _ignoreCase;
}
public void Reset()
{
_length = -1;
_endOfString = false;
}
public void Reset(string str)
{
Reset(new ReaderBuffer(new StringReader(str)));
}
public void Reset(ReaderBuffer buffer)
{
this._buffer = buffer;
Reset();
}
public int Start()
{
return _start;
}
public int End()
{
if (_length > 0)
{
return _start + _length;
}
else
{
return _start;
}
}
public int Length()
{
return _length;
}
public bool HasReadEndOfString()
{
return _endOfString;
}
public bool MatchFromBeginning()
{
return MatchFrom(0);
}
public bool MatchFrom(int pos)
{
Reset();
_start = pos;
_length = _element.Match(this, _buffer, _start, 0);
return _length >= 0;
}
public override string ToString()
{
if (_length <= 0)
{
return "";
}
else
{
return _buffer.Substring(_buffer.Position, _length);
}
}
internal void SetReadEndOfString()
{
_endOfString = true;
}
}
}

240
Parsing/Node.cs Normal file
View File

@@ -0,0 +1,240 @@
using System.Collections;
namespace Flee.Parsing
{
/**
* An abstract parse tree node. This class is inherited by all
* nodes in the parse tree, i.e. by the token and production
* classes.
*/
internal abstract class Node
{
private Node _parent;
private ArrayList _values;
internal virtual bool IsHidden()
{
return false;
}
public abstract int Id
{
get;
}
public virtual int GetId()
{
return Id;
}
public abstract string Name
{
get;
}
public virtual string GetName()
{
return Name;
}
public virtual int StartLine
{
get
{
for (int i = 0; i < Count; i++)
{
var line = this[i].StartLine;
if (line >= 0)
{
return line;
}
}
return -1;
}
}
public virtual int GetStartLine()
{
return StartLine;
}
public virtual int StartColumn
{
get
{
for (int i = 0; i < Count; i++)
{
var col = this[i].StartColumn;
if (col >= 0)
{
return col;
}
}
return -1;
}
}
public virtual int GetStartColumn()
{
return StartColumn;
}
public virtual int EndLine
{
get
{
for (int i = Count - 1; i >= 0; i--)
{
var line = this[i].EndLine;
if (line >= 0)
{
return line;
}
}
return -1;
}
}
public virtual int GetEndLine()
{
return EndLine;
}
public virtual int EndColumn
{
get
{
int col;
for (int i = Count - 1; i >= 0; i--)
{
col = this[i].EndColumn;
if (col >= 0)
{
return col;
}
}
return -1;
}
}
public virtual int GetEndColumn()
{
return EndColumn;
}
public Node Parent => _parent;
public Node GetParent()
{
return Parent;
}
internal void SetParent(Node parent)
{
this._parent = parent;
}
public virtual int Count => 0;
public virtual int GetChildCount()
{
return Count;
}
public int GetDescendantCount()
{
int count = 0;
for (int i = 0; i < Count; i++)
{
count += 1 + this[i].GetDescendantCount();
}
return count;
}
public virtual Node this[int index] => null;
public virtual Node GetChildAt(int index)
{
return this[index];
}
public ArrayList Values
{
get
{
if (_values == null)
{
_values = new ArrayList();
}
return _values;
}
set
{
this._values = value;
}
}
public int GetValueCount()
{
if (_values == null)
{
return 0;
}
else
{
return _values.Count;
}
}
public object GetValue(int pos)
{
return Values[pos];
}
public ArrayList GetAllValues()
{
return _values;
}
public void AddValue(object value)
{
if (value != null)
{
Values.Add(value);
}
}
public void AddValues(ArrayList values)
{
if (values != null)
{
Values.AddRange(values);
}
}
public void RemoveAllValues()
{
_values = null;
}
public void PrintTo(TextWriter output)
{
PrintTo(output, "");
output.Flush();
}
private void PrintTo(TextWriter output, string indent)
{
output.WriteLine(indent + ToString());
indent = indent + " ";
for (int i = 0; i < Count; i++)
{
this[i].PrintTo(output, indent);
}
}
}
}

250
Parsing/ParseException.cs Normal file
View File

@@ -0,0 +1,250 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
/**
* A parse exception.
*/
public class ParseException : Exception
{
public enum ErrorType
{
/**
* The internal error type is only used to signal an error
* that is a result of a bug in the parser or tokenizer
* code.
*/
INTERNAL,
/**
* The I/O error type is used for stream I/O errors.
*/
IO,
/**
* The unexpected end of file error type is used when end
* of file is encountered instead of a valid token.
*/
UNEXPECTED_EOF,
/**
* The unexpected character error type is used when a
* character is read that isn't handled by one of the
* token patterns.
*/
UNEXPECTED_CHAR,
/**
* The unexpected token error type is used when another
* token than the expected one is encountered.
*/
UNEXPECTED_TOKEN,
/**
* The invalid token error type is used when a token
* pattern with an error message is matched. The
* additional information provided should contain the
* error message.
*/
INVALID_TOKEN,
/**
* The analysis error type is used when an error is
* encountered in the analysis. The additional information
* provided should contain the error message.
*/
ANALYSIS
}
private readonly ErrorType _type;
private readonly string _info;
private readonly ArrayList _details;
private readonly int _line;
private readonly int _column;
/// <summary>
/// Creates a new parse exception.
/// </summary>
/// <param name="type"></param>
/// <param name="info"></param>
/// <param name="line"></param>
/// <param name="column"></param>
public ParseException(ErrorType type,
string info,
int line,
int column)
: this(type, info, null, line, column)
{
}
/// <summary>
/// Creates a new parse exception. This constructor is only
/// used to supply the detailed information array, which is
/// only used for expected token errors. The list then contains
/// descriptions of the expected tokens.
/// </summary>
/// <param name="type"></param>
/// <param name="info"></param>
/// <param name="details"></param>
/// <param name="line"></param>
/// <param name="column"></param>
public ParseException(ErrorType type,
string info,
ArrayList details,
int line,
int column)
{
this._type = type;
this._info = info;
this._details = details;
this._line = line;
this._column = column;
}
public ErrorType Type => _type;
public ErrorType GetErrorType()
{
return Type;
}
public string Info => _info;
public string GetInfo()
{
return Info;
}
public ArrayList Details => new ArrayList(_details);
public ArrayList GetDetails()
{
return Details;
}
public int Line => _line;
public int GetLine()
{
return Line;
}
public int Column => _column;
public int GetColumn()
{
return _column;
}
public override string Message
{
get
{
StringBuilder buffer = new StringBuilder();
// Add error description
buffer.Append(ErrorMessage);
// Add line and column
if (_line > 0 && _column > 0)
{
buffer.Append(", on line: ");
buffer.Append(_line);
buffer.Append(" column: ");
buffer.Append(_column);
}
return buffer.ToString();
}
}
public string GetMessage()
{
return Message;
}
public string ErrorMessage
{
get
{
StringBuilder buffer = new StringBuilder();
// Add type and info
switch (_type)
{
case ErrorType.IO:
buffer.Append("I/O error: ");
buffer.Append(_info);
break;
case ErrorType.UNEXPECTED_EOF:
buffer.Append("unexpected end of file");
break;
case ErrorType.UNEXPECTED_CHAR:
buffer.Append("unexpected character '");
buffer.Append(_info);
buffer.Append("'");
break;
case ErrorType.UNEXPECTED_TOKEN:
buffer.Append("unexpected token ");
buffer.Append(_info);
if (_details != null)
{
buffer.Append(", expected ");
if (_details.Count > 1)
{
buffer.Append("one of ");
}
buffer.Append(GetMessageDetails());
}
break;
case ErrorType.INVALID_TOKEN:
buffer.Append(_info);
break;
case ErrorType.ANALYSIS:
buffer.Append(_info);
break;
default:
buffer.Append("internal error");
if (_info != null)
{
buffer.Append(": ");
buffer.Append(_info);
}
break;
}
return buffer.ToString();
}
}
public string GetErrorMessage()
{
return ErrorMessage;
}
private string GetMessageDetails()
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < _details.Count; i++)
{
if (i > 0)
{
buffer.Append(", ");
if (i + 1 == _details.Count)
{
buffer.Append("or ");
}
}
buffer.Append(_details[i]);
}
return buffer.ToString();
}
}
}

492
Parsing/Parser.cs Normal file
View File

@@ -0,0 +1,492 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
[Obsolete(" A base parser class. This class provides the standard parser interface, as well as token handling.")]
internal abstract class Parser
{
private bool _initialized;
private readonly Tokenizer _tokenizer;
private Analyzer _analyzer;
private readonly ArrayList _patterns = new ArrayList();
private readonly Hashtable _patternIds = new Hashtable();
private readonly ArrayList _tokens = new ArrayList();
private ParserLogException _errorLog = new ParserLogException();
private int _errorRecovery = -1;
/// <summary>
/// Creates a new parser.
/// </summary>
/// <param name="input"></param>
internal Parser(TextReader input) : this(input, null)
{
}
/// <summary>
/// Creates a new parser.
/// </summary>
/// <param name="input"></param>
/// <param name="analyzer"></param>
internal Parser(TextReader input, Analyzer analyzer)
{
_tokenizer = NewTokenizer(input);
this._analyzer = analyzer ?? NewAnalyzer();
}
/**
* Creates a new parser.
*
* @param tokenizer the tokenizer to use
*/
internal Parser(Tokenizer tokenizer) : this(tokenizer, null)
{
}
internal Parser(Tokenizer tokenizer, Analyzer analyzer)
{
this._tokenizer = tokenizer;
this._analyzer = analyzer ?? NewAnalyzer();
}
protected virtual Tokenizer NewTokenizer(TextReader input)
{
// TODO: This method should really be abstract, but it isn't in this
// version due to backwards compatibility requirements.
return new Tokenizer(input);
}
protected virtual Analyzer NewAnalyzer()
{
// TODO: This method should really be abstract, but it isn't in this
// version due to backwards compatibility requirements.
return new Analyzer();
}
public Tokenizer Tokenizer => _tokenizer;
public Analyzer Analyzer => _analyzer;
public Tokenizer GetTokenizer()
{
return Tokenizer;
}
public Analyzer GetAnalyzer()
{
return Analyzer;
}
internal void SetInitialized(bool initialized)
{
_initialized = initialized;
}
public virtual void AddPattern(ProductionPattern pattern)
{
if (pattern.Count <= 0)
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
pattern.Name,
"no production alternatives are present (must have at " +
"least one)");
}
if (_patternIds.ContainsKey(pattern.Id))
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
pattern.Name,
"another pattern with the same id (" + pattern.Id +
") has already been added");
}
_patterns.Add(pattern);
_patternIds.Add(pattern.Id, pattern);
SetInitialized(false);
}
public virtual void Prepare()
{
if (_patterns.Count <= 0)
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PARSER,
"no production patterns have been added");
}
for (int i = 0; i < _patterns.Count; i++)
{
CheckPattern((ProductionPattern)_patterns[i]);
}
SetInitialized(true);
}
private void CheckPattern(ProductionPattern pattern)
{
for (int i = 0; i < pattern.Count; i++)
{
CheckAlternative(pattern.Name, pattern[i]);
}
}
private void CheckAlternative(string name,
ProductionPatternAlternative alt)
{
for (int i = 0; i < alt.Count; i++)
{
CheckElement(name, alt[i]);
}
}
private void CheckElement(string name,
ProductionPatternElement elem)
{
if (elem.IsProduction() && GetPattern(elem.Id) == null)
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
name,
"an undefined production pattern id (" + elem.Id +
") is referenced");
}
}
public void Reset(TextReader input)
{
this._tokenizer.Reset(input);
this._analyzer.Reset();
}
public void Reset(TextReader input, Analyzer analyzer)
{
this._tokenizer.Reset(input);
this._analyzer = analyzer;
}
public Node Parse()
{
Node root = null;
// Initialize parser
if (!_initialized)
{
Prepare();
}
this._tokens.Clear();
this._errorLog = new ParserLogException();
this._errorRecovery = -1;
// Parse input
try
{
root = ParseStart();
}
catch (ParseException e)
{
AddError(e, true);
}
// Check for errors
if (_errorLog.Count > 0)
{
throw _errorLog;
}
return root;
}
protected abstract Node ParseStart();
protected virtual Production NewProduction(ProductionPattern pattern)
{
return _analyzer.NewProduction(pattern);
}
internal void AddError(ParseException e, bool recovery)
{
if (_errorRecovery <= 0)
{
_errorLog.AddError(e);
}
if (recovery)
{
_errorRecovery = 3;
}
}
internal ProductionPattern GetPattern(int id)
{
return (ProductionPattern)_patternIds[id];
}
internal ProductionPattern GetStartPattern()
{
if (_patterns.Count <= 0)
{
return null;
}
else
{
return (ProductionPattern)_patterns[0];
}
}
internal ICollection GetPatterns()
{
return _patterns;
}
internal void EnterNode(Node node)
{
if (!node.IsHidden() && _errorRecovery < 0)
{
try
{
_analyzer.Enter(node);
}
catch (ParseException e)
{
AddError(e, false);
}
}
}
internal Node ExitNode(Node node)
{
if (!node.IsHidden() && _errorRecovery < 0)
{
try
{
return _analyzer.Exit(node);
}
catch (ParseException e)
{
AddError(e, false);
}
}
return node;
}
internal void AddNode(Production node, Node child)
{
if (_errorRecovery >= 0)
{
// Do nothing
}
else if (node.IsHidden())
{
node.AddChild(child);
}
else if (child != null && child.IsHidden())
{
for (int i = 0; i < child.Count; i++)
{
AddNode(node, child[i]);
}
}
else
{
try
{
_analyzer.Child(node, child);
}
catch (ParseException e)
{
AddError(e, false);
}
}
}
internal Token NextToken()
{
Token token = PeekToken(0);
if (token != null)
{
_tokens.RemoveAt(0);
return token;
}
else
{
throw new ParseException(
ParseException.ErrorType.UNEXPECTED_EOF,
null,
_tokenizer.GetCurrentLine(),
_tokenizer.GetCurrentColumn());
}
}
internal Token NextToken(int id)
{
Token token = NextToken();
if (token.Id == id)
{
if (_errorRecovery > 0)
{
_errorRecovery--;
}
return token;
}
else
{
var list = new ArrayList(1) {_tokenizer.GetPatternDescription(id)};
throw new ParseException(
ParseException.ErrorType.UNEXPECTED_TOKEN,
token.ToShortString(),
list,
token.StartLine,
token.StartColumn);
}
}
internal Token PeekToken(int steps)
{
while (steps >= _tokens.Count)
{
try
{
var token = _tokenizer.Next();
if (token == null)
{
return null;
}
else
{
_tokens.Add(token);
}
}
catch (ParseException e)
{
AddError(e, true);
}
}
return (Token)_tokens[steps];
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < _patterns.Count; i++)
{
buffer.Append(ToString((ProductionPattern)_patterns[i]));
buffer.Append("\n");
}
return buffer.ToString();
}
private string ToString(ProductionPattern prod)
{
StringBuilder buffer = new StringBuilder();
StringBuilder indent = new StringBuilder();
int i;
buffer.Append(prod.Name);
buffer.Append(" (");
buffer.Append(prod.Id);
buffer.Append(") ");
for (i = 0; i < buffer.Length; i++)
{
indent.Append(" ");
}
buffer.Append("= ");
indent.Append("| ");
for (i = 0; i < prod.Count; i++)
{
if (i > 0)
{
buffer.Append(indent);
}
buffer.Append(ToString(prod[i]));
buffer.Append("\n");
}
for (i = 0; i < prod.Count; i++)
{
var set = prod[i].LookAhead;
if (set.GetMaxLength() > 1)
{
buffer.Append("Using ");
buffer.Append(set.GetMaxLength());
buffer.Append(" token look-ahead for alternative ");
buffer.Append(i + 1);
buffer.Append(": ");
buffer.Append(set.ToString(_tokenizer));
buffer.Append("\n");
}
}
return buffer.ToString();
}
private string ToString(ProductionPatternAlternative alt)
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < alt.Count; i++)
{
if (i > 0)
{
buffer.Append(" ");
}
buffer.Append(ToString(alt[i]));
}
return buffer.ToString();
}
private string ToString(ProductionPatternElement elem)
{
StringBuilder buffer = new StringBuilder();
int min = elem.MinCount;
int max = elem.MaxCount;
if (min == 0 && max == 1)
{
buffer.Append("[");
}
if (elem.IsToken())
{
buffer.Append(GetTokenDescription(elem.Id));
}
else
{
buffer.Append(GetPattern(elem.Id).Name);
}
if (min == 0 && max == 1)
{
buffer.Append("]");
}
else if (min == 0 && max == Int32.MaxValue)
{
buffer.Append("*");
}
else if (min == 1 && max == Int32.MaxValue)
{
buffer.Append("+");
}
else if (min != 1 || max != 1)
{
buffer.Append("{");
buffer.Append(min);
buffer.Append(",");
buffer.Append(max);
buffer.Append("}");
}
return buffer.ToString();
}
internal string GetTokenDescription(int token)
{
if (_tokenizer == null)
{
return "";
}
else
{
return _tokenizer.GetPatternDescription(token);
}
}
}
}

View File

@@ -0,0 +1,216 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
/**
* A parser creation exception. This exception is used for signalling
* an error in the token or production patterns, making it impossible
* to create a working parser or tokenizer.
*/
internal class ParserCreationException : Exception
{
/**
* The error type enumeration.
*/
public enum ErrorType
{
/**
* The internal error type is only used to signal an
* error that is a result of a bug in the parser or
* tokenizer code.
*/
INTERNAL,
/**
* The invalid parser error type is used when the parser
* as such is invalid. This error is typically caused by
* using a parser without any patterns.
*/
INVALID_PARSER,
/**
* The invalid token error type is used when a token
* pattern is erroneous. This error is typically caused
* by an invalid pattern type or an erroneous regular
* expression.
*/
INVALID_TOKEN,
/**
* The invalid production error type is used when a
* production pattern is erroneous. This error is
* typically caused by referencing undeclared productions,
* or violating some other production pattern constraint.
*/
INVALID_PRODUCTION,
/**
* The infinite loop error type is used when an infinite
* loop has been detected in the grammar. One of the
* productions in the loop will be reported.
*/
INFINITE_LOOP,
/**
* The inherent ambiguity error type is used when the set
* of production patterns (i.e. the grammar) contains
* ambiguities that cannot be resolved.
*/
INHERENT_AMBIGUITY
}
private readonly ErrorType _type;
private readonly string _name;
private readonly string _info;
private readonly ArrayList _details;
public ParserCreationException(ErrorType type,
String info)
: this(type, null, info)
{
}
public ParserCreationException(ErrorType type,
String name,
String info)
: this(type, name, info, null)
{
}
public ParserCreationException(ErrorType type,
String name,
String info,
ArrayList details)
{
this._type = type;
this._name = name;
this._info = info;
this._details = details;
}
public ErrorType Type => _type;
public ErrorType GetErrorType()
{
return Type;
}
public string Name => _name;
public string GetName()
{
return Name;
}
public string Info => _info;
public string GetInfo()
{
return Info;
}
public string Details
{
get
{
StringBuilder buffer = new StringBuilder();
if (_details == null)
{
return null;
}
for (int i = 0; i < _details.Count; i++)
{
if (i > 0)
{
buffer.Append(", ");
if (i + 1 == _details.Count)
{
buffer.Append("and ");
}
}
buffer.Append(_details[i]);
}
return buffer.ToString();
}
}
public string GetDetails()
{
return Details;
}
public override string Message
{
get
{
StringBuilder buffer = new StringBuilder();
switch (_type)
{
case ErrorType.INVALID_PARSER:
buffer.Append("parser is invalid, as ");
buffer.Append(_info);
break;
case ErrorType.INVALID_TOKEN:
buffer.Append("token '");
buffer.Append(_name);
buffer.Append("' is invalid, as ");
buffer.Append(_info);
break;
case ErrorType.INVALID_PRODUCTION:
buffer.Append("production '");
buffer.Append(_name);
buffer.Append("' is invalid, as ");
buffer.Append(_info);
break;
case ErrorType.INFINITE_LOOP:
buffer.Append("infinite loop found in production pattern '");
buffer.Append(_name);
buffer.Append("'");
break;
case ErrorType.INHERENT_AMBIGUITY:
buffer.Append("inherent ambiguity in production '");
buffer.Append(_name);
buffer.Append("'");
if (_info != null)
{
buffer.Append(" ");
buffer.Append(_info);
}
if (_details != null)
{
buffer.Append(" starting with ");
if (_details.Count > 1)
{
buffer.Append("tokens ");
}
else
{
buffer.Append("token ");
}
buffer.Append(Details);
}
break;
default:
buffer.Append("internal error");
break;
}
return buffer.ToString();
}
}
public string GetMessage()
{
return Message;
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
internal class ParserLogException : Exception
{
private readonly ArrayList _errors = new ArrayList();
public ParserLogException()
{
}
public override string Message
{
get
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < Count; i++)
{
if (i > 0)
{
buffer.Append("\n");
}
buffer.Append(this[i].Message);
}
return buffer.ToString();
}
}
public int Count => _errors.Count;
public int GetErrorCount()
{
return Count;
}
public ParseException this[int index] => (ParseException)_errors[index];
public ParseException GetError(int index)
{
return this[index];
}
public void AddError(ParseException e)
{
_errors.Add(e);
}
public string GetMessage()
{
return Message;
}
}
}

70
Parsing/Production.cs Normal file
View File

@@ -0,0 +1,70 @@
using System.Collections;
namespace Flee.Parsing
{
/**
* A production node. This class represents a grammar production
* (i.e. a list of child nodes) in a parse tree. The productions
* are created by a parser, that adds children a according to a
* set of production patterns (i.e. grammar rules).
*/
internal class Production : Node
{
private readonly ProductionPattern _pattern;
private readonly ArrayList _children;
public Production(ProductionPattern pattern)
{
this._pattern = pattern;
this._children = new ArrayList();
}
public override int Id => _pattern.Id;
public override string Name => _pattern.Name;
public override int Count => _children.Count;
public override Node this[int index]
{
get
{
if (index < 0 || index >= _children.Count)
{
return null;
}
else
{
return (Node)_children[index];
}
}
}
public void AddChild(Node child)
{
if (child != null)
{
child.SetParent(this);
_children.Add(child);
}
}
public ProductionPattern Pattern => _pattern;
public ProductionPattern GetPattern()
{
return Pattern;
}
internal override bool IsHidden()
{
return _pattern.Synthetic;
}
public override string ToString()
{
return _pattern.Name + '(' + _pattern.Id + ')';
}
}
}

View File

@@ -0,0 +1,213 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
/**
* A production pattern. This class represents a set of production
* alternatives that together forms a single production. A
* production pattern is identified by an integer id and a name,
* both provided upon creation. The pattern id is used for
* referencing the production pattern from production pattern
* elements.
*/
internal class ProductionPattern
{
private readonly int _id;
private readonly string _name;
private bool _synthetic;
private readonly ArrayList _alternatives;
private int _defaultAlt;
private LookAheadSet _lookAhead;
public ProductionPattern(int id, string name)
{
this._id = id;
this._name = name;
this._synthetic = false;
this._alternatives = new ArrayList();
this._defaultAlt = -1;
this._lookAhead = null;
}
public int Id => _id;
public int GetId()
{
return Id;
}
public string Name => _name;
public string GetName()
{
return Name;
}
public bool Synthetic
{
get
{
return _synthetic;
}
set
{
_synthetic = value;
}
}
public bool IsSyntetic()
{
return Synthetic;
}
public void SetSyntetic(bool synthetic)
{
Synthetic = synthetic;
}
internal LookAheadSet LookAhead
{
get
{
return _lookAhead;
}
set
{
_lookAhead = value;
}
}
internal ProductionPatternAlternative DefaultAlternative
{
get
{
if (_defaultAlt >= 0)
{
object obj = _alternatives[_defaultAlt];
return (ProductionPatternAlternative)obj;
}
else
{
return null;
}
}
set
{
_defaultAlt = 0;
for (int i = 0; i < _alternatives.Count; i++)
{
if (_alternatives[i] == value)
{
_defaultAlt = i;
}
}
}
}
public int Count => _alternatives.Count;
public int GetAlternativeCount()
{
return Count;
}
public ProductionPatternAlternative this[int index] => (ProductionPatternAlternative)_alternatives[index];
public ProductionPatternAlternative GetAlternative(int pos)
{
return this[pos];
}
public bool IsLeftRecursive()
{
ProductionPatternAlternative alt;
for (int i = 0; i < _alternatives.Count; i++)
{
alt = (ProductionPatternAlternative)_alternatives[i];
if (alt.IsLeftRecursive())
{
return true;
}
}
return false;
}
public bool IsRightRecursive()
{
ProductionPatternAlternative alt;
for (int i = 0; i < _alternatives.Count; i++)
{
alt = (ProductionPatternAlternative)_alternatives[i];
if (alt.IsRightRecursive())
{
return true;
}
}
return false;
}
public bool IsMatchingEmpty()
{
ProductionPatternAlternative alt;
for (int i = 0; i < _alternatives.Count; i++)
{
alt = (ProductionPatternAlternative)_alternatives[i];
if (alt.IsMatchingEmpty())
{
return true;
}
}
return false;
}
public void AddAlternative(ProductionPatternAlternative alt)
{
if (_alternatives.Contains(alt))
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
_name,
"two identical alternatives exist");
}
alt.SetPattern(this);
_alternatives.Add(alt);
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
StringBuilder indent = new StringBuilder();
int i;
buffer.Append(_name);
buffer.Append("(");
buffer.Append(_id);
buffer.Append(") ");
for (i = 0; i < buffer.Length; i++)
{
indent.Append(" ");
}
for (i = 0; i < _alternatives.Count; i++)
{
if (i == 0)
{
buffer.Append("= ");
}
else
{
buffer.Append("\n");
buffer.Append(indent);
buffer.Append("| ");
}
buffer.Append(_alternatives[i]);
}
return buffer.ToString();
}
}
}

View File

@@ -0,0 +1,211 @@
using System.Collections;
using System.Text;
namespace Flee.Parsing
{
/**
* A production pattern alternative. This class represents a list of
* production pattern elements. In order to provide productions that
* cannot be represented with the element occurance counters, multiple
* alternatives must be created and added to the same production
* pattern. A production pattern alternative is always contained
* within a production pattern.
*/
internal class ProductionPatternAlternative
{
private ProductionPattern _pattern;
private readonly ArrayList _elements = new ArrayList();
private LookAheadSet _lookAhead = null;
public ProductionPatternAlternative()
{
}
public ProductionPattern Pattern => _pattern;
public ProductionPattern GetPattern()
{
return Pattern;
}
internal LookAheadSet LookAhead
{
get
{
return _lookAhead;
}
set
{
_lookAhead = value;
}
}
public int Count => _elements.Count;
public int GetElementCount()
{
return Count;
}
public ProductionPatternElement this[int index] => (ProductionPatternElement)_elements[index];
public ProductionPatternElement GetElement(int pos)
{
return this[pos];
}
public bool IsLeftRecursive()
{
for (int i = 0; i < _elements.Count; i++)
{
var elem = (ProductionPatternElement)_elements[i];
if (elem.Id == _pattern.Id)
{
return true;
}
else if (elem.MinCount > 0)
{
break;
}
}
return false;
}
public bool IsRightRecursive()
{
for (int i = _elements.Count - 1; i >= 0; i--)
{
var elem = (ProductionPatternElement)_elements[i];
if (elem.Id == _pattern.Id)
{
return true;
}
else if (elem.MinCount > 0)
{
break;
}
}
return false;
}
public bool IsMatchingEmpty()
{
return GetMinElementCount() == 0;
}
internal void SetPattern(ProductionPattern pattern)
{
this._pattern = pattern;
}
public int GetMinElementCount()
{
int min = 0;
for (int i = 0; i < _elements.Count; i++)
{
var elem = (ProductionPatternElement)_elements[i];
min += elem.MinCount;
}
return min;
}
public int GetMaxElementCount()
{
int max = 0;
for (int i = 0; i < _elements.Count; i++)
{
var elem = (ProductionPatternElement)_elements[i];
if (elem.MaxCount >= Int32.MaxValue)
{
return Int32.MaxValue;
}
else
{
max += elem.MaxCount;
}
}
return max;
}
public void AddToken(int id, int min, int max)
{
AddElement(new ProductionPatternElement(true, id, min, max));
}
public void AddProduction(int id, int min, int max)
{
AddElement(new ProductionPatternElement(false, id, min, max));
}
public void AddElement(ProductionPatternElement elem)
{
_elements.Add(elem);
}
public void AddElement(ProductionPatternElement elem,
int min,
int max)
{
if (elem.IsToken())
{
AddToken(elem.Id, min, max);
}
else
{
AddProduction(elem.Id, min, max);
}
}
public override bool Equals(object obj)
{
if (obj is ProductionPatternAlternative)
{
return Equals((ProductionPatternAlternative)obj);
}
else
{
return false;
}
}
public bool Equals(ProductionPatternAlternative alt)
{
if (_elements.Count != alt._elements.Count)
{
return false;
}
for (int i = 0; i < _elements.Count; i++)
{
if (!_elements[i].Equals(alt._elements[i]))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
return _elements.Count.GetHashCode();
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < _elements.Count; i++)
{
if (i > 0)
{
buffer.Append(" ");
}
buffer.Append(_elements[i]);
}
return buffer.ToString();
}
}
}

View File

@@ -0,0 +1,138 @@
using System.Text;
namespace Flee.Parsing
{
/**
* A production pattern element. This class represents a reference to
* either a token or a production. Each element also contains minimum
* and maximum occurence counters, controlling the number of
* repetitions allowed. A production pattern element is always
* contained within a production pattern rule.
*/
internal class ProductionPatternElement
{
private readonly bool _token;
private readonly int _id;
private readonly int _min;
private readonly int _max;
private LookAheadSet _lookAhead;
public ProductionPatternElement(bool isToken,
int id,
int min,
int max)
{
this._token = isToken;
this._id = id;
if (min < 0)
{
min = 0;
}
this._min = min;
if (max <= 0)
{
max = Int32.MaxValue;
}
else if (max < min)
{
max = min;
}
this._max = max;
this._lookAhead = null;
}
public int Id => _id;
public int GetId()
{
return Id;
}
public int MinCount => _min;
public int GetMinCount()
{
return MinCount;
}
public int MaxCount => _max;
public int GetMaxCount()
{
return MaxCount;
}
internal LookAheadSet LookAhead
{
get
{
return _lookAhead;
}
set
{
_lookAhead = value;
}
}
public bool IsToken()
{
return _token;
}
public bool IsProduction()
{
return !_token;
}
public bool IsMatch(Token token)
{
return IsToken() && token != null && token.Id == _id;
}
public override bool Equals(object obj)
{
if (obj is ProductionPatternElement)
{
var elem = (ProductionPatternElement)obj;
return this._token == elem._token
&& this._id == elem._id
&& this._min == elem._min
&& this._max == elem._max;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return this._id * 37;
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
buffer.Append(_id);
if (_token)
{
buffer.Append("(Token)");
}
else
{
buffer.Append("(Production)");
}
if (_min != 1 || _max != 1)
{
buffer.Append("{");
buffer.Append(_min);
buffer.Append(",");
buffer.Append(_max);
buffer.Append("}");
}
return buffer.ToString();
}
}
}

180
Parsing/ReaderBuffer.cs Normal file
View File

@@ -0,0 +1,180 @@
namespace Flee.Parsing
{
/**
* A character buffer that automatically reads from an input source
* stream when needed. This class keeps track of the current position
* in the buffer and its line and column number in the original input
* source. It allows unlimited look-ahead of characters in the input,
* reading and buffering the required data internally. As the
* position is advanced, the buffer content prior to the current
* position is subject to removal to make space for reading new
* content. A few characters before the current position are always
* kept to enable boundary condition checks.
*/
internal class ReaderBuffer
{
public const int BlockSize = 1024;
private char[] _buffer = new char[BlockSize * 4];
private int _pos = 0;
private int _length = 0;
private TextReader _input;
private int _line = 1;
private int _column = 1;
public ReaderBuffer(TextReader input)
{
this._input = input;
}
public void Dispose()
{
_buffer = null;
_pos = 0;
_length = 0;
if (_input != null)
{
try
{
_input.Close();
}
catch (Exception)
{
// Do nothing
}
_input = null;
}
}
public int Position => _pos;
public int LineNumber => _line;
public int ColumnNumber => _column;
public int Length => _length;
public string Substring(int index, int length)
{
return new string(_buffer, index, length);
}
public override string ToString()
{
return new string(_buffer, 0, _length);
}
public int Peek(int offset)
{
int index = _pos + offset;
// Avoid most calls to EnsureBuffered(), since we are in a
// performance hotspot here. This check is not exhaustive,
// but only present here to speed things up.
if (index >= _length)
{
EnsureBuffered(offset + 1);
index = _pos + offset;
}
return (index >= _length) ? -1 : _buffer[index];
}
public string Read(int offset)
{
EnsureBuffered(offset + 1);
if (_pos >= _length)
{
return null;
}
else
{
var count = _length - _pos;
if (count > offset)
{
count = offset;
}
UpdateLineColumnNumbers(count);
var result = new string(_buffer, _pos, count);
_pos += count;
if (_input == null && _pos >= _length)
{
Dispose();
}
return result;
}
}
private void UpdateLineColumnNumbers(int offset)
{
for (int i = 0; i < offset; i++)
{
if (_buffer[_pos + i] == '\n')
{
_line++;
_column = 1;
}
else
{
_column++;
}
}
}
private void EnsureBuffered(int offset)
{
// Check for end of stream or already read characters
if (_input == null || _pos + offset < _length)
{
return;
}
// Remove (almost all) old characters from buffer
if (_pos > BlockSize)
{
_length -= (_pos - 16);
Array.Copy(_buffer, _pos - 16, _buffer, 0, _length);
_pos = 16;
}
// Calculate number of characters to read
var size = _pos + offset - _length + 1;
if (size % BlockSize != 0)
{
size = (1 + size / BlockSize) * BlockSize;
}
EnsureCapacity(_length + size);
// Read characters
try
{
while (_input != null && size > 0)
{
var readSize = _input.Read(_buffer, _length, size);
if (readSize > 0)
{
_length += readSize;
size -= readSize;
}
else
{
_input.Close();
_input = null;
}
}
}
catch (IOException e)
{
_input = null;
throw e;
}
}
private void EnsureCapacity(int size)
{
if (_buffer.Length >= size)
{
return;
}
if (size % BlockSize != 0)
{
size = (1 + size / BlockSize) * BlockSize;
}
Array.Resize(ref _buffer, size);
}
}
}

View File

@@ -0,0 +1,648 @@
using System.Collections;
namespace Flee.Parsing
{
/**
* A recursive descent parser. This parser handles LL(n) grammars,
* selecting the appropriate pattern to parse based on the next few
* tokens. The parser is more efficient the fewer look-ahead tokens
* that is has to consider.
*/
internal class RecursiveDescentParser : Parser
{
private int _stackdepth = 0;
public RecursiveDescentParser(TextReader input) : base(input)
{
}
public RecursiveDescentParser(TextReader input, Analyzer analyzer)
: base(input, analyzer)
{
}
public RecursiveDescentParser(Tokenizer tokenizer)
: base(tokenizer)
{
}
public RecursiveDescentParser(Tokenizer tokenizer,
Analyzer analyzer)
: base(tokenizer, analyzer)
{
}
public override void AddPattern(ProductionPattern pattern)
{
// Check for empty matches
if (pattern.IsMatchingEmpty())
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
pattern.Name,
"zero elements can be matched (minimum is one)");
}
// Check for left-recusive patterns
if (pattern.IsLeftRecursive())
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
pattern.Name,
"left recursive patterns are not allowed");
}
// Add pattern
base.AddPattern(pattern);
}
public override void Prepare()
{
// Performs production pattern checks
base.Prepare();
SetInitialized(false);
// Calculate production look-ahead sets
var e = GetPatterns().GetEnumerator();
while (e.MoveNext())
{
CalculateLookAhead((ProductionPattern)e.Current);
}
// Set initialized flag
SetInitialized(true);
}
protected override Node ParseStart()
{
_stackdepth = 0;
var node = ParsePattern(GetStartPattern());
var token = PeekToken(0);
if (token != null)
{
var list = new ArrayList(1) { "<EOF>" };
throw new ParseException(
ParseException.ErrorType.UNEXPECTED_TOKEN,
token.ToShortString(),
list,
token.StartLine,
token.StartColumn);
}
return node;
}
private Node ParsePattern(ProductionPattern pattern)
{
_stackdepth++;
if (_stackdepth > 200)
{
throw new System.StackOverflowException();
}
try
{
var defaultAlt = pattern.DefaultAlternative;
for (int i = 0; i < pattern.Count; i++)
{
var alt = pattern[i];
if (defaultAlt != alt && IsNext(alt))
{
return ParseAlternative(alt);
}
}
if (defaultAlt == null || !IsNext(defaultAlt))
{
ThrowParseException(FindUnion(pattern));
}
return ParseAlternative(defaultAlt);
}
finally
{
_stackdepth--;
}
}
private Node ParseAlternative(ProductionPatternAlternative alt)
{
var node = NewProduction(alt.Pattern);
EnterNode(node);
for (int i = 0; i < alt.Count; i++)
{
try
{
ParseElement(node, alt[i]);
}
catch (ParseException e)
{
AddError(e, true);
NextToken();
i--;
}
}
return ExitNode(node);
}
private void ParseElement(Production node,
ProductionPatternElement elem)
{
for (int i = 0; i < elem.MaxCount; i++)
{
if (i < elem.MinCount || IsNext(elem))
{
Node child;
if (elem.IsToken())
{
child = NextToken(elem.Id);
EnterNode(child);
AddNode(node, ExitNode(child));
}
else
{
child = ParsePattern(GetPattern(elem.Id));
AddNode(node, child);
}
}
else
{
break;
}
}
}
private bool IsNext(ProductionPattern pattern)
{
LookAheadSet set = pattern.LookAhead;
if (set == null)
{
return false;
}
else
{
return set.IsNext(this);
}
}
private bool IsNext(ProductionPatternAlternative alt)
{
LookAheadSet set = alt.LookAhead;
if (set == null)
{
return false;
}
else
{
return set.IsNext(this);
}
}
private bool IsNext(ProductionPatternElement elem)
{
LookAheadSet set = elem.LookAhead;
if (set != null)
{
return set.IsNext(this);
}
else if (elem.IsToken())
{
return elem.IsMatch(PeekToken(0));
}
else
{
return IsNext(GetPattern(elem.Id));
}
}
private void CalculateLookAhead(ProductionPattern pattern)
{
ProductionPatternAlternative alt;
LookAheadSet previous = new LookAheadSet(0);
int length = 1;
int i;
CallStack stack = new CallStack();
// Calculate simple look-ahead
stack.Push(pattern.Name, 1);
var result = new LookAheadSet(1);
var alternatives = new LookAheadSet[pattern.Count];
for (i = 0; i < pattern.Count; i++)
{
alt = pattern[i];
alternatives[i] = FindLookAhead(alt, 1, 0, stack, null);
alt.LookAhead = alternatives[i];
result.AddAll(alternatives[i]);
}
if (pattern.LookAhead == null)
{
pattern.LookAhead = result;
}
var conflicts = FindConflicts(pattern, 1);
// Resolve conflicts
while (conflicts.Size() > 0)
{
length++;
stack.Clear();
stack.Push(pattern.Name, length);
conflicts.AddAll(previous);
for (i = 0; i < pattern.Count; i++)
{
alt = pattern[i];
if (alternatives[i].Intersects(conflicts))
{
alternatives[i] = FindLookAhead(alt,
length,
0,
stack,
conflicts);
alt.LookAhead = alternatives[i];
}
if (alternatives[i].Intersects(conflicts))
{
if (pattern.DefaultAlternative == null)
{
pattern.DefaultAlternative = alt;
}
else if (pattern.DefaultAlternative != alt)
{
result = alternatives[i].CreateIntersection(conflicts);
ThrowAmbiguityException(pattern.Name,
null,
result);
}
}
}
previous = conflicts;
conflicts = FindConflicts(pattern, length);
}
// Resolve conflicts inside rules
for (i = 0; i < pattern.Count; i++)
{
CalculateLookAhead(pattern[i], 0);
}
}
private void CalculateLookAhead(ProductionPatternAlternative alt,
int pos)
{
LookAheadSet previous = new LookAheadSet(0);
int length = 1;
// Check trivial cases
if (pos >= alt.Count)
{
return;
}
// Check for non-optional element
var pattern = alt.Pattern;
var elem = alt[pos];
if (elem.MinCount == elem.MaxCount)
{
CalculateLookAhead(alt, pos + 1);
return;
}
// Calculate simple look-aheads
var first = FindLookAhead(elem, 1, new CallStack(), null);
var follow = FindLookAhead(alt, 1, pos + 1, new CallStack(), null);
// Resolve conflicts
var location = "at position " + (pos + 1);
var conflicts = FindConflicts(pattern.Name,
location,
first,
follow);
while (conflicts.Size() > 0)
{
length++;
conflicts.AddAll(previous);
first = FindLookAhead(elem,
length,
new CallStack(),
conflicts);
follow = FindLookAhead(alt,
length,
pos + 1,
new CallStack(),
conflicts);
first = first.CreateCombination(follow);
elem.LookAhead = first;
if (first.Intersects(conflicts))
{
first = first.CreateIntersection(conflicts);
ThrowAmbiguityException(pattern.Name, location, first);
}
previous = conflicts;
conflicts = FindConflicts(pattern.Name,
location,
first,
follow);
}
// Check remaining elements
CalculateLookAhead(alt, pos + 1);
}
private LookAheadSet FindLookAhead(ProductionPattern pattern,
int length,
CallStack stack,
LookAheadSet filter)
{
// Check for infinite loop
if (stack.Contains(pattern.Name, length))
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INFINITE_LOOP,
pattern.Name,
(String)null);
}
// Find pattern look-ahead
stack.Push(pattern.Name, length);
var result = new LookAheadSet(length);
for (int i = 0; i < pattern.Count; i++)
{
var temp = FindLookAhead(pattern[i],
length,
0,
stack,
filter);
result.AddAll(temp);
}
stack.Pop();
return result;
}
private LookAheadSet FindLookAhead(ProductionPatternAlternative alt,
int length,
int pos,
CallStack stack,
LookAheadSet filter)
{
LookAheadSet follow;
// Check trivial cases
if (length <= 0 || pos >= alt.Count)
{
return new LookAheadSet(0);
}
// Find look-ahead for this element
var first = FindLookAhead(alt[pos], length, stack, filter);
if (alt[pos].MinCount == 0)
{
first.AddEmpty();
}
// Find remaining look-ahead
if (filter == null)
{
length -= first.GetMinLength();
if (length > 0)
{
follow = FindLookAhead(alt, length, pos + 1, stack, null);
first = first.CreateCombination(follow);
}
}
else if (filter.IsOverlap(first))
{
var overlaps = first.CreateOverlaps(filter);
length -= overlaps.GetMinLength();
filter = filter.CreateFilter(overlaps);
follow = FindLookAhead(alt, length, pos + 1, stack, filter);
first.RemoveAll(overlaps);
first.AddAll(overlaps.CreateCombination(follow));
}
return first;
}
private LookAheadSet FindLookAhead(ProductionPatternElement elem,
int length,
CallStack stack,
LookAheadSet filter)
{
// Find initial element look-ahead
var first = FindLookAhead(elem, length, 0, stack, filter);
var result = new LookAheadSet(length);
result.AddAll(first);
if (filter == null || !filter.IsOverlap(result))
{
return result;
}
// Handle element repetitions
if (elem.MaxCount == Int32.MaxValue)
{
first = first.CreateRepetitive();
}
var max = elem.MaxCount;
if (length < max)
{
max = length;
}
for (int i = 1; i < max; i++)
{
first = first.CreateOverlaps(filter);
if (first.Size() <= 0 || first.GetMinLength() >= length)
{
break;
}
var follow = FindLookAhead(elem,
length,
0,
stack,
filter.CreateFilter(first));
first = first.CreateCombination(follow);
result.AddAll(first);
}
return result;
}
private LookAheadSet FindLookAhead(ProductionPatternElement elem,
int length,
int dummy,
CallStack stack,
LookAheadSet filter)
{
LookAheadSet result;
if (elem.IsToken())
{
result = new LookAheadSet(length);
result.Add(elem.Id);
}
else
{
var pattern = GetPattern(elem.Id);
result = FindLookAhead(pattern, length, stack, filter);
if (stack.Contains(pattern.Name))
{
result = result.CreateRepetitive();
}
}
return result;
}
private LookAheadSet FindConflicts(ProductionPattern pattern,
int maxLength)
{
LookAheadSet result = new LookAheadSet(maxLength);
for (int i = 0; i < pattern.Count; i++)
{
var set1 = pattern[i].LookAhead;
for (int j = 0; j < i; j++)
{
var set2 = pattern[j].LookAhead;
result.AddAll(set1.CreateIntersection(set2));
}
}
if (result.IsRepetitive())
{
ThrowAmbiguityException(pattern.Name, null, result);
}
return result;
}
private LookAheadSet FindConflicts(string pattern,
string location,
LookAheadSet set1,
LookAheadSet set2)
{
var result = set1.CreateIntersection(set2);
if (result.IsRepetitive())
{
ThrowAmbiguityException(pattern, location, result);
}
return result;
}
private LookAheadSet FindUnion(ProductionPattern pattern)
{
LookAheadSet result;
int length = 0;
int i;
for (i = 0; i < pattern.Count; i++)
{
result = pattern[i].LookAhead;
if (result.GetMaxLength() > length)
{
length = result.GetMaxLength();
}
}
result = new LookAheadSet(length);
for (i = 0; i < pattern.Count; i++)
{
result.AddAll(pattern[i].LookAhead);
}
return result;
}
private void ThrowParseException(LookAheadSet set)
{
ArrayList list = new ArrayList();
// Read tokens until mismatch
while (set.IsNext(this, 1))
{
set = set.CreateNextSet(NextToken().Id);
}
// Find next token descriptions
var initials = set.GetInitialTokens();
for (int i = 0; i < initials.Length; i++)
{
list.Add(GetTokenDescription(initials[i]));
}
// Create exception
var token = NextToken();
throw new ParseException(ParseException.ErrorType.UNEXPECTED_TOKEN,
token.ToShortString(),
list,
token.StartLine,
token.StartColumn);
}
private void ThrowAmbiguityException(string pattern,
string location,
LookAheadSet set)
{
ArrayList list = new ArrayList();
// Find next token descriptions
var initials = set.GetInitialTokens();
for (int i = 0; i < initials.Length; i++)
{
list.Add(GetTokenDescription(initials[i]));
}
// Create exception
throw new ParserCreationException(
ParserCreationException.ErrorType.INHERENT_AMBIGUITY,
pattern,
location,
list);
}
private class CallStack
{
private readonly ArrayList _nameStack = new ArrayList();
private readonly ArrayList _valueStack = new ArrayList();
public bool Contains(string name)
{
return _nameStack.Contains(name);
}
public bool Contains(string name, int value)
{
for (int i = 0; i < _nameStack.Count; i++)
{
if (_nameStack[i].Equals(name)
&& _valueStack[i].Equals(value))
{
return true;
}
}
return false;
}
public void Clear()
{
_nameStack.Clear();
_valueStack.Clear();
}
public void Push(string name, int value)
{
_nameStack.Add(name);
_valueStack.Add(value);
}
public void Pop()
{
if (_nameStack.Count > 0)
{
_nameStack.RemoveAt(_nameStack.Count - 1);
_valueStack.RemoveAt(_valueStack.Count - 1);
}
}
}
}
}

505
Parsing/RegExp.cs Normal file
View File

@@ -0,0 +1,505 @@
using System.Collections;
using System.Globalization;
using System.Text;
namespace Flee.Parsing
{
/**
* A regular expression. This class creates and holds an internal
* data structure representing a regular expression. It also
* allows creating matchers. This class is thread-safe. Multiple
* matchers may operate simultanously on the same regular
* expression.
*/
internal class RegExp
{
private readonly Element _element;
private readonly string _pattern;
private readonly bool _ignoreCase;
private int _pos;
public RegExp(string pattern)
: this(pattern, false)
{
}
public RegExp(string pattern, bool ignoreCase)
{
this._pattern = pattern;
this._ignoreCase = ignoreCase;
this._pos = 0;
this._element = ParseExpr();
if (_pos < pattern.Length)
{
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos,
pattern);
}
}
public Matcher Matcher(string str)
{
return Matcher(new ReaderBuffer(new StringReader(str)));
}
public Matcher Matcher(ReaderBuffer buffer)
{
return new Matcher((Element)_element.Clone(), buffer, _ignoreCase);
}
public override string ToString()
{
var str = new StringWriter();
str.WriteLine("Regular Expression");
str.WriteLine(" Pattern: " + _pattern);
str.Write(" Flags:");
if (_ignoreCase)
{
str.Write(" caseignore");
}
str.WriteLine();
str.WriteLine(" Compiled:");
_element.PrintTo(str, " ");
return str.ToString();
}
private Element ParseExpr()
{
var first = ParseTerm();
if (PeekChar(0) != '|')
{
return first;
}
else
{
ReadChar('|');
var second = ParseExpr();
return new AlternativeElement(first, second);
}
}
private Element ParseTerm()
{
ArrayList list = new ArrayList();
list.Add(ParseFact());
while (true)
{
switch (PeekChar(0))
{
case -1:
case ')':
case ']':
case '{':
case '}':
case '?':
case '+':
case '|':
return CombineElements(list);
default:
list.Add(ParseFact());
break;
}
}
}
private Element ParseFact()
{
var elem = ParseAtom();
switch (PeekChar(0))
{
case '?':
case '*':
case '+':
case '{':
return ParseAtomModifier(elem);
default:
return elem;
}
}
private Element ParseAtom()
{
Element elem;
switch (PeekChar(0))
{
case '.':
ReadChar('.');
return CharacterSetElement.Dot;
case '(':
ReadChar('(');
elem = ParseExpr();
ReadChar(')');
return elem;
case '[':
ReadChar('[');
elem = ParseCharSet();
ReadChar(']');
return elem;
case -1:
case ')':
case ']':
case '{':
case '}':
case '?':
case '*':
case '+':
case '|':
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos,
_pattern);
default:
return ParseChar();
}
}
private Element ParseAtomModifier(Element elem)
{
int min = 0;
int max = -1;
RepeatElement.RepeatType type;
int firstPos;
// Read min and max
type = RepeatElement.RepeatType.GREEDY;
switch (ReadChar())
{
case '?':
min = 0;
max = 1;
break;
case '*':
min = 0;
max = -1;
break;
case '+':
min = 1;
max = -1;
break;
case '{':
firstPos = _pos - 1;
min = ReadNumber();
max = min;
if (PeekChar(0) == ',')
{
ReadChar(',');
max = -1;
if (PeekChar(0) != '}')
{
max = ReadNumber();
}
}
ReadChar('}');
if (max == 0 || (max > 0 && min > max))
{
throw new RegExpException(
RegExpException.ErrorType.INVALID_REPEAT_COUNT,
firstPos,
_pattern);
}
break;
default:
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos - 1,
_pattern);
}
// Read operator mode
if (PeekChar(0) == '?')
{
ReadChar('?');
type = RepeatElement.RepeatType.RELUCTANT;
}
else if (PeekChar(0) == '+')
{
ReadChar('+');
type = RepeatElement.RepeatType.POSSESSIVE;
}
return new RepeatElement(elem, min, max, type);
}
private Element ParseCharSet()
{
CharacterSetElement charset;
bool repeat = true;
if (PeekChar(0) == '^')
{
ReadChar('^');
charset = new CharacterSetElement(true);
}
else
{
charset = new CharacterSetElement(false);
}
while (PeekChar(0) > 0 && repeat)
{
var start = (char)PeekChar(0);
switch (start)
{
case ']':
repeat = false;
break;
case '\\':
var elem = ParseEscapeChar();
if (elem is StringElement)
{
charset.AddCharacters((StringElement)elem);
}
else
{
charset.AddCharacterSet((CharacterSetElement)elem);
}
break;
default:
ReadChar(start);
if (PeekChar(0) == '-'
&& PeekChar(1) > 0
&& PeekChar(1) != ']')
{
ReadChar('-');
var end = ReadChar();
charset.AddRange(FixChar(start), FixChar(end));
}
else
{
charset.AddCharacter(FixChar(start));
}
break;
}
}
return charset;
}
private Element ParseChar()
{
switch (PeekChar(0))
{
case '\\':
return ParseEscapeChar();
case '^':
case '$':
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_SPECIAL_CHARACTER,
_pos,
_pattern);
default:
return new StringElement(FixChar(ReadChar()));
}
}
private Element ParseEscapeChar()
{
char c;
string str;
int value;
ReadChar('\\');
c = ReadChar();
switch (c)
{
case '0':
c = ReadChar();
if (c < '0' || c > '3')
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - 3,
_pattern);
}
value = c - '0';
c = (char)PeekChar(0);
if ('0' <= c && c <= '7')
{
value *= 8;
value += ReadChar() - '0';
c = (char)PeekChar(0);
if ('0' <= c && c <= '7')
{
value *= 8;
value += ReadChar() - '0';
}
}
return new StringElement(FixChar((char)value));
case 'x':
str = ReadChar().ToString() +
ReadChar().ToString();
try
{
value = Int32.Parse(str,
NumberStyles.AllowHexSpecifier);
return new StringElement(FixChar((char)value));
}
catch (FormatException)
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - str.Length - 2,
_pattern);
}
case 'u':
str = ReadChar().ToString() +
ReadChar().ToString() +
ReadChar().ToString() +
ReadChar().ToString();
try
{
value = Int32.Parse(str,
NumberStyles.AllowHexSpecifier);
return new StringElement(FixChar((char)value));
}
catch (FormatException)
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - str.Length - 2,
_pattern);
}
case 't':
return new StringElement('\t');
case 'n':
return new StringElement('\n');
case 'r':
return new StringElement('\r');
case 'f':
return new StringElement('\f');
case 'a':
return new StringElement('\u0007');
case 'e':
return new StringElement('\u001B');
case 'd':
return CharacterSetElement.Digit;
case 'D':
return CharacterSetElement.NonDigit;
case 's':
return CharacterSetElement.Whitespace;
case 'S':
return CharacterSetElement.NonWhitespace;
case 'w':
return CharacterSetElement.Word;
case 'W':
return CharacterSetElement.NonWord;
default:
if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - 2,
_pattern);
}
return new StringElement(FixChar(c));
}
}
private char FixChar(char c)
{
return _ignoreCase ? Char.ToLower(c) : c;
}
private int ReadNumber()
{
StringBuilder buf = new StringBuilder();
int c;
c = PeekChar(0);
while ('0' <= c && c <= '9')
{
buf.Append(ReadChar());
c = PeekChar(0);
}
if (buf.Length <= 0)
{
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos,
_pattern);
}
return Int32.Parse(buf.ToString());
}
private char ReadChar()
{
int c = PeekChar(0);
if (c < 0)
{
throw new RegExpException(
RegExpException.ErrorType.UNTERMINATED_PATTERN,
_pos,
_pattern);
}
else
{
_pos++;
return (char)c;
}
}
private char ReadChar(char c)
{
if (c != ReadChar())
{
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos - 1,
_pattern);
}
return c;
}
private int PeekChar(int count)
{
if (_pos + count < _pattern.Length)
{
return _pattern[_pos + count];
}
else
{
return -1;
}
}
private Element CombineElements(ArrayList list)
{
Element elem;
int i;
// Concatenate string elements
var prev = (Element)list[0];
for (i = 1; i < list.Count; i++)
{
elem = (Element)list[i];
if (prev is StringElement
&& elem is StringElement)
{
var str = ((StringElement)prev).GetString() +
((StringElement)elem).GetString();
elem = new StringElement(str);
list.RemoveAt(i);
list[i - 1] = elem;
i--;
}
prev = elem;
}
// Combine all remaining elements
elem = (Element)list[list.Count - 1];
for (i = list.Count - 2; i >= 0; i--)
{
prev = (Element)list[i];
elem = new CombineElement(prev, elem);
}
return elem;
}
}
}

113
Parsing/RegExpException.cs Normal file
View File

@@ -0,0 +1,113 @@
using System.Text;
namespace Flee.Parsing
{
/**
* A regular expression exception. This exception is thrown if a
* regular expression couldn't be processed (or "compiled")
* properly.
*/
internal class RegExpException : Exception
{
public enum ErrorType
{
/**
* The unexpected character error constant. This error is
* used when a character was read that didn't match the
* allowed set of characters at the given position.
*/
UNEXPECTED_CHARACTER,
/**
* The unterminated pattern error constant. This error is
* used when more characters were expected in the pattern.
*/
UNTERMINATED_PATTERN,
/**
* The unsupported special character error constant. This
* error is used when special regular expression
* characters are used in the pattern, but not supported
* in this implementation.
*/
UNSUPPORTED_SPECIAL_CHARACTER,
/**
* The unsupported escape character error constant. This
* error is used when an escape character construct is
* used in the pattern, but not supported in this
* implementation.
*/
UNSUPPORTED_ESCAPE_CHARACTER,
/**
* The invalid repeat count error constant. This error is
* used when a repetition count of zero is specified, or
* when the minimum exceeds the maximum.
*/
INVALID_REPEAT_COUNT
}
private readonly ErrorType _type;
private readonly int _position;
private readonly string _pattern;
public RegExpException(ErrorType type, int pos, string pattern)
{
this._type = type;
this._position = pos;
this._pattern = pattern;
}
public override string Message => GetMessage();
public string GetMessage()
{
StringBuilder buffer = new StringBuilder();
// Append error type name
switch (_type)
{
case ErrorType.UNEXPECTED_CHARACTER:
buffer.Append("unexpected character");
break;
case ErrorType.UNTERMINATED_PATTERN:
buffer.Append("unterminated pattern");
break;
case ErrorType.UNSUPPORTED_SPECIAL_CHARACTER:
buffer.Append("unsupported character");
break;
case ErrorType.UNSUPPORTED_ESCAPE_CHARACTER:
buffer.Append("unsupported escape character");
break;
case ErrorType.INVALID_REPEAT_COUNT:
buffer.Append("invalid repeat count");
break;
default:
buffer.Append("internal error");
break;
}
// Append erroneous character
buffer.Append(": ");
if (_position < _pattern.Length)
{
buffer.Append('\'');
buffer.Append(_pattern.Substring(_position));
buffer.Append('\'');
}
else
{
buffer.Append("<end of pattern>");
}
// Append position
buffer.Append(" at position ");
buffer.Append(_position);
return buffer.ToString();
}
}
}

239
Parsing/RepeatElement.cs Normal file
View File

@@ -0,0 +1,239 @@
using System.Collections;
namespace Flee.Parsing
{
/**
* A regular expression element repeater. The element repeats the
* matches from a specified element, attempting to reach the
* maximum repetition count.
*/
internal class RepeatElement : Element
{
public enum RepeatType
{
GREEDY = 1,
RELUCTANT = 2,
POSSESSIVE = 3
}
private readonly Element _elem;
private readonly int _min;
private readonly int _max;
private readonly RepeatType _type;
private int _matchStart;
private BitArray _matches;
public RepeatElement(Element elem,
int min,
int max,
RepeatType type)
{
this._elem = elem;
this._min = min;
if (max <= 0)
{
this._max = Int32.MaxValue;
}
else
{
this._max = max;
}
this._type = type;
this._matchStart = -1;
this._matches = null;
}
public override object Clone()
{
return new RepeatElement((Element)_elem.Clone(),
_min,
_max,
_type);
}
public override int Match(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
if (skip == 0)
{
_matchStart = -1;
_matches = null;
}
switch (_type)
{
case RepeatType.GREEDY:
return MatchGreedy(m, buffer, start, skip);
case RepeatType.RELUCTANT:
return MatchReluctant(m, buffer, start, skip);
case RepeatType.POSSESSIVE:
if (skip == 0)
{
return MatchPossessive(m, buffer, start, 0);
}
break;
}
return -1;
}
private int MatchGreedy(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
// Check for simple case
if (skip == 0)
{
return MatchPossessive(m, buffer, start, 0);
}
// Find all matches
if (_matchStart != start)
{
_matchStart = start;
_matches = new BitArray(10);
FindMatches(m, buffer, start, 0, 0, 0);
}
// Find first non-skipped match
for (int i = _matches.Count - 1; i >= 0; i--)
{
if (_matches[i])
{
if (skip == 0)
{
return i;
}
skip--;
}
}
return -1;
}
private int MatchReluctant(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
if (_matchStart != start)
{
_matchStart = start;
_matches = new BitArray(10);
FindMatches(m, buffer, start, 0, 0, 0);
}
// Find first non-skipped match
for (int i = 0; i < _matches.Count; i++)
{
if (_matches[i])
{
if (skip == 0)
{
return i;
}
skip--;
}
}
return -1;
}
private int MatchPossessive(Matcher m,
ReaderBuffer buffer,
int start,
int count)
{
int length = 0;
int subLength = 1;
// Match as many elements as possible
while (subLength > 0 && count < _max)
{
subLength = _elem.Match(m, buffer, start + length, 0);
if (subLength >= 0)
{
count++;
length += subLength;
}
}
// Return result
if (_min <= count && count <= _max)
{
return length;
}
else
{
return -1;
}
}
private void FindMatches(Matcher m,
ReaderBuffer buffer,
int start,
int length,
int count,
int attempt)
{
int subLength;
// Check match ending here
if (count > _max)
{
return;
}
if (_min <= count && attempt == 0)
{
if (_matches.Length <= length)
{
_matches.Length = length + 10;
}
_matches[length] = true;
}
// Check element match
subLength = _elem.Match(m, buffer, start, attempt);
if (subLength < 0)
{
return;
}
else if (subLength == 0)
{
if (_min == count + 1)
{
if (_matches.Length <= length)
{
_matches.Length = length + 10;
}
_matches[length] = true;
}
return;
}
// Find alternative and subsequent matches
FindMatches(m, buffer, start, length, count, attempt + 1);
FindMatches(m,
buffer,
start + subLength,
length + subLength,
count + 1,
0);
}
public override void PrintTo(TextWriter output, string indent)
{
output.Write(indent + "Repeat (" + _min + "," + _max + ")");
if (_type == RepeatType.RELUCTANT)
{
output.Write("?");
}
else if (_type == RepeatType.POSSESSIVE)
{
output.Write("+");
}
output.WriteLine();
_elem.PrintTo(output, indent + " ");
}
}
}

761
Parsing/StackParser.cs Normal file
View File

@@ -0,0 +1,761 @@
using System.Collections;
namespace Flee.Parsing
{
/**
* based on recursive descent parser, this implementation removes recursion
* and uses a stack instead. This parser handles LL(n) grammars,
* selecting the appropriate pattern to parse based on the next few
* tokens.
*/
internal class StackParser : Parser
{
/**
* this is the parser state that is pushed onto the stack, simulating
* the variable state needed in recursive version. Some variables
* substitute for execution position, such as validnext, so patterns
* are processed in the proper order.
*/
internal class ParseState
{
/**
* pattern for this state
*/
internal ProductionPattern pattern;
/**
* index of the alt pattern we are currently checking
*/
internal int altindex;
/**
* index into the list of elements for the alt pattern
*/
internal int elementindex;
/**
* index to the token we are processing.
*/
internal int tokenindex;
/**
* The node for current state
*/
internal Node node;
/**
* true if we already checked IsNext on the current pattern
* so we should not call it again
*/
internal bool validnext;
}
public StackParser(TextReader input) : base(input)
{
}
public StackParser(TextReader input, Analyzer analyzer)
: base(input, analyzer)
{
}
public StackParser(Tokenizer tokenizer)
: base(tokenizer)
{
}
public StackParser(Tokenizer tokenizer,
Analyzer analyzer)
: base(tokenizer, analyzer)
{
}
public override void AddPattern(ProductionPattern pattern)
{
// Check for empty matches
if (pattern.IsMatchingEmpty())
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
pattern.Name,
"zero elements can be matched (minimum is one)");
}
// Check for left-recusive patterns
if (pattern.IsLeftRecursive())
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_PRODUCTION,
pattern.Name,
"left recursive patterns are not allowed");
}
// Add pattern
base.AddPattern(pattern);
}
public override void Prepare()
{
// Performs production pattern checks
base.Prepare();
SetInitialized(false);
// Calculate production look-ahead sets
var e = GetPatterns().GetEnumerator();
while (e.MoveNext())
{
CalculateLookAhead((ProductionPattern)e.Current);
}
// Set initialized flag
SetInitialized(true);
}
protected override Node ParseStart()
{
var node = ParsePatterns(GetStartPattern());
var token = PeekToken(0);
if (token != null)
{
var list = new ArrayList(1) { "<EOF>" };
throw new ParseException(
ParseException.ErrorType.UNEXPECTED_TOKEN,
token.ToShortString(),
list,
token.StartLine,
token.StartColumn);
}
return node;
}
private ParseState NewState(ProductionPattern pattern)
{
return new ParseState()
{
pattern = pattern,
altindex = 0,
elementindex = 0,
tokenindex = 0,
node = null,
validnext = false
};
}
/// <summary>
/// parse patterns using a stack. The stack is local to this method, since the parser
/// is a singleton and may be parsing expressions from multiple threads, so cannot
/// use the object to store our stack.
/// </summary>
/// <param name="start"></param>
/// <returns></returns>
private Node ParsePatterns(ProductionPattern start)
{
Stack<ParseState> _stack = new Stack<ParseState>();
_stack.Push(NewState(start));
while (_stack.Count > 0)
{
ParseState state = _stack.Peek();
ProductionPattern pattern = state.pattern;
var defaultAlt = pattern.DefaultAlternative;
ProductionPattern nextpattern = null;
while (state.altindex < pattern.Count)
{
var alt = pattern[state.altindex];
if (state.validnext || (defaultAlt != alt && IsNext(alt)))
{
state.validnext = true;
nextpattern = ParseAlternative(state, alt);
break;
}
else
{
state.altindex++;
state.validnext = false;
}
}
// check if completed pass through alt patterns. try default
if (state.altindex >= pattern.Count)
{
if (!state.validnext && (defaultAlt == null || !IsNext(defaultAlt)))
{
ThrowParseException(FindUnion(pattern));
}
else
{
state.validnext = true;
nextpattern = ParseAlternative(state, defaultAlt);
}
}
if (nextpattern != null)
{
_stack.Push(NewState(nextpattern));
}
// we finished current pattern, so back up to previous state.
else
{
// if we have a node set, add it to the parent
var child = state.node;
_stack.Pop();
if (_stack.Count == 0)
{
// back to top, can return our result, which is top node
return child;
}
state = _stack.Peek();
AddNode((Production)state.node, child);
}
}
// should never get here, but must show we return something.
return null;
}
/**
* return the pattern to push onto stack and process next.
*/
private ProductionPattern ParseAlternative(ParseState state, ProductionPatternAlternative alt)
{
if (state.node == null)
{
state.node = NewProduction(alt.Pattern);
state.elementindex = 0;
EnterNode(state.node);
}
while (state.elementindex < alt.Count)
{
try
{
var pattern = ParseElement(state, alt[state.elementindex]);
if (pattern == null)
state.elementindex++;
else
return pattern;
}
catch (ParseException e)
{
AddError(e, true);
NextToken();
}
}
state.node = ExitNode(state.node);
return null;
}
private ProductionPattern ParseElement(ParseState state,
ProductionPatternElement elem)
{
for (int i = state.tokenindex; i < elem.MaxCount; i++)
{
if (i < elem.MinCount || IsNext(elem))
{
Node child;
if (elem.IsToken())
{
child = NextToken(elem.Id);
EnterNode(child);
AddNode((Production)state.node, ExitNode(child));
}
else
{
// continue from next token when we return
state.tokenindex = i + 1;
// return to start processing the new pattern at this state
return GetPattern(elem.Id); ;
}
}
else
{
break;
}
}
//
// we completed processing this element
state.tokenindex = 0;
return null;
}
private bool IsNext(ProductionPattern pattern)
{
LookAheadSet set = pattern.LookAhead;
if (set == null)
{
return false;
}
else
{
return set.IsNext(this);
}
}
private bool IsNext(ProductionPatternAlternative alt)
{
LookAheadSet set = alt.LookAhead;
if (set == null)
{
return false;
}
else
{
return set.IsNext(this);
}
}
private bool IsNext(ProductionPatternElement elem)
{
LookAheadSet set = elem.LookAhead;
if (set != null)
{
return set.IsNext(this);
}
else if (elem.IsToken())
{
return elem.IsMatch(PeekToken(0));
}
else
{
return IsNext(GetPattern(elem.Id));
}
}
private void CalculateLookAhead(ProductionPattern pattern)
{
ProductionPatternAlternative alt;
LookAheadSet previous = new LookAheadSet(0);
int length = 1;
int i;
CallStack stack = new CallStack();
// Calculate simple look-ahead
stack.Push(pattern.Name, 1);
var result = new LookAheadSet(1);
var alternatives = new LookAheadSet[pattern.Count];
for (i = 0; i < pattern.Count; i++)
{
alt = pattern[i];
alternatives[i] = FindLookAhead(alt, 1, 0, stack, null);
alt.LookAhead = alternatives[i];
result.AddAll(alternatives[i]);
}
if (pattern.LookAhead == null)
{
pattern.LookAhead = result;
}
var conflicts = FindConflicts(pattern, 1);
// Resolve conflicts
while (conflicts.Size() > 0)
{
length++;
stack.Clear();
stack.Push(pattern.Name, length);
conflicts.AddAll(previous);
for (i = 0; i < pattern.Count; i++)
{
alt = pattern[i];
if (alternatives[i].Intersects(conflicts))
{
alternatives[i] = FindLookAhead(alt,
length,
0,
stack,
conflicts);
alt.LookAhead = alternatives[i];
}
if (alternatives[i].Intersects(conflicts))
{
if (pattern.DefaultAlternative == null)
{
pattern.DefaultAlternative = alt;
}
else if (pattern.DefaultAlternative != alt)
{
result = alternatives[i].CreateIntersection(conflicts);
ThrowAmbiguityException(pattern.Name,
null,
result);
}
}
}
previous = conflicts;
conflicts = FindConflicts(pattern, length);
}
// Resolve conflicts inside rules
for (i = 0; i < pattern.Count; i++)
{
CalculateLookAhead(pattern[i], 0);
}
}
private void CalculateLookAhead(ProductionPatternAlternative alt,
int pos)
{
LookAheadSet previous = new LookAheadSet(0);
int length = 1;
// Check trivial cases
if (pos >= alt.Count)
{
return;
}
// Check for non-optional element
var pattern = alt.Pattern;
var elem = alt[pos];
if (elem.MinCount == elem.MaxCount)
{
CalculateLookAhead(alt, pos + 1);
return;
}
// Calculate simple look-aheads
var first = FindLookAhead(elem, 1, new CallStack(), null);
var follow = FindLookAhead(alt, 1, pos + 1, new CallStack(), null);
// Resolve conflicts
var location = "at position " + (pos + 1);
var conflicts = FindConflicts(pattern.Name,
location,
first,
follow);
while (conflicts.Size() > 0)
{
length++;
conflicts.AddAll(previous);
first = FindLookAhead(elem,
length,
new CallStack(),
conflicts);
follow = FindLookAhead(alt,
length,
pos + 1,
new CallStack(),
conflicts);
first = first.CreateCombination(follow);
elem.LookAhead = first;
if (first.Intersects(conflicts))
{
first = first.CreateIntersection(conflicts);
ThrowAmbiguityException(pattern.Name, location, first);
}
previous = conflicts;
conflicts = FindConflicts(pattern.Name,
location,
first,
follow);
}
// Check remaining elements
CalculateLookAhead(alt, pos + 1);
}
private LookAheadSet FindLookAhead(ProductionPattern pattern,
int length,
CallStack stack,
LookAheadSet filter)
{
// Check for infinite loop
if (stack.Contains(pattern.Name, length))
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INFINITE_LOOP,
pattern.Name,
(String)null);
}
// Find pattern look-ahead
stack.Push(pattern.Name, length);
var result = new LookAheadSet(length);
for (int i = 0; i < pattern.Count; i++)
{
var temp = FindLookAhead(pattern[i],
length,
0,
stack,
filter);
result.AddAll(temp);
}
stack.Pop();
return result;
}
private LookAheadSet FindLookAhead(ProductionPatternAlternative alt,
int length,
int pos,
CallStack stack,
LookAheadSet filter)
{
LookAheadSet follow;
// Check trivial cases
if (length <= 0 || pos >= alt.Count)
{
return new LookAheadSet(0);
}
// Find look-ahead for this element
var first = FindLookAhead(alt[pos], length, stack, filter);
if (alt[pos].MinCount == 0)
{
first.AddEmpty();
}
// Find remaining look-ahead
if (filter == null)
{
length -= first.GetMinLength();
if (length > 0)
{
follow = FindLookAhead(alt, length, pos + 1, stack, null);
first = first.CreateCombination(follow);
}
}
else if (filter.IsOverlap(first))
{
var overlaps = first.CreateOverlaps(filter);
length -= overlaps.GetMinLength();
filter = filter.CreateFilter(overlaps);
follow = FindLookAhead(alt, length, pos + 1, stack, filter);
first.RemoveAll(overlaps);
first.AddAll(overlaps.CreateCombination(follow));
}
return first;
}
private LookAheadSet FindLookAhead(ProductionPatternElement elem,
int length,
CallStack stack,
LookAheadSet filter)
{
// Find initial element look-ahead
var first = FindLookAhead(elem, length, 0, stack, filter);
var result = new LookAheadSet(length);
result.AddAll(first);
if (filter == null || !filter.IsOverlap(result))
{
return result;
}
// Handle element repetitions
if (elem.MaxCount == Int32.MaxValue)
{
first = first.CreateRepetitive();
}
var max = elem.MaxCount;
if (length < max)
{
max = length;
}
for (int i = 1; i < max; i++)
{
first = first.CreateOverlaps(filter);
if (first.Size() <= 0 || first.GetMinLength() >= length)
{
break;
}
var follow = FindLookAhead(elem,
length,
0,
stack,
filter.CreateFilter(first));
first = first.CreateCombination(follow);
result.AddAll(first);
}
return result;
}
private LookAheadSet FindLookAhead(ProductionPatternElement elem,
int length,
int dummy,
CallStack stack,
LookAheadSet filter)
{
LookAheadSet result;
if (elem.IsToken())
{
result = new LookAheadSet(length);
result.Add(elem.Id);
}
else
{
var pattern = GetPattern(elem.Id);
result = FindLookAhead(pattern, length, stack, filter);
if (stack.Contains(pattern.Name))
{
result = result.CreateRepetitive();
}
}
return result;
}
private LookAheadSet FindConflicts(ProductionPattern pattern,
int maxLength)
{
LookAheadSet result = new LookAheadSet(maxLength);
for (int i = 0; i < pattern.Count; i++)
{
var set1 = pattern[i].LookAhead;
for (int j = 0; j < i; j++)
{
var set2 = pattern[j].LookAhead;
result.AddAll(set1.CreateIntersection(set2));
}
}
if (result.IsRepetitive())
{
ThrowAmbiguityException(pattern.Name, null, result);
}
return result;
}
private LookAheadSet FindConflicts(string pattern,
string location,
LookAheadSet set1,
LookAheadSet set2)
{
var result = set1.CreateIntersection(set2);
if (result.IsRepetitive())
{
ThrowAmbiguityException(pattern, location, result);
}
return result;
}
private LookAheadSet FindUnion(ProductionPattern pattern)
{
LookAheadSet result;
int length = 0;
int i;
for (i = 0; i < pattern.Count; i++)
{
result = pattern[i].LookAhead;
if (result.GetMaxLength() > length)
{
length = result.GetMaxLength();
}
}
result = new LookAheadSet(length);
for (i = 0; i < pattern.Count; i++)
{
result.AddAll(pattern[i].LookAhead);
}
return result;
}
private void ThrowParseException(LookAheadSet set)
{
ArrayList list = new ArrayList();
// Read tokens until mismatch
while (set.IsNext(this, 1))
{
set = set.CreateNextSet(NextToken().Id);
}
// Find next token descriptions
var initials = set.GetInitialTokens();
for (int i = 0; i < initials.Length; i++)
{
list.Add(GetTokenDescription(initials[i]));
}
// Create exception
var token = NextToken();
throw new ParseException(ParseException.ErrorType.UNEXPECTED_TOKEN,
token.ToShortString(),
list,
token.StartLine,
token.StartColumn);
}
private void ThrowAmbiguityException(string pattern,
string location,
LookAheadSet set)
{
ArrayList list = new ArrayList();
// Find next token descriptions
var initials = set.GetInitialTokens();
for (int i = 0; i < initials.Length; i++)
{
list.Add(GetTokenDescription(initials[i]));
}
// Create exception
throw new ParserCreationException(
ParserCreationException.ErrorType.INHERENT_AMBIGUITY,
pattern,
location,
list);
}
private class CallStack
{
private readonly ArrayList _nameStack = new ArrayList();
private readonly ArrayList _valueStack = new ArrayList();
public bool Contains(string name)
{
return _nameStack.Contains(name);
}
public bool Contains(string name, int value)
{
for (int i = 0; i < _nameStack.Count; i++)
{
if (_nameStack[i].Equals(name)
&& _valueStack[i].Equals(value))
{
return true;
}
}
return false;
}
public void Clear()
{
_nameStack.Clear();
_valueStack.Clear();
}
public void Push(string name, int value)
{
_nameStack.Add(name);
_valueStack.Add(value);
}
public void Pop()
{
if (_nameStack.Count > 0)
{
_nameStack.RemoveAt(_nameStack.Count - 1);
_valueStack.RemoveAt(_valueStack.Count - 1);
}
}
}
}
}

64
Parsing/StringElement.cs Normal file
View File

@@ -0,0 +1,64 @@
namespace Flee.Parsing
{
/**
* A regular expression string element. This element only matches
* an exact string. Once created, the string element is immutable.
*/
internal class StringElement : Element
{
private readonly string _value;
public StringElement(char c)
: this(c.ToString())
{
}
public StringElement(string str)
{
_value = str;
}
public string GetString()
{
return _value;
}
public override object Clone()
{
return this;
}
public override int Match(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
if (skip != 0)
{
return -1;
}
for (int i = 0; i < _value.Length; i++)
{
var c = buffer.Peek(start + i);
if (c < 0)
{
m.SetReadEndOfString();
return -1;
}
if (m.IsCaseInsensitive())
{
c = (int)Char.ToLower((char)c);
}
if (c != (int)_value[i])
{
return -1;
}
}
return _value.Length;
}
public override void PrintTo(TextWriter output, string indent)
{
output.WriteLine(indent + "'" + _value + "'");
}
}
}

168
Parsing/Token.cs Normal file
View File

@@ -0,0 +1,168 @@
using System.Text;
namespace Flee.Parsing
{
/**
* A token node. This class represents a token (i.e. a set of adjacent
* characters) in a parse tree. The tokens are created by a tokenizer,
* that groups characters together into tokens according to a set of
* token patterns.
*/
internal class Token : Node
{
private readonly TokenPattern _pattern;
private readonly string _image;
private readonly int _startLine;
private readonly int _startColumn;
private readonly int _endLine;
private readonly int _endColumn;
private Token _previous = null;
private Token _next = null;
public Token(TokenPattern pattern, string image, int line, int col)
{
this._pattern = pattern;
this._image = image;
this._startLine = line;
this._startColumn = col;
this._endLine = line;
this._endColumn = col + image.Length - 1;
for (int pos = 0; image.IndexOf('\n', pos) >= 0;)
{
pos = image.IndexOf('\n', pos) + 1;
this._endLine++;
_endColumn = image.Length - pos;
}
}
public override int Id => _pattern.Id;
public override string Name => _pattern.Name;
public override int StartLine => _startLine;
public override int StartColumn => _startColumn;
public override int EndLine => _endLine;
public override int EndColumn => _endColumn;
public string Image => _image;
public string GetImage()
{
return Image;
}
internal TokenPattern Pattern => _pattern;
public Token Previous
{
get
{
return _previous;
}
set
{
if (_previous != null)
{
_previous._next = null;
}
_previous = value;
if (_previous != null)
{
_previous._next = this;
}
}
}
public Token GetPreviousToken()
{
return Previous;
}
public Token Next
{
get
{
return _next;
}
set
{
if (_next != null)
{
_next._previous = null;
}
_next = value;
if (_next != null)
{
_next._previous = this;
}
}
}
public Token GetNextToken()
{
return Next;
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
int newline = _image.IndexOf('\n');
buffer.Append(_pattern.Name);
buffer.Append("(");
buffer.Append(_pattern.Id);
buffer.Append("): \"");
if (newline >= 0)
{
if (newline > 0 && _image[newline - 1] == '\r')
{
newline--;
}
buffer.Append(_image.Substring(0, newline));
buffer.Append("(...)");
}
else
{
buffer.Append(_image);
}
buffer.Append("\", line: ");
buffer.Append(_startLine);
buffer.Append(", col: ");
buffer.Append(_startColumn);
return buffer.ToString();
}
public string ToShortString()
{
StringBuilder buffer = new StringBuilder();
int newline = _image.IndexOf('\n');
buffer.Append('"');
if (newline >= 0)
{
if (newline > 0 && _image[newline - 1] == '\r')
{
newline--;
}
buffer.Append(_image.Substring(0, newline));
buffer.Append("(...)");
}
else
{
buffer.Append(_image);
}
buffer.Append('"');
if (_pattern.Type == TokenPattern.PatternType.REGEXP)
{
buffer.Append(" <");
buffer.Append(_pattern.Name);
buffer.Append(">");
}
return buffer.ToString();
}
}
}

31
Parsing/TokenMatch.cs Normal file
View File

@@ -0,0 +1,31 @@
namespace Flee.Parsing
{
/**
* The token match status. This class contains logic to ensure that
* only the longest match is considered.
*/
internal class TokenMatch
{
private int _length = 0;
private TokenPattern _pattern = null;
public void Clear()
{
_length = 0;
_pattern = null;
}
public int Length => _length;
public TokenPattern Pattern => _pattern;
public void Update(int length, TokenPattern pattern)
{
if (this._length < length)
{
this._length = length;
this._pattern = pattern;
}
}
}
}

825
Parsing/TokenNFA.cs Normal file
View File

@@ -0,0 +1,825 @@
namespace Flee.Parsing
{
/**
* A non-deterministic finite state automaton (NFA) for matching
* tokens. It supports both fixed strings and simple regular
* expressions, but should perform similar to a DFA due to highly
* optimized data structures and tuning. The memory footprint during
* matching should be near zero, since no heap memory is allocated
* unless the pre-allocated queues need to be enlarged. The NFA also
* does not use recursion, but iterates in a loop instead.
*/
internal class TokenNFA
{
private readonly NFAState[] _initialChar = new NFAState[128];
private readonly NFAState _initial = new NFAState();
private readonly NFAStateQueue _queue = new NFAStateQueue();
public void AddTextMatch(string str, bool ignoreCase, TokenPattern value)
{
NFAState state;
char ch = str[0];
if (ch < 128 && !ignoreCase)
{
state = _initialChar[ch];
if (state == null)
{
state = _initialChar[ch] = new NFAState();
}
}
else
{
state = _initial.AddOut(ch, ignoreCase, null);
}
for (int i = 1; i < str.Length; i++)
{
state = state.AddOut(str[i], ignoreCase, null);
}
state.Value = value;
}
public void AddRegExpMatch(string pattern,
bool ignoreCase,
TokenPattern value)
{
TokenRegExpParser parser = new TokenRegExpParser(pattern, ignoreCase);
string debug = "DFA regexp; " + parser.GetDebugInfo();
var isAscii = parser.Start.IsAsciiOutgoing();
for (int i = 0; isAscii && i < 128; i++)
{
bool match = false;
for (int j = 0; j < parser.Start.Outgoing.Length; j++)
{
if (parser.Start.Outgoing[j].Match((char)i))
{
if (match)
{
isAscii = false;
break;
}
match = true;
}
}
if (match && _initialChar[i] != null)
{
isAscii = false;
}
}
if (parser.Start.Incoming.Length > 0)
{
_initial.AddOut(new NFAEpsilonTransition(parser.Start));
debug += ", uses initial epsilon";
}
else if (isAscii && !ignoreCase)
{
for (int i = 0; isAscii && i < 128; i++)
{
for (int j = 0; j < parser.Start.Outgoing.Length; j++)
{
if (parser.Start.Outgoing[j].Match((char)i))
{
_initialChar[i] = parser.Start.Outgoing[j].State;
}
}
}
debug += ", uses ASCII lookup";
}
else
{
parser.Start.MergeInto(_initial);
debug += ", uses initial state";
}
parser.End.Value = value;
value.DebugInfo = debug;
}
public int Match(ReaderBuffer buffer, TokenMatch match)
{
int length = 0;
int pos = 1;
NFAState state;
// The first step of the match loop has been unrolled and
// optimized for performance below.
this._queue.Clear();
var peekChar = buffer.Peek(0);
if (0 <= peekChar && peekChar < 128)
{
state = this._initialChar[peekChar];
if (state != null)
{
this._queue.AddLast(state);
}
}
if (peekChar >= 0)
{
this._initial.MatchTransitions((char)peekChar, this._queue, true);
}
this._queue.MarkEnd();
peekChar = buffer.Peek(1);
// The remaining match loop processes all subsequent states
while (!this._queue.Empty)
{
if (this._queue.Marked)
{
pos++;
peekChar = buffer.Peek(pos);
this._queue.MarkEnd();
}
state = this._queue.RemoveFirst();
if (state.Value != null)
{
match.Update(pos, state.Value);
}
if (peekChar >= 0)
{
state.MatchTransitions((char)peekChar, this._queue, false);
}
}
return length;
}
}
/**
* An NFA state. The NFA consists of a series of states, each
* having zero or more transitions to other states.
*/
internal class NFAState
{
internal TokenPattern Value = null;
internal NFATransition[] Incoming = new NFATransition[0];
internal NFATransition[] Outgoing = new NFATransition[0];
internal bool EpsilonOut = false;
public bool HasTransitions()
{
return Incoming.Length > 0 || Outgoing.Length > 0;
}
public bool IsAsciiOutgoing()
{
for (int i = 0; i < Outgoing.Length; i++)
{
if (!Outgoing[i].IsAscii())
{
return false;
}
}
return true;
}
public void AddIn(NFATransition trans)
{
Array.Resize(ref Incoming, Incoming.Length + 1);
Incoming[Incoming.Length - 1] = trans;
}
public NFAState AddOut(char ch, bool ignoreCase, NFAState state)
{
if (ignoreCase)
{
if (state == null)
{
state = new NFAState();
}
AddOut(new NFACharTransition(Char.ToLower(ch), state));
AddOut(new NFACharTransition(Char.ToUpper(ch), state));
return state;
}
else
{
if (state == null)
{
state = FindUniqueCharTransition(ch);
if (state != null)
{
return state;
}
state = new NFAState();
}
return AddOut(new NFACharTransition(ch, state));
}
}
public NFAState AddOut(NFATransition trans)
{
Array.Resize(ref Outgoing, Outgoing.Length + 1);
Outgoing[Outgoing.Length - 1] = trans;
if (trans is NFAEpsilonTransition)
{
EpsilonOut = true;
}
return trans.State;
}
public void MergeInto(NFAState state)
{
for (int i = 0; i < Incoming.Length; i++)
{
state.AddIn(Incoming[i]);
Incoming[i].State = state;
}
Incoming = null;
for (int i = 0; i < Outgoing.Length; i++)
{
state.AddOut(Outgoing[i]);
}
Outgoing = null;
}
private NFAState FindUniqueCharTransition(char ch)
{
NFATransition res = null;
NFATransition trans;
for (int i = 0; i < Outgoing.Length; i++)
{
trans = Outgoing[i];
if (trans.Match(ch) && trans is NFACharTransition)
{
if (res != null)
{
return null;
}
res = trans;
}
}
for (int i = 0; res != null && i < Outgoing.Length; i++)
{
trans = Outgoing[i];
if (trans != res && trans.State == res.State)
{
return null;
}
}
return res?.State;
}
public void MatchTransitions(char ch, NFAStateQueue queue, bool initial)
{
for (int i = 0; i < Outgoing.Length; i++)
{
var trans = Outgoing[i];
var target = trans.State;
if (initial && trans is NFAEpsilonTransition)
{
target.MatchTransitions(ch, queue, true);
}
else if (trans.Match(ch))
{
queue.AddLast(target);
if (target.EpsilonOut)
{
target.MatchEmpty(queue);
}
}
}
}
public void MatchEmpty(NFAStateQueue queue)
{
for (int i = 0; i < Outgoing.Length; i++)
{
var trans = Outgoing[i];
if (trans is NFAEpsilonTransition)
{
var target = trans.State;
queue.AddLast(target);
if (target.EpsilonOut)
{
target.MatchEmpty(queue);
}
}
}
}
}
/**
* An NFA state transition. A transition checks a single
* character of input an determines if it is a match. If a match
* is encountered, the NFA should move forward to the transition
* state.
*/
internal abstract class NFATransition
{
internal NFAState State;
protected NFATransition(NFAState state)
{
this.State = state;
this.State.AddIn(this);
}
public abstract bool IsAscii();
public abstract bool Match(char ch);
public abstract NFATransition Copy(NFAState state);
}
/**
* The special epsilon transition. This transition matches the
* empty input, i.e. it is an automatic transition that doesn't
* read any input. As such, it returns false in the match method
* and is handled specially everywhere.
*/
internal class NFAEpsilonTransition : NFATransition
{
public NFAEpsilonTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return false;
}
public override bool Match(char ch)
{
return false;
}
public override NFATransition Copy(NFAState state)
{
return new NFAEpsilonTransition(state);
}
}
/**
* A single character match transition.
*/
internal class NFACharTransition : NFATransition
{
private readonly char _match;
public NFACharTransition(char match, NFAState state) : base(state)
{
_match = match;
}
public override bool IsAscii()
{
return 0 <= _match && _match < 128;
}
public override bool Match(char ch)
{
return this._match == ch;
}
public override NFATransition Copy(NFAState state)
{
return new NFACharTransition(_match, state);
}
}
/**
* A character range match transition. Used for user-defined
* character sets in regular expressions.
*/
internal class NFACharRangeTransition : NFATransition
{
protected bool Inverse;
protected bool IgnoreCase;
private object[] _contents = new object[0];
public NFACharRangeTransition(bool inverse,
bool ignoreCase,
NFAState state) : base(state)
{
this.Inverse = inverse;
this.IgnoreCase = ignoreCase;
}
public override bool IsAscii()
{
if (Inverse)
{
return false;
}
for (int i = 0; i < _contents.Length; i++)
{
var obj = _contents[i];
if (obj is char)
{
var c = (char)obj;
if (c < 0 || 128 <= c)
{
return false;
}
}
else if (obj is Range)
{
if (!((Range)obj).IsAscii())
{
return false;
}
}
}
return true;
}
public void AddCharacter(char c)
{
if (IgnoreCase)
{
c = Char.ToLower(c);
}
AddContent(c);
}
public void AddRange(char min, char max)
{
if (IgnoreCase)
{
min = Char.ToLower(min);
max = Char.ToLower(max);
}
AddContent(new Range(min, max));
}
private void AddContent(Object obj)
{
Array.Resize(ref _contents, _contents.Length + 1);
_contents[_contents.Length - 1] = obj;
}
public override bool Match(char ch)
{
object obj;
char c;
Range r;
if (IgnoreCase)
{
ch = Char.ToLower(ch);
}
for (int i = 0; i < _contents.Length; i++)
{
obj = _contents[i];
if (obj is char)
{
c = (char)obj;
if (c == ch)
{
return !Inverse;
}
}
else if (obj is Range)
{
r = (Range)obj;
if (r.Inside(ch))
{
return !Inverse;
}
}
}
return Inverse;
}
public override NFATransition Copy(NFAState state)
{
var copy = new NFACharRangeTransition(Inverse, IgnoreCase, state) { _contents = _contents };
return copy;
}
private class Range
{
private readonly char _min;
private readonly char _max;
public Range(char min, char max)
{
this._min = min;
this._max = max;
}
public bool IsAscii()
{
return 0 <= _min && _min < 128 &&
0 <= _max && _max < 128;
}
public bool Inside(char c)
{
return _min <= c && c <= _max;
}
}
}
/**
* The dot ('.') character set transition. This transition
* matches a single character that is not equal to a newline
* character.
*/
internal class NFADotTransition : NFATransition
{
public NFADotTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return false;
}
public override bool Match(char ch)
{
switch (ch)
{
case '\n':
case '\r':
case '\u0085':
case '\u2028':
case '\u2029':
return false;
default:
return true;
}
}
public override NFATransition Copy(NFAState state)
{
return new NFADotTransition(state);
}
}
/**
* The digit character set transition. This transition matches a
* single numeric character.
*/
internal class NFADigitTransition : NFATransition
{
public NFADigitTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return true;
}
public override bool Match(char ch)
{
return '0' <= ch && ch <= '9';
}
public override NFATransition Copy(NFAState state)
{
return new NFADigitTransition(state);
}
}
/**
* The non-digit character set transition. This transition
* matches a single non-numeric character.
*/
internal class NFANonDigitTransition : NFATransition
{
public NFANonDigitTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return false;
}
public override bool Match(char ch)
{
return ch < '0' || '9' < ch;
}
public override NFATransition Copy(NFAState state)
{
return new NFANonDigitTransition(state);
}
}
/**
* The whitespace character set transition. This transition
* matches a single whitespace character.
*/
internal class NFAWhitespaceTransition : NFATransition
{
public NFAWhitespaceTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return true;
}
public override bool Match(char ch)
{
switch (ch)
{
case ' ':
case '\t':
case '\n':
case '\f':
case '\r':
case (char)11:
return true;
default:
return false;
}
}
public override NFATransition Copy(NFAState state)
{
return new NFAWhitespaceTransition(state);
}
}
/**
* The non-whitespace character set transition. This transition
* matches a single non-whitespace character.
*/
internal class NFANonWhitespaceTransition : NFATransition
{
public NFANonWhitespaceTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return false;
}
public override bool Match(char ch)
{
switch (ch)
{
case ' ':
case '\t':
case '\n':
case '\f':
case '\r':
case (char)11:
return false;
default:
return true;
}
}
public override NFATransition Copy(NFAState state)
{
return new NFANonWhitespaceTransition(state);
}
}
/**
* The word character set transition. This transition matches a
* single word character.
*/
internal class NFAWordTransition : NFATransition
{
public NFAWordTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return true;
}
public override bool Match(char ch)
{
return ('a' <= ch && ch <= 'z')
|| ('A' <= ch && ch <= 'Z')
|| ('0' <= ch && ch <= '9')
|| ch == '_';
}
public override NFATransition Copy(NFAState state)
{
return new NFAWordTransition(state);
}
}
/**
* The non-word character set transition. This transition matches
* a single non-word character.
*/
internal class NFANonWordTransition : NFATransition
{
public NFANonWordTransition(NFAState state) : base(state)
{
}
public override bool IsAscii()
{
return false;
}
public override bool Match(char ch)
{
bool word = ('a' <= ch && ch <= 'z')
|| ('A' <= ch && ch <= 'Z')
|| ('0' <= ch && ch <= '9')
|| ch == '_';
return !word;
}
public override NFATransition Copy(NFAState state)
{
return new NFANonWordTransition(state);
}
}
/**
* An NFA state queue. This queue is used during processing to
* keep track of the current and subsequent NFA states. The
* current state is read from the beginning of the queue, and new
* states are added at the end. A marker index is used to
* separate the current from the subsequent states.<p>
*
* The queue implementation is optimized for quick removal at the
* beginning and addition at the end. It will attempt to use a
* fixed-size array to store the whole queue, and moves the data
* in this array only when absolutely needed. The array is also
* enlarged automatically if too many states are being processed
* at a single time.
*/
internal class NFAStateQueue
{
private NFAState[] _queue = new NFAState[2048];
private int _first = 0;
private int _last = 0;
private int _mark = 0;
public bool Empty => (_last <= _first);
public bool Marked => _first == _mark;
public void Clear()
{
_first = 0;
_last = 0;
_mark = 0;
}
public void MarkEnd()
{
_mark = _last;
}
public NFAState RemoveFirst()
{
if (_first < _last)
{
_first++;
return _queue[_first - 1];
}
else
{
return null;
}
}
public void AddLast(NFAState state)
{
if (_last >= _queue.Length)
{
if (_first <= 0)
{
Array.Resize(ref _queue, _queue.Length * 2);
}
else
{
Array.Copy(_queue, _first, _queue, 0, _last - _first);
_last -= _first;
_mark -= _first;
_first = 0;
}
}
_queue[_last++] = state;
}
}
}

303
Parsing/TokenPattern.cs Normal file
View File

@@ -0,0 +1,303 @@
using System.Text;
namespace Flee.Parsing
{
/**
* A token pattern. This class contains the definition of a token
* (i.e. it's pattern), and allows testing a string against this
* pattern. A token pattern is uniquely identified by an integer id,
* that must be provided upon creation.
*
*/
internal class TokenPattern
{
public enum PatternType
{
/**
* The string pattern type is used for tokens that only
* match an exact string.
*/
STRING,
/**
* The regular expression pattern type is used for tokens
* that match a regular expression.
*/
REGEXP
}
private int _id;
private string _name;
private PatternType _type;
private string _pattern;
private bool _error;
private string _errorMessage;
private bool _ignore;
private string _ignoreMessage;
private string _debugInfo;
public TokenPattern(int id,
string name,
PatternType type,
string pattern)
{
this._id = id;
this._name = name;
this._type = type;
this._pattern = pattern;
}
public int Id
{
get
{
return _id;
}
set { _id = value; }
}
public int GetId()
{
return _id;
}
public string Name
{
get
{
return _name;
}
set { _name = value; }
}
public string GetName()
{
return _name;
}
public PatternType Type
{
get
{
return _type;
}
set { _type = value; }
}
public PatternType GetPatternType()
{
return _type;
}
public string Pattern
{
get
{
return _pattern;
}
set { _pattern = value; }
}
public string GetPattern()
{
return _pattern;
}
public bool Error
{
get
{
return _error;
}
set
{
_error = value;
if (_error && _errorMessage == null)
{
_errorMessage = "unrecognized token found";
}
}
}
public string ErrorMessage
{
get
{
return _errorMessage;
}
set
{
_error = true;
_errorMessage = value;
}
}
public bool IsError()
{
return Error;
}
public string GetErrorMessage()
{
return ErrorMessage;
}
public void SetError()
{
Error = true;
}
public void SetError(string message)
{
ErrorMessage = message;
}
public bool Ignore
{
get
{
return _ignore;
}
set
{
_ignore = value;
}
}
public string IgnoreMessage
{
get
{
return _ignoreMessage;
}
set
{
_ignore = true;
_ignoreMessage = value;
}
}
public bool IsIgnore()
{
return Ignore;
}
public string GetIgnoreMessage()
{
return IgnoreMessage;
}
public void SetIgnore()
{
Ignore = true;
}
public void SetIgnore(string message)
{
IgnoreMessage = message;
}
public string DebugInfo
{
get
{
return _debugInfo;
}
set
{
_debugInfo = value;
}
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
buffer.Append(_name);
buffer.Append(" (");
buffer.Append(_id);
buffer.Append("): ");
switch (_type)
{
case PatternType.STRING:
buffer.Append("\"");
buffer.Append(_pattern);
buffer.Append("\"");
break;
case PatternType.REGEXP:
buffer.Append("<<");
buffer.Append(_pattern);
buffer.Append(">>");
break;
}
if (_error)
{
buffer.Append(" ERROR: \"");
buffer.Append(_errorMessage);
buffer.Append("\"");
}
if (_ignore)
{
buffer.Append(" IGNORE");
if (_ignoreMessage != null)
{
buffer.Append(": \"");
buffer.Append(_ignoreMessage);
buffer.Append("\"");
}
}
if (_debugInfo != null)
{
buffer.Append("\n ");
buffer.Append(_debugInfo);
}
return buffer.ToString();
}
public string ToShortString()
{
StringBuilder buffer = new StringBuilder();
int newline = _pattern.IndexOf('\n');
if (_type == PatternType.STRING)
{
buffer.Append("\"");
if (newline >= 0)
{
if (newline > 0 && _pattern[newline - 1] == '\r')
{
newline--;
}
buffer.Append(_pattern.Substring(0, newline));
buffer.Append("(...)");
}
else
{
buffer.Append(_pattern);
}
buffer.Append("\"");
}
else
{
buffer.Append("<");
buffer.Append(_name);
buffer.Append(">");
}
return buffer.ToString();
}
public void SetData(int id, string name, PatternType type, string pattern)
{
Id = id;
Name = name;
Type = type;
Pattern = pattern;
}
}
}

View File

@@ -0,0 +1,545 @@
using System.Collections;
using System.Globalization;
using System.Text;
namespace Flee.Parsing
{
/**
* A regular expression parser. The parser creates an NFA for the
* regular expression having a single start and acceptance states.
*/
internal class TokenRegExpParser
{
private readonly string _pattern;
private readonly bool _ignoreCase;
private int _pos;
internal NFAState Start = new NFAState();
internal NFAState End;
private int _stateCount;
private int _transitionCount;
private int _epsilonCount;
public TokenRegExpParser(string pattern) : this(pattern, false)
{
}
public TokenRegExpParser(string pattern, bool ignoreCase)
{
this._pattern = pattern;
this._ignoreCase = ignoreCase;
this._pos = 0;
this.End = ParseExpr(Start);
if (_pos < pattern.Length)
{
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos,
pattern);
}
}
public string GetDebugInfo()
{
if (_stateCount == 0)
{
UpdateStats(Start, new Hashtable());
}
return _stateCount + " states, " +
_transitionCount + " transitions, " +
_epsilonCount + " epsilons";
}
private void UpdateStats(NFAState state, Hashtable visited)
{
if (!visited.ContainsKey(state))
{
visited.Add(state, state);
_stateCount++;
for (int i = 0; i < state.Outgoing.Length; i++)
{
_transitionCount++;
if (state.Outgoing[i] is NFAEpsilonTransition)
{
_epsilonCount++;
}
UpdateStats(state.Outgoing[i].State, visited);
}
}
}
private NFAState ParseExpr(NFAState start)
{
NFAState end = new NFAState();
do
{
if (PeekChar(0) == '|')
{
ReadChar('|');
}
var subStart = new NFAState();
var subEnd = ParseTerm(subStart);
if (subStart.Incoming.Length == 0)
{
subStart.MergeInto(start);
}
else
{
start.AddOut(new NFAEpsilonTransition(subStart));
}
if (subEnd.Outgoing.Length == 0 ||
(!end.HasTransitions() && PeekChar(0) != '|'))
{
subEnd.MergeInto(end);
}
else
{
subEnd.AddOut(new NFAEpsilonTransition(end));
}
} while (PeekChar(0) == '|');
return end;
}
private NFAState ParseTerm(NFAState start)
{
var end = ParseFact(start);
while (true)
{
switch (PeekChar(0))
{
case -1:
case ')':
case ']':
case '{':
case '}':
case '?':
case '+':
case '|':
return end;
default:
end = ParseFact(end);
break;
}
}
}
private NFAState ParseFact(NFAState start)
{
NFAState placeholder = new NFAState();
var end = ParseAtom(placeholder);
switch (PeekChar(0))
{
case '?':
case '*':
case '+':
case '{':
end = ParseAtomModifier(placeholder, end);
break;
}
if (placeholder.Incoming.Length > 0 && start.Outgoing.Length > 0)
{
start.AddOut(new NFAEpsilonTransition(placeholder));
return end;
}
else
{
placeholder.MergeInto(start);
return (end == placeholder) ? start : end;
}
}
private NFAState ParseAtom(NFAState start)
{
NFAState end;
switch (PeekChar(0))
{
case '.':
ReadChar('.');
return start.AddOut(new NFADotTransition(new NFAState()));
case '(':
ReadChar('(');
end = ParseExpr(start);
ReadChar(')');
return end;
case '[':
ReadChar('[');
end = ParseCharSet(start);
ReadChar(']');
return end;
case -1:
case ')':
case ']':
case '{':
case '}':
case '?':
case '*':
case '+':
case '|':
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos,
_pattern);
default:
return ParseChar(start);
}
}
private NFAState ParseAtomModifier(NFAState start, NFAState end)
{
int min = 0;
int max = -1;
int firstPos = _pos;
// Read min and max
switch (ReadChar())
{
case '?':
min = 0;
max = 1;
break;
case '*':
min = 0;
max = -1;
break;
case '+':
min = 1;
max = -1;
break;
case '{':
min = ReadNumber();
max = min;
if (PeekChar(0) == ',')
{
ReadChar(',');
max = -1;
if (PeekChar(0) != '}')
{
max = ReadNumber();
}
}
ReadChar('}');
if (max == 0 || (max > 0 && min > max))
{
throw new RegExpException(
RegExpException.ErrorType.INVALID_REPEAT_COUNT,
firstPos,
_pattern);
}
break;
default:
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos - 1,
_pattern);
}
// Read possessive or reluctant modifiers
if (PeekChar(0) == '?')
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_SPECIAL_CHARACTER,
_pos,
_pattern);
}
else if (PeekChar(0) == '+')
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_SPECIAL_CHARACTER,
_pos,
_pattern);
}
// Handle supported repeaters
if (min == 0 && max == 1)
{
return start.AddOut(new NFAEpsilonTransition(end));
}
else if (min == 0 && max == -1)
{
if (end.Outgoing.Length == 0)
{
end.MergeInto(start);
}
else
{
end.AddOut(new NFAEpsilonTransition(start));
}
return start;
}
else if (min == 1 && max == -1)
{
if (start.Outgoing.Length == 1 &&
end.Outgoing.Length == 0 &&
end.Incoming.Length == 1 &&
start.Outgoing[0] == end.Incoming[0])
{
end.AddOut(start.Outgoing[0].Copy(end));
}
else
{
end.AddOut(new NFAEpsilonTransition(start));
}
return end;
}
else
{
throw new RegExpException(
RegExpException.ErrorType.INVALID_REPEAT_COUNT,
firstPos,
_pattern);
}
}
private NFAState ParseCharSet(NFAState start)
{
NFAState end = new NFAState();
NFACharRangeTransition range;
if (PeekChar(0) == '^')
{
ReadChar('^');
range = new NFACharRangeTransition(true, _ignoreCase, end);
}
else
{
range = new NFACharRangeTransition(false, _ignoreCase, end);
}
start.AddOut(range);
while (PeekChar(0) > 0)
{
var min = (char)PeekChar(0);
switch (min)
{
case ']':
return end;
case '\\':
range.AddCharacter(ReadEscapeChar());
break;
default:
ReadChar(min);
if (PeekChar(0) == '-' &&
PeekChar(1) > 0 &&
PeekChar(1) != ']')
{
ReadChar('-');
var max = ReadChar();
range.AddRange(min, max);
}
else
{
range.AddCharacter(min);
}
break;
}
}
return end;
}
private NFAState ParseChar(NFAState start)
{
switch (PeekChar(0))
{
case '\\':
return ParseEscapeChar(start);
case '^':
case '$':
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_SPECIAL_CHARACTER,
_pos,
_pattern);
default:
return start.AddOut(ReadChar(), _ignoreCase, new NFAState());
}
}
private NFAState ParseEscapeChar(NFAState start)
{
NFAState end = new NFAState();
if (PeekChar(0) == '\\' && PeekChar(1) > 0)
{
switch ((char)PeekChar(1))
{
case 'd':
ReadChar();
ReadChar();
return start.AddOut(new NFADigitTransition(end));
case 'D':
ReadChar();
ReadChar();
return start.AddOut(new NFANonDigitTransition(end));
case 's':
ReadChar();
ReadChar();
return start.AddOut(new NFAWhitespaceTransition(end));
case 'S':
ReadChar();
ReadChar();
return start.AddOut(new NFANonWhitespaceTransition(end));
case 'w':
ReadChar();
ReadChar();
return start.AddOut(new NFAWordTransition(end));
case 'W':
ReadChar();
ReadChar();
return start.AddOut(new NFANonWordTransition(end));
}
}
return start.AddOut(ReadEscapeChar(), _ignoreCase, end);
}
private char ReadEscapeChar()
{
string str;
int value;
ReadChar('\\');
var c = ReadChar();
switch (c)
{
case '0':
c = ReadChar();
if (c < '0' || c > '3')
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - 3,
_pattern);
}
value = c - '0';
c = (char)PeekChar(0);
if ('0' <= c && c <= '7')
{
value *= 8;
value += ReadChar() - '0';
c = (char)PeekChar(0);
if ('0' <= c && c <= '7')
{
value *= 8;
value += ReadChar() - '0';
}
}
return (char)value;
case 'x':
str = ReadChar().ToString() + ReadChar().ToString();
try
{
value = Int32.Parse(str, NumberStyles.AllowHexSpecifier);
return (char)value;
}
catch (FormatException)
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - str.Length - 2,
_pattern);
}
case 'u':
str = ReadChar().ToString() +
ReadChar().ToString() +
ReadChar().ToString() +
ReadChar().ToString();
try
{
value = Int32.Parse(str, NumberStyles.AllowHexSpecifier);
return (char)value;
}
catch (FormatException)
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - str.Length - 2,
_pattern);
}
case 't':
return '\t';
case 'n':
return '\n';
case 'r':
return '\r';
case 'f':
return '\f';
case 'a':
return '\u0007';
case 'e':
return '\u001B';
default:
if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
{
throw new RegExpException(
RegExpException.ErrorType.UNSUPPORTED_ESCAPE_CHARACTER,
_pos - 2,
_pattern);
}
return c;
}
}
private int ReadNumber()
{
StringBuilder buf = new StringBuilder();
int c;
c = PeekChar(0);
while ('0' <= c && c <= '9')
{
buf.Append(ReadChar());
c = PeekChar(0);
}
if (buf.Length <= 0)
{
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos,
_pattern);
}
return Int32.Parse(buf.ToString());
}
private char ReadChar()
{
int c = PeekChar(0);
if (c < 0)
{
throw new RegExpException(
RegExpException.ErrorType.UNTERMINATED_PATTERN,
_pos,
_pattern);
}
else
{
_pos++;
return (char)c;
}
}
private char ReadChar(char c)
{
if (c != ReadChar())
{
throw new RegExpException(
RegExpException.ErrorType.UNEXPECTED_CHARACTER,
_pos - 1,
_pattern);
}
return c;
}
private int PeekChar(int count)
{
if (_pos + count < _pattern.Length)
{
return _pattern[_pos + count];
}
else
{
return -1;
}
}
}
}

213
Parsing/TokenStringDFA.cs Normal file
View File

@@ -0,0 +1,213 @@
using System.Text;
namespace Flee.Parsing
{
/**
* A deterministic finite state automaton for matching exact strings.
* It uses a sorted binary tree representation of the state
* transitions in order to enable quick matches with a minimal memory
* footprint. It only supports a single character transition between
* states, but may be run in an all case-insensitive mode.
*/
internal class TokenStringDFA
{
private readonly DFAState[] _ascii = new DFAState[128];
private readonly DFAState _nonAscii = new DFAState();
public TokenStringDFA()
{
}
public void AddMatch(string str, bool caseInsensitive, TokenPattern value)
{
DFAState state;
char c = str[0];
int start = 0;
if (caseInsensitive)
{
c = Char.ToLower(c);
}
if (c < 128)
{
state = _ascii[c];
if (state == null)
{
state = _ascii[c] = new DFAState();
}
start++;
}
else
{
state = _nonAscii;
}
for (int i = start; i < str.Length; i++)
{
var next = state.Tree.Find(str[i], caseInsensitive);
if (next == null)
{
next = new DFAState();
state.Tree.Add(str[i], caseInsensitive, next);
}
state = next;
}
state.Value = value;
}
public TokenPattern Match(ReaderBuffer buffer, bool caseInsensitive)
{
TokenPattern result = null;
DFAState state;
int pos = 0;
var c = buffer.Peek(0);
if (c < 0)
{
return null;
}
if (caseInsensitive)
{
c = Char.ToLower((char)c);
}
if (c < 128)
{
state = _ascii[c];
if (state == null)
{
return null;
}
else if (state.Value != null)
{
result = state.Value;
}
pos++;
}
else
{
state = _nonAscii;
}
while ((c = buffer.Peek(pos)) >= 0)
{
state = state.Tree.Find((char)c, caseInsensitive);
if (state == null)
{
break;
}
else if (state.Value != null)
{
result = state.Value;
}
pos++;
}
return result;
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < _ascii.Length; i++)
{
if (_ascii[i] != null)
{
buffer.Append((char)i);
if (_ascii[i].Value != null)
{
buffer.Append(": ");
buffer.Append(_ascii[i].Value);
buffer.Append("\n");
}
_ascii[i].Tree.PrintTo(buffer, " ");
}
}
_nonAscii.Tree.PrintTo(buffer, "");
return buffer.ToString();
}
}
internal class DFAState
{
internal TokenPattern Value;
internal TransitionTree Tree = new TransitionTree();
}
internal class TransitionTree
{
private char _value = '\0';
private DFAState _state;
private TransitionTree _left;
private TransitionTree _right;
public TransitionTree()
{
}
public DFAState Find(char c, bool lowerCase)
{
if (lowerCase)
{
c = Char.ToLower(c);
}
if (_value == '\0' || _value == c)
{
return _state;
}
else if (_value > c)
{
return _left.Find(c, false);
}
else
{
return _right.Find(c, false);
}
}
public void Add(char c, bool lowerCase, DFAState state)
{
if (lowerCase)
{
c = Char.ToLower(c);
}
if (_value == '\0')
{
this._value = c;
this._state = state;
this._left = new TransitionTree();
this._right = new TransitionTree();
}
else if (_value > c)
{
_left.Add(c, false, state);
}
else
{
_right.Add(c, false, state);
}
}
public void PrintTo(StringBuilder buffer, String indent)
{
_left?.PrintTo(buffer, indent);
if (this._value != '\0')
{
if (buffer.Length > 0 && buffer[buffer.Length - 1] == '\n')
{
buffer.Append(indent);
}
buffer.Append(this._value);
if (this._state.Value != null)
{
buffer.Append(": ");
buffer.Append(this._state.Value);
buffer.Append("\n");
}
this._state.Tree.PrintTo(buffer, indent + " ");
}
_right?.PrintTo(buffer, indent);
}
}
}

444
Parsing/Tokenizer.cs Normal file
View File

@@ -0,0 +1,444 @@
using System.Text;
using System.Text.RegularExpressions;
namespace Flee.Parsing
{
/**
* A character stream tokenizer. This class groups the characters read
* from the stream together into tokens ("words"). The grouping is
* controlled by token patterns that contain either a fixed string to
* search for, or a regular expression. If the stream of characters
* don't match any of the token patterns, a parse exception is thrown.
*/
internal class Tokenizer
{
private bool _useTokenList = false;
private readonly StringDFAMatcher _stringDfaMatcher;
private readonly NFAMatcher _nfaMatcher;
private readonly RegExpMatcher _regExpMatcher;
private ReaderBuffer _buffer = null;
private readonly TokenMatch _lastMatch = new TokenMatch();
private Token _previousToken = null;
public Tokenizer(TextReader input)
: this(input, false)
{
}
public Tokenizer(TextReader input, bool ignoreCase)
{
this._stringDfaMatcher = new StringDFAMatcher(ignoreCase);
this._nfaMatcher = new NFAMatcher(ignoreCase);
this._regExpMatcher = new RegExpMatcher(ignoreCase);
this._buffer = new ReaderBuffer(input);
}
public bool UseTokenList
{
get
{
return _useTokenList;
}
set
{
_useTokenList = value;
}
}
public bool GetUseTokenList()
{
return _useTokenList;
}
public void SetUseTokenList(bool useTokenList)
{
this._useTokenList = useTokenList;
}
public string GetPatternDescription(int id)
{
var pattern = _stringDfaMatcher.GetPattern(id);
if (pattern == null)
{
pattern = _nfaMatcher.GetPattern(id);
}
if (pattern == null)
{
pattern = _regExpMatcher.GetPattern(id);
}
return pattern?.ToShortString();
}
public int GetCurrentLine()
{
return _buffer.LineNumber;
}
public int GetCurrentColumn()
{
return _buffer.ColumnNumber;
}
/**
* nfa - true to attempt as an nfa pattern for regexp. This handles most things except the complex repeates, ie {1,4}
*/
public void AddPattern(TokenPattern pattern, bool nfa=true)
{
switch (pattern.Type)
{
case TokenPattern.PatternType.STRING:
try
{
_stringDfaMatcher.AddPattern(pattern);
}
catch (Exception e)
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_TOKEN,
pattern.Name,
"error adding string token: " +
e.Message);
}
break;
case TokenPattern.PatternType.REGEXP:
if (nfa)
{
try
{
_nfaMatcher.AddPattern(pattern);
}
catch (Exception)
{
nfa = false;
}
}
if (!nfa)
{
try
{
_regExpMatcher.AddPattern(pattern);
}
catch (Exception e)
{
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_TOKEN,
pattern.Name,
"regular expression contains error(s): " +
e.Message);
}
}
break;
default:
throw new ParserCreationException(
ParserCreationException.ErrorType.INVALID_TOKEN,
pattern.Name,
"pattern type " + pattern.Type +
" is undefined");
}
}
public void Reset(TextReader input)
{
//this.buffer.Dispose();
this._buffer = new ReaderBuffer(input);
this._previousToken = null;
this._lastMatch.Clear();
}
public Token Next()
{
Token token = null;
do
{
token = NextToken();
if (token == null)
{
_previousToken = null;
return null;
}
if (_useTokenList)
{
token.Previous = _previousToken;
_previousToken = token;
}
if (token.Pattern.Ignore)
{
token = null;
}
else if (token.Pattern.Error)
{
throw new ParseException(
ParseException.ErrorType.INVALID_TOKEN,
token.Pattern.ErrorMessage,
token.StartLine,
token.StartColumn);
}
} while (token == null);
return token;
}
private Token NextToken()
{
try
{
_lastMatch.Clear();
_stringDfaMatcher.Match(_buffer, _lastMatch);
_nfaMatcher.Match(_buffer, _lastMatch);
_regExpMatcher.Match(_buffer, _lastMatch);
int line;
int column;
if (_lastMatch.Length > 0)
{
line = _buffer.LineNumber;
column = _buffer.ColumnNumber;
var str = _buffer.Read(_lastMatch.Length);
return NewToken(_lastMatch.Pattern, str, line, column);
}
else if (_buffer.Peek(0) < 0)
{
return null;
}
else
{
line = _buffer.LineNumber;
column = _buffer.ColumnNumber;
throw new ParseException(
ParseException.ErrorType.UNEXPECTED_CHAR,
_buffer.Read(1),
line,
column);
}
}
catch (IOException e)
{
throw new ParseException(ParseException.ErrorType.IO,
e.Message,
-1,
-1);
}
}
protected virtual Token NewToken(TokenPattern pattern,
string image,
int line,
int column)
{
return new Token(pattern, image, line, column);
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
buffer.Append(_stringDfaMatcher);
buffer.Append(_nfaMatcher);
buffer.Append(_regExpMatcher);
return buffer.ToString();
}
}
internal abstract class TokenMatcher
{
protected TokenPattern[] Patterns = new TokenPattern[0];
protected bool IgnoreCase = false;
protected TokenMatcher(bool ignoreCase)
{
IgnoreCase = ignoreCase;
}
public abstract void Match(ReaderBuffer buffer, TokenMatch match);
public TokenPattern GetPattern(int id)
{
for (int i = 0; i < Patterns.Length; i++)
{
if (Patterns[i].Id == id)
{
return Patterns[i];
}
}
return null;
}
public virtual void AddPattern(TokenPattern pattern)
{
Array.Resize(ref Patterns, Patterns.Length + 1);
Patterns[Patterns.Length - 1] = pattern;
}
public override string ToString()
{
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < Patterns.Length; i++)
{
buffer.Append(Patterns[i]);
buffer.Append("\n\n");
}
return buffer.ToString();
}
}
internal class StringDFAMatcher : TokenMatcher
{
private readonly TokenStringDFA _automaton = new TokenStringDFA();
public StringDFAMatcher(bool ignoreCase) : base(ignoreCase)
{
}
public override void AddPattern(TokenPattern pattern)
{
_automaton.AddMatch(pattern.Pattern, IgnoreCase, pattern);
base.AddPattern(pattern);
}
public override void Match(ReaderBuffer buffer, TokenMatch match)
{
TokenPattern res = _automaton.Match(buffer, IgnoreCase);
if (res != null)
{
match.Update(res.Pattern.Length, res);
}
}
}
internal class NFAMatcher : TokenMatcher
{
private readonly TokenNFA _automaton = new TokenNFA();
public NFAMatcher(bool ignoreCase) : base(ignoreCase)
{
}
public override void AddPattern(TokenPattern pattern)
{
if (pattern.Type == TokenPattern.PatternType.STRING)
{
_automaton.AddTextMatch(pattern.Pattern, IgnoreCase, pattern);
}
else
{
_automaton.AddRegExpMatch(pattern.Pattern, IgnoreCase, pattern);
}
base.AddPattern(pattern);
}
public override void Match(ReaderBuffer buffer, TokenMatch match)
{
_automaton.Match(buffer, match);
}
}
internal class RegExpMatcher : TokenMatcher
{
private REHandler[] _regExps = new REHandler[0];
public RegExpMatcher(bool ignoreCase) : base(ignoreCase)
{
}
public override void AddPattern(TokenPattern pattern)
{
REHandler re;
try
{
re = new GrammaticaRE(pattern.Pattern, IgnoreCase);
pattern.DebugInfo = "Grammatica regexp\n" + re;
}
catch (Exception)
{
re = new SystemRE(pattern.Pattern, IgnoreCase);
pattern.DebugInfo = "native .NET regexp";
}
Array.Resize(ref _regExps, _regExps.Length + 1);
_regExps[_regExps.Length - 1] = re;
base.AddPattern(pattern);
}
public override void Match(ReaderBuffer buffer, TokenMatch match)
{
for (int i = 0; i < _regExps.Length; i++)
{
int length = _regExps[i].Match(buffer);
if (length > 0)
{
match.Update(length, Patterns[i]);
}
}
}
}
internal abstract class REHandler
{
public abstract int Match(ReaderBuffer buffer);
}
internal class GrammaticaRE : REHandler
{
private readonly RegExp _regExp;
private Matcher _matcher = null;
public GrammaticaRE(string regex, bool ignoreCase)
{
_regExp = new RegExp(regex, ignoreCase);
}
public override int Match(ReaderBuffer buffer)
{
if (_matcher == null)
{
_matcher = _regExp.Matcher(buffer);
}
else
{
_matcher.Reset(buffer);
}
return _matcher.MatchFromBeginning() ? _matcher.Length() : 0;
}
}
internal class SystemRE : REHandler
{
private readonly Regex _reg;
public SystemRE(string regex, bool ignoreCase)
{
if (ignoreCase)
{
_reg = new Regex(regex, RegexOptions.IgnoreCase);
}
else
{
_reg = new Regex(regex);
}
}
public override int Match(ReaderBuffer buffer)
{
Match m;
// Ugly hack since .NET doesn't have a flag for when the
// end of the input string was encountered...
buffer.Peek(1024 * 16);
// Also, there is no API to limit the search to the specified
// position, so we double-check the index afterwards instead.
m = _reg.Match(buffer.ToString(), buffer.Position);
if (m.Success && m.Index == buffer.Position)
{
return m.Length;
}
else
{
return 0;
}
}
}
}

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

48
README.md Normal file
View File

@@ -0,0 +1,48 @@
# Flee (Supports Net6.0, Net5.0, Netstandard2.1, Netstandard2.0)
Fast Lightweight Expression Evaluator.
Convert this project vb.net to c#.
## Project Description
Flee is an expression parser and evaluator for the .NET framework. It allows you to compute the value of string expressions such as sqrt(a^2 + b^2) at runtime. It uses a custom compiler, strongly-typed expression language, and lightweight codegen to compile expressions directly to IL. This means that expression evaluation is extremely fast and efficient.
## Features
* Fast and efficient expression evaluation
* Small, lightweight library
* Compiles expressions to IL using a custom compiler, lightweight codegen, and the DynamicMethod class
* Expressions (and the IL generated for them) are garbage-collected when no longer used
* Does not create any dynamic assemblies that stay in memory
* Backed by a comprehensive suite of unit tests
* Culture-sensitive decimal point
* Fine-grained control of what types an expression can use
* Supports all arithmetic operations including the power (^) operator
* Supports string, char, boolean, and floating-point literals
* Supports 32/64 bit, signed/unsigned, and hex integer literals
* Features a true conditional operator
* Supports short-circuited logical operations
* Supports arithmetic, comparison, implicit, and explicit overloaded operators
* Variables of any type can be dynamically defined and used in expressions
* CalculationEngine: Reference other expressions in an expression and recalculate in natural order
* Expressions can index arrays and collections, access fields and properties, and call functions on various types
* Generated IL can be saved to an assembly and viewed with a disassembler
### Installing Flee
You should install [Flee with NuGet](https://www.nuget.org/packages/Flee):
Install-Package Flee
Or via the .NET Core command line interface:
dotnet add package Flee
## NuGet Packages
| Name | NuGet |
| :--- | :--- |
| [Flee](https://www.nuget.org/packages/Flee) | [![Flee](https://img.shields.io/badge/nuget-v2.0.0-blue.svg)](https://www.nuget.org/packages/Flee)
## More information
* [Examples](https://github.com/mparlak/Flee/wiki/Examples) to learn how to create and evaluate expressions.
## License
Flee is licensed under the LGPL. This means that as long as you dynamically link (ie: add a reference) to the officially released assemblies, you can use it in commercial and non-commercial applications.