/LINQToTTree/LINQToTTreeLib/Variables/VarUtils.cs

# · C# · 367 lines · 191 code · 40 blank · 136 comment · 69 complexity · debaf34657057816babd788b851ecebb MD5 · raw file

  1. using LinqToTTreeInterfacesLib;
  2. using LINQToTTreeLib.CodeAttributes;
  3. using LINQToTTreeLib.Expressions;
  4. using LINQToTTreeLib.Utils;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq.Expressions;
  8. using System.Text;
  9. using System.Text.RegularExpressions;
  10. namespace LINQToTTreeLib.Variables
  11. {
  12. public static class VarUtils
  13. {
  14. /// <summary>
  15. /// Make sure it is a solid reference, not a -> and ignore type issues. :-)
  16. ///
  17. /// 1) If the object is an array (like int[]) then:
  18. /// If the array is a member access from some obj, then we assume we need a pointer
  19. /// If the array is off another array, then we assume we don't need a pointer.
  20. ///
  21. /// 2) If it is a class, we assume it needs to be dereferenced.
  22. ///
  23. /// 3) We assume no deref is required.
  24. ///
  25. /// A few consequences of this logic:
  26. /// 1) Any non .NET class doesn't get dereferenced. We are probably pretty safe here
  27. /// 2) Any array doesn't get dereferenced unless it is coming from a root object. So if you call a function that returns a pointer
  28. /// to an array then you'll get the wrong thing.
  29. /// 3) Any object that is part of a class will get dereferenced, even if it doesn't need it. This can cause problems
  30. /// when there is a sub-object that is completely contained.
  31. /// </summary>
  32. /// <param name="val"></param>
  33. /// <returns></returns>
  34. public static string AsObjectReference(this IValue val, Expression destExpression = null)
  35. {
  36. StringBuilder bld = new StringBuilder();
  37. //
  38. // If it isn't a pointer type (that is, a class) then
  39. // this is easy
  40. //
  41. if (!val.Type.IsPointerType())
  42. return val.RawValue;
  43. //
  44. // If this is a "null" reference, then just return it.
  45. //
  46. if (val.RawValue == "0")
  47. return val.RawValue;
  48. //
  49. // We are adding a member de-referencing. If we know something about the thing we are de-referencing,
  50. // then use that in deciding if in C++ this is a pointer or not. The basic problem is that C# doesn't tell
  51. // the difference between a pointer or an object - so we have to infer it.
  52. //
  53. bool isObject = true;
  54. if (destExpression != null)
  55. {
  56. if (destExpression.NodeType == ExpressionType.MemberAccess
  57. && (destExpression as MemberExpression).Member.TypeHasAttribute<NotAPointerAttribute>() != null)
  58. {
  59. // We are looking at a.b.c, and c is declared as not being a pointer. So we don't do a pointer de-ref.
  60. isObject = false;
  61. }
  62. else if (val.Type.IsArray)
  63. {
  64. if (destExpression.NodeType == ExpressionType.ArrayIndex)
  65. {
  66. //
  67. // Dealing with the proper array acces is complex:
  68. // vector<vector<int>> stuff; -> stuff[0] -> object should be false.
  69. // vector<vector<TLorentzVector>> stuff; -> stuff[0] -> object should be false.
  70. // TClonesArray vector<int> stuff[] -> stuff[0] -> object should be true!
  71. // int[] parameter -> not object!
  72. //
  73. var rootMember = destExpression.RemoveArrayReferences();
  74. isObject = false;
  75. if (rootMember.NodeType == ExpressionType.MemberAccess)
  76. {
  77. var rootType = (rootMember as MemberExpression).Expression.Type;
  78. isObject = rootType.TypeHasAttribute<TClonesArrayImpliedClassAttribute>() != null;
  79. }
  80. }
  81. else if (destExpression.NodeType == ExpressionType.Parameter)
  82. {
  83. // A parameter inserted by our own code.
  84. isObject = false;
  85. }
  86. else if (destExpression is DeclarableParameter)
  87. {
  88. // Same wiht a declarable parameter - don't defreference it.
  89. isObject = false;
  90. }
  91. }
  92. }
  93. ///
  94. /// Now, return our result!
  95. ///
  96. if (isObject)
  97. {
  98. bld.AppendFormat("(*{0})", val.RawValue);
  99. return bld.ToString();
  100. }
  101. else
  102. {
  103. return val.RawValue;
  104. }
  105. }
  106. /// <summary>
  107. /// Takes the value, out puts it, and casts it to the type destType. We use heuristics at the moment to figure out what
  108. /// the reference is. We are forced to do this at the moment because in C++ there are points and also references to objects,
  109. /// a distinction not made in .NET...
  110. ///
  111. /// 1) If the object is an array (like int[]) then:
  112. /// If the array is a member access from some obj, then we assume we need a pointer
  113. /// If the array is off another array, then we assume we don't need a pointer.
  114. ///
  115. /// 2) If it is a class, we assume it needs to be dereferenced.
  116. ///
  117. /// 3) We assume no deref is required.
  118. ///
  119. /// A few consequences of this logic:
  120. /// 1) Any non .NET class doesn't get dereferenced. We are probably pretty safe here
  121. /// 2) Any array doesn't get dereferenced unless it is coming from a root object. So if you call a function that returns a pointer
  122. /// to an array then you'll get the wrong thing.
  123. /// 3) Any object that is part of a class will get dereferenced, even if it doesn't need it. This can cause problems
  124. /// when there is a sub-object that is completely contained.
  125. ///
  126. /// </summary>
  127. /// <param name="sourceValue"></param>
  128. /// <param name="expressionDestType"></param>
  129. /// <returns></returns>
  130. public static string CastToType(this IValue sourceValue, Expression expressionDestType, bool ignorePointer = false)
  131. {
  132. if (sourceValue == null || expressionDestType == null)
  133. throw new ArgumentNullException("Cannot pass ource or dest type/vars as null");
  134. string objRefForm = sourceValue.RawValue;
  135. if (!ignorePointer)
  136. objRefForm = sourceValue.AsObjectReference(expressionDestType);
  137. ///
  138. /// If the type is already there or if the type is an array, then we will do no conversion.
  139. /// Already there: not needed
  140. /// Array: vector or regular C style array - we don't know this deep in the code, so
  141. /// conversion would probably make a mess of things!
  142. ///
  143. if (!RequiresConversion(expressionDestType.Type, sourceValue.Type) || expressionDestType.Type.IsArray)
  144. {
  145. return objRefForm;
  146. }
  147. ///
  148. /// Type conversion required!
  149. ///
  150. StringBuilder bld = new StringBuilder();
  151. bld.AppendFormat("(({0}){1})", expressionDestType.Type.AsCPPType(), objRefForm);
  152. return bld.ToString();
  153. }
  154. /// <summary>
  155. /// Determine if a conversion is required.
  156. /// </summary>
  157. /// <param name="destType"></param>
  158. /// <param name="type"></param>
  159. /// <returns></returns>
  160. private static bool RequiresConversion(Type destType, Type sourceType)
  161. {
  162. if (destType == sourceType)
  163. return false;
  164. /// If the dest is double and the source if float, or int or something like that, ignore it
  165. if (destType == typeof(double))
  166. {
  167. if (sourceType == typeof(float))
  168. return false;
  169. }
  170. /// Oh well. convert!
  171. return true;
  172. }
  173. /// <summary>
  174. /// Track a term search string.
  175. /// </summary>
  176. static Regex singleTerm = new Regex(@"^\w+$");
  177. /// <summary>
  178. /// Returns true if this is ValSimple is simple (like aInt32_1) vs complex (v.Phi()).
  179. /// </summary>
  180. /// <param name="v"></param>
  181. /// <returns></returns>
  182. internal static bool IsSimpleTerm(this IValue v)
  183. {
  184. return singleTerm.Match(v.RawValue).Success;
  185. }
  186. /// <summary>
  187. /// Returns the type as a CPP type
  188. /// </summary>
  189. /// <param name="t"></param>
  190. /// <returns></returns>
  191. public static string AsCPPType(this Type t)
  192. {
  193. if (t == null)
  194. throw new ArgumentNullException("Type must not be null");
  195. //
  196. // Simple type or array?
  197. //
  198. if (t.IsArray)
  199. {
  200. return string.Format("vector<{0}>", t.GetElementType().AsCPPType());
  201. }
  202. else if (t.IsGenericType)
  203. {
  204. var genericDef = t.GetGenericTypeDefinition();
  205. if (genericDef.Name == "Dictionary`2")
  206. {
  207. var tlist = t.GetGenericArguments();
  208. return string.Format("map<{0}, {1} >", tlist[0].AsCPPType(), tlist[1].AsCPPType());
  209. }
  210. else if (genericDef == typeof(IEnumerable<int>).GetGenericTypeDefinition())
  211. {
  212. return string.Format("{0}::const_iterator", t.GetGenericArguments()[0].AsCPPType());
  213. }
  214. else
  215. {
  216. throw new InvalidOperationException(string.Format("Do not know how to convert generic type '{0}' to a C++ type", t.FullName));
  217. }
  218. }
  219. else
  220. {
  221. if (t == typeof(int))
  222. {
  223. return "int";
  224. }
  225. else if (t == typeof(double))
  226. {
  227. return "double";
  228. }
  229. else if (t == typeof(float))
  230. {
  231. return "float";
  232. }
  233. else if (t == typeof(bool))
  234. {
  235. return "bool";
  236. }
  237. ///
  238. /// Is this a ROOT type? then translate it as well!
  239. ///
  240. if (t.FullName.StartsWith("ROOTNET.N"))
  241. {
  242. return t.FullName.Substring(9) + "*";
  243. }
  244. if (t.FullName.StartsWith("ROOTNET.Interface.N"))
  245. {
  246. return t.FullName.Substring(19) + "*";
  247. }
  248. }
  249. ///
  250. /// Ok - if this is an object, for example, the enclosing ntuple object
  251. ///
  252. return t.Name;
  253. }
  254. /// <summary>
  255. /// Returns true if this is a pointer to a class of some sort.
  256. /// </summary>
  257. /// <param name="t"></param>
  258. /// <returns></returns>
  259. public static bool IsPointerType(this Type t)
  260. {
  261. if (t == null)
  262. throw new ArgumentNullException();
  263. return t.IsClass;
  264. }
  265. /// <summary>
  266. /// Figure out if this is a root class or not!
  267. /// </summary>
  268. /// <param name="className"></param>
  269. /// <returns></returns>
  270. public static bool IsROOTClass(this string className)
  271. {
  272. var c = ROOTNET.NTClass.GetClass(className);
  273. return c != null;
  274. }
  275. /// <summary>
  276. /// Returns true if this type is a ROOT class
  277. /// </summary>
  278. /// <param name="t"></param>
  279. /// <returns></returns>
  280. public static bool IsROOTClass(this Type t)
  281. {
  282. if (t == null)
  283. throw new ArgumentNullException("type must not be null");
  284. return t.Name.Substring(1).IsROOTClass();
  285. }
  286. /// <summary>
  287. /// Performs all expression subsitutions known by the code context on the input. It does not
  288. /// touch the input - but returns a new IValue. Types are not tracked.
  289. /// </summary>
  290. /// <param name="input"></param>
  291. /// <param name="cc"></param>
  292. /// <returns></returns>
  293. /// <remarks>
  294. /// This exists because sometimes you need to do this sort of replacement when dealing with complex
  295. /// expressions that don't translated to C# easily. In partiuclar, for example, the iterator expressions
  296. /// that are used to move through maps in the Group By operators. Otherwise, the regular GetExpression would
  297. /// do this just fine.
  298. /// </remarks>
  299. public static IValue PerformAllSubstitutions(this IValue input, ICodeContext cc)
  300. {
  301. var vFinder = new Regex(@"\b(?<vname>[\w]*)\b");
  302. string v = input.RawValue;
  303. bool subDone = true;
  304. int count = 0;
  305. while (subDone)
  306. {
  307. count++;
  308. if (count > 100)
  309. throw new InvalidOperationException(string.Format("Unable to translate '{0}' due to too many sutstitutions.", input.RawValue));
  310. subDone = false;
  311. foreach (Match match in vFinder.Matches(v))
  312. {
  313. var vname = match.Groups["vname"].Value;
  314. var r = cc.GetReplacement(vname);
  315. if (r != null)
  316. {
  317. v = Regex.Replace(v, string.Format(@"\b{0}\b", vname), r.ParameterName());
  318. subDone = true;
  319. }
  320. }
  321. }
  322. if (count == 1)
  323. return input;
  324. return new ValSimple(v, input.Type);
  325. }
  326. }
  327. }