Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DynamicCallDispatcher.cs
Go to the documentation of this file.
1 #region License
2 /* **********************************************************************************
3  * Copyright (c) Roman Ivantsov
4  * This source code is subject to terms and conditions of the MIT License
5  * for Irony. A copy of the license can be found in the License.txt file
6  * at the root of this distribution.
7  * By using this source code in any fashion, you are agreeing to be bound by the terms of the
8  * MIT License.
9  * You must not remove this notice from this software.
10  * **********************************************************************************/
11 #endregion
12 
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using System.Text;
17 using Irony.Parsing;
18 
19 namespace Irony.Interpreter {
20 
21  #region OperatorDispatchKey class
22  /// <summary>
23  /// The struct is used as a key for the dictionary of operator implementations.
24  /// Contains types of arguments for a method or operator implementation.
25  /// </summary>
26  public struct OperatorDispatchKey : IEquatable<OperatorDispatchKey> {
27  public string OpSymbol;
28  public Type Arg1Type;
29  public Type Arg2Type;
30  public int HashCode;
31  private OperatorDispatchKey(string opSymbol, Type arg1Type, Type arg2Type) {
32  OpSymbol = opSymbol;
33  Arg1Type = arg1Type;
34  Arg2Type = arg2Type;
35  int h1 = (arg1Type == null ? 0 : arg1Type.GetHashCode());
36  int h2 = (arg2Type == null ? 0 : arg2Type.GetHashCode());
37  //shift is for assymetry
38  HashCode = unchecked((h1 << 1) + h2 + opSymbol.GetHashCode());
39  }//OpKey
40 
41  public override int GetHashCode() {
42  return HashCode;
43  }
44  public override string ToString() {
45  return "(" + Arg1Type + " " + OpSymbol + " " + Arg2Type + ")";
46  }
47 
48  public static OperatorDispatchKey CreateFromTypes(string opSymbol, Type arg1Type, Type arg2Type) {
49  return new OperatorDispatchKey(opSymbol, arg1Type, arg2Type);
50  }
51  public static OperatorDispatchKey CreateFromArgs(string opSymbol, object arg1, object arg2) {
52  return new OperatorDispatchKey(opSymbol, (arg1 == null ? null : arg1.GetType()), (arg2 == null ? null : arg2.GetType()));
53  }
54  public static OperatorDispatchKey CreateForTypeConverter(Type fromType, Type toType) {
55  return new OperatorDispatchKey(string.Empty, fromType, toType);
56  }
57 
58  #region IEquatable<DispatchKey> Members
59  public bool Equals(OperatorDispatchKey other) {
60  return HashCode == other.HashCode && OpSymbol == other.OpSymbol && Arg1Type == other.Arg1Type && Arg2Type == other.Arg2Type;
61  }
62  #endregion
63  }//class
64  #endregion
65 
66  #region TypeConverter
67  public delegate object TypeConverter(object arg);
68  public class TypeConverterTable : Dictionary<OperatorDispatchKey, TypeConverter> {
69  public void Add(Type fromType, Type toType, TypeConverter converter) {
70  OperatorDispatchKey key = OperatorDispatchKey.CreateForTypeConverter(fromType, toType);
71  this[key] = converter;
72  }
73  public TypeConverter Find(Type fromType, Type toType) {
74  OperatorDispatchKey key = OperatorDispatchKey.CreateForTypeConverter(fromType, toType);
75  TypeConverter result;
76  TryGetValue(key, out result);
77  return result;
78  }
79  }
80  #endregion
81 
82  #region OperatorImplementation
83  public delegate object BinaryOperatorMethod(object arg1, object arg2);
84  public delegate object UnaryOperatorMethod(object arg1, object arg2);
85 
86  ///<summary>
87  ///The OperatorImplementation class represents an implementation of an operator or method with specific argument types.
88  ///</summary>
89  ///<remarks>
90  /// The OperatorImplementation holds 4 method execution components, which are simply delegate references:
91  /// converters for both arguments, implementation method and converter for the result.
92  ///</remarks>
93  public sealed class OperatorImplementation {
94  public readonly OperatorDispatchKey Key;
95  // The type to which arguments are converted and no-conversion method for this type.
96  public readonly Type BaseType;
98  //converters
99  internal TypeConverter Arg1Converter;
100  internal TypeConverter Arg2Converter;
101  internal TypeConverter ResultConverter;
102  //A reference to the actual method - one of EvaluateConvXXX
104 
105  public OperatorImplementation(OperatorDispatchKey key, Type baseType, BinaryOperatorMethod baseMethod,
106  TypeConverter arg1Converter, TypeConverter arg2Converter, TypeConverter resultConverter) {
107  Key = key;
108  BaseType = baseType;
109  Arg1Converter = arg1Converter;
110  Arg2Converter = arg2Converter;
111  ResultConverter = resultConverter;
112  BaseMethod = baseMethod;
113  SetupEvaluationMethod();
114  }
115 
116  public void SetupEvaluationMethod() {
117  if (ResultConverter == null) {
118  //without ResultConverter
119  if (Arg1Converter == null && Arg2Converter == null)
120  Evaluate = EvaluateConvNone;
121  else if (Arg1Converter != null && Arg2Converter == null)
122  Evaluate = EvaluateConvLeft;
123  else if (Arg1Converter == null && Arg2Converter != null)
124  Evaluate = EvaluateConvRight;
125  else // if (Arg1Converter != null && arg2Converter != null)
126  Evaluate = EvaluateConvBoth;
127  } else {
128  //with result converter
129  if (Arg1Converter == null && Arg2Converter == null)
130  Evaluate = EvaluateConvNoneConvResult;
131  else if (Arg1Converter != null && Arg2Converter == null)
132  Evaluate = EvaluateConvLeftConvResult;
133  else if (Arg1Converter == null && Arg2Converter != null)
134  Evaluate = EvaluateConvRightConvResult;
135  else // if (Arg1Converter != null && Arg2Converter != null)
136  Evaluate = EvaluateConvBothConvResult;
137  }
138  }
139 
140  private object EvaluateConvNone(object arg1, object arg2) {
141  return BaseMethod(arg1, arg2);
142  }
143  private object EvaluateConvLeft(object arg1, object arg2) {
144  return BaseMethod(Arg1Converter(arg1), arg2);
145  }
146  private object EvaluateConvRight(object arg1, object arg2) {
147  return BaseMethod(arg1, Arg2Converter(arg2));
148  }
149  private object EvaluateConvBoth(object arg1, object arg2) {
150  return BaseMethod(Arg1Converter(arg1), Arg2Converter(arg2));
151  }
152 
153  private object EvaluateConvNoneConvResult(object arg1, object arg2) {
154  return ResultConverter(BaseMethod(arg1, arg2));
155  }
156  private object EvaluateConvLeftConvResult(object arg1, object arg2) {
157  return ResultConverter(BaseMethod(Arg1Converter(arg1), arg2));
158  }
159  private object EvaluateConvRightConvResult(object arg1, object arg2) {
160  return ResultConverter(BaseMethod(arg1, Arg2Converter(arg2)));
161  }
162  private object EvaluateConvBothConvResult(object arg1, object arg2) {
163  return ResultConverter(BaseMethod(Arg1Converter(arg1), Arg2Converter(arg2)));
164  }
165  }//class
166 
167  public class OperatorImplementationTable : Dictionary<OperatorDispatchKey, OperatorImplementation> { }
168  #endregion
169 
170  #region OperatorDispatcher
171  /// <summary>
172  /// The DynamicCallDispatcher class is responsible for fast dispatching to the implementation based on argument types
173  /// It is one per context which is one per thread.
174  /// </summary>
175  public class DynamicCallDispatcher {
176  EvaluationContext _context;
177  LanguageRuntime _runtime;
179 
181  _context = context;
182  _runtime = _context.Runtime;
183  OperatorImplementations = _runtime.CreateOperatorImplementationsTable();
184  }
185 
186  public void ExecuteBinaryOperator(string op) {
187  var arg1 = _context.Data[1];
188  var arg2 = _context.Data[0];
189  var key = OperatorDispatchKey.CreateFromArgs(op, arg1, arg2);
190  OperatorImplementation opImpl;
191  if (!OperatorImplementations.TryGetValue(key, out opImpl))
192  opImpl = _runtime.AddOperatorImplementation(OperatorImplementations, key);
193  if (opImpl != null) {
194  try {
195  var result = opImpl.Evaluate(arg1, arg2);
196  _context.Data.Replace(2, result);
197  return;
198  } catch (OverflowException) {
199  if (TryConvertArgsOnOverflow(opImpl.BaseType)) {
200  ExecuteBinaryOperator(op); //call self recursively, now with new arg types
201  return;
202  }
203  throw;
204  }//catch
205  }//if
206 
207  //Treating as normal call - first comes implementor (arg1), then argument (ag2); something like:
208  // a + b => a._add(b)
209  //ExecuteMethod(arg1, op, 1);
210  _context.ThrowError(Resources.ErrOpNotImplemented, op);
211  }//method
212 
213  private bool TryConvertArgsOnOverflow(Type baseType) {
214  //get the up-type
215  Type upType = _runtime.GetUpType(baseType);
216  if (upType == null)
217  return false;
218  var arg2 = _context.Data.Pop();
219  var arg1 = _context.Data.Pop();
220  var arg1Conv = ConvertValue(arg1, upType);
221  var arg2Conv = ConvertValue(arg2, upType);
222  _context.Data.Push(arg1Conv);
223  _context.Data.Push(arg2Conv);
224  return true;
225  }
226 
227  private object ConvertValue(object value, Type toType) {
228  var key = OperatorDispatchKey.CreateForTypeConverter(value.GetType(), toType);
229  TypeConverter converter;
230  if (_runtime.TypeConverters.TryGetValue(key, out converter)) {
231  var result = converter.Invoke(value);
232  return result;
233  }
234  throw new Exception(string.Format("Failed to convert value '%1' to type %2.", value, toType));
235  }
236 
237  }//class
238  #endregion
239 
240 }//namespace
The struct is used as a key for the dictionary of operator implementations. Contains types of argumen...
delegate object UnaryOperatorMethod(object arg1, object arg2)
DynamicCallDispatcher(EvaluationContext context)
delegate object BinaryOperatorMethod(object arg1, object arg2)
static OperatorDispatchKey CreateFromTypes(string opSymbol, Type arg1Type, Type arg2Type)
The DynamicCallDispatcher class is responsible for fast dispatching to the implementation based on ar...
void Add(Type fromType, Type toType, TypeConverter converter)
static OperatorDispatchKey CreateForTypeConverter(Type fromType, Type toType)
readonly OperatorImplementationTable OperatorImplementations
bool Equals(OperatorDispatchKey other)
delegate object TypeConverter(object arg)
TypeConverter Find(Type fromType, Type toType)
OperatorImplementation(OperatorDispatchKey key, Type baseType, BinaryOperatorMethod baseMethod, TypeConverter arg1Converter, TypeConverter arg2Converter, TypeConverter resultConverter)
readonly BinaryOperatorMethod BaseMethod
static OperatorDispatchKey CreateFromArgs(string opSymbol, object arg1, object arg2)
static string ErrOpNotImplemented
Looks up a localized string similar to Operator '{0} not imlemented..