/Clojure/Clojure/CljCompiler/Ast/NumericConvertBinder.cs

https://github.com/abimaran/clojure-clr · C# · 453 lines · 326 code · 71 blank · 56 comment · 76 complexity · 6e762a237fa01a3bdb03e881ccd8bdee MD5 · raw file

  1. /**
  2. * Copyright (c) Rich Hickey. All rights reserved.
  3. * The use and distribution terms for this software are covered by the
  4. * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
  5. * which can be found in the file epl-v10.html at the root of this distribution.
  6. * By using this software in any fashion, you are agreeing to be bound by
  7. * the terms of this license.
  8. * You must not remove this notice, or any other, from this software.
  9. **/
  10. /**
  11. * Author: David Miller
  12. **/
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. #if CLR2
  17. using Microsoft.Scripting.Ast;
  18. #else
  19. using System.Linq.Expressions;
  20. #endif
  21. using Microsoft.Scripting.Actions;
  22. using Microsoft.Scripting.Actions.Calls;
  23. using Microsoft.Scripting.Utils;
  24. using Microsoft.Scripting.Generation;
  25. using System.Dynamic;
  26. using Microsoft.Scripting.Runtime;
  27. namespace clojure.lang.CljCompiler.Ast
  28. {
  29. public class NumericConvertBinder : DefaultBinder
  30. {
  31. // Inspired by the Converter class in IronRuby
  32. // Inspired == close to ripped-off
  33. static readonly NumericConvertBinder _instance = new NumericConvertBinder();
  34. public static NumericConvertBinder Instance { get { return _instance; } }
  35. public override bool CanConvertFrom(Type fromType, Type toType, bool toNotNullable, NarrowingLevel level)
  36. {
  37. //
  38. // NarrowLevel.Zero
  39. //
  40. if (fromType == toType)
  41. return true;
  42. // I don't want to consider boxing before numeric conversion, else I don't get the convert-int-to-long behavior required to select Numbers.lt(long,long) over Numbers.lt(long,Object)
  43. // We also need to get the narrow-long-to-int behavior required to avoid casting in host expression calls.
  44. // IronRuby does this here:
  45. //if (toType.IsAssignableFrom(fromType))
  46. //{
  47. // return true;
  48. //}
  49. if ( !Util.IsPrimitiveNumeric(fromType) && toType.IsAssignableFrom(fromType))
  50. {
  51. return true;
  52. }
  53. //
  54. // NarrowingLevel.One
  55. //
  56. if (level < NarrowingLevel.One)
  57. {
  58. return false;
  59. }
  60. if (WideningIntegerConversion(fromType, toType))
  61. {
  62. return true;
  63. }
  64. //
  65. // NarrowingLevel.Two
  66. //
  67. if (level < NarrowingLevel.Two)
  68. {
  69. return false;
  70. }
  71. if ( SpecialClojureConversion(fromType, toType) )
  72. {
  73. return true;
  74. }
  75. //if (fromType == typeof(char) && toType == typeof(string))
  76. //{
  77. // return true;
  78. //}
  79. if (toType == typeof(bool))
  80. {
  81. return true;
  82. }
  83. //
  84. // NarrowingLevel.Three
  85. //
  86. if (level < NarrowingLevel.Three)
  87. {
  88. return false;
  89. }
  90. if ( Util.IsPrimitiveNumeric(toType) && Util.IsPrimitiveNumeric(fromType) )
  91. {
  92. return true;
  93. }
  94. //
  95. // NarrowingLevel.All
  96. //
  97. if (level < NarrowingLevel.All)
  98. {
  99. return false;
  100. }
  101. // pick up boxing numerics here
  102. if (toType.IsAssignableFrom(fromType))
  103. {
  104. return true;
  105. }
  106. return false;
  107. //if (level == NarrowingLevel.All)
  108. //{
  109. // if (fromType == typeof(long))
  110. // {
  111. // if (toType == typeof(int) || toType == typeof(uint) || toType == typeof(short) || toType == typeof(ushort) || toType == typeof(byte) || toType == typeof(sbyte))
  112. // return true;
  113. // }
  114. // else if (fromType == typeof(double))
  115. // {
  116. // if (toType == typeof(float))
  117. // return true;
  118. // }
  119. //}
  120. //return base.CanConvertFrom(fromType, toType, toNotNullable, level);
  121. }
  122. // From IronRuby. We'll see if it works for our purpose.
  123. public override Candidate PreferConvert(Type t1, Type t2)
  124. {
  125. switch (Type.GetTypeCode(t1)) {
  126. case TypeCode.SByte:
  127. switch (Type.GetTypeCode(t2)) {
  128. case TypeCode.Byte:
  129. case TypeCode.UInt16:
  130. case TypeCode.UInt32:
  131. case TypeCode.UInt64:
  132. return Candidate.Two;
  133. default:
  134. return Candidate.Equivalent;
  135. }
  136. case TypeCode.Int16:
  137. switch (Type.GetTypeCode(t2)) {
  138. case TypeCode.UInt16:
  139. case TypeCode.UInt32:
  140. case TypeCode.UInt64:
  141. return Candidate.Two;
  142. default:
  143. return Candidate.Equivalent;
  144. }
  145. case TypeCode.Int32:
  146. switch (Type.GetTypeCode(t2)) {
  147. case TypeCode.UInt32:
  148. case TypeCode.UInt64:
  149. return Candidate.Two;
  150. default:
  151. return Candidate.Equivalent;
  152. }
  153. case TypeCode.Int64:
  154. switch (Type.GetTypeCode(t2)) {
  155. case TypeCode.UInt64:
  156. return Candidate.Two;
  157. default:
  158. return Candidate.Equivalent;
  159. }
  160. case TypeCode.Boolean:
  161. if (t2 == typeof(int)) {
  162. return Candidate.Two;
  163. }
  164. return Candidate.Equivalent;
  165. case TypeCode.Decimal:
  166. case TypeCode.Double:
  167. if (t2 == typeof(BigInteger)) {
  168. return Candidate.Two;
  169. }
  170. return Candidate.Equivalent;
  171. case TypeCode.Char:
  172. if (t2 == typeof(string)) {
  173. return Candidate.Two;
  174. }
  175. return Candidate.Equivalent;
  176. }
  177. return Candidate.Equivalent;
  178. }
  179. public override object Convert(object obj, Type toType)
  180. {
  181. if (obj is long)
  182. {
  183. long lobj = (long)obj;
  184. if (toType == typeof(long))
  185. return obj;
  186. else if (toType == typeof(int))
  187. return (int)lobj;
  188. else if (toType == typeof(uint))
  189. return (uint)lobj;
  190. else if (toType == typeof(short))
  191. return (uint)lobj;
  192. else if (toType == typeof(byte))
  193. return (byte)lobj;
  194. else if (toType == typeof(sbyte))
  195. return (sbyte)lobj;
  196. }
  197. else if (obj is double)
  198. {
  199. double d = (double)obj;
  200. if (toType == typeof(float))
  201. return (float)d;
  202. }
  203. return base.Convert(obj, toType);
  204. }
  205. #region Numeric conversion calculations
  206. internal static bool WideningIntegerConversion(Type fromType, Type toType)
  207. {
  208. TypeCode fromTC = Type.GetTypeCode(fromType);
  209. TypeCode toTC = Type.GetTypeCode(toType);
  210. bool toBigInt = toType == typeof(BigInt) || toType == typeof(BigInteger);
  211. switch (fromTC)
  212. {
  213. case TypeCode.Char:
  214. switch (toTC)
  215. {
  216. case TypeCode.Char:
  217. case TypeCode.UInt16:
  218. case TypeCode.UInt32:
  219. case TypeCode.UInt64:
  220. case TypeCode.Int32:
  221. case TypeCode.Int64:
  222. return true;
  223. case TypeCode.Object:
  224. return toBigInt;
  225. default:
  226. return false;
  227. }
  228. case TypeCode.Byte:
  229. switch (toTC)
  230. {
  231. case TypeCode.Byte:
  232. case TypeCode.UInt16:
  233. case TypeCode.UInt32:
  234. case TypeCode.UInt64:
  235. case TypeCode.Int32:
  236. case TypeCode.Int64:
  237. return true;
  238. case TypeCode.Object:
  239. return toBigInt;
  240. default:
  241. return false;
  242. }
  243. case TypeCode.UInt16:
  244. switch (toTC)
  245. {
  246. case TypeCode.UInt16:
  247. case TypeCode.UInt32:
  248. case TypeCode.UInt64:
  249. case TypeCode.Int32:
  250. case TypeCode.Int64:
  251. return true;
  252. case TypeCode.Object:
  253. return toBigInt;
  254. default:
  255. return false;
  256. }
  257. case TypeCode.UInt32:
  258. switch (toTC)
  259. {
  260. case TypeCode.UInt32:
  261. case TypeCode.UInt64:
  262. case TypeCode.Int64:
  263. return true;
  264. case TypeCode.Object:
  265. return toBigInt;
  266. default:
  267. return false;
  268. }
  269. case TypeCode.UInt64:
  270. switch (toTC)
  271. {
  272. case TypeCode.UInt64:
  273. return true;
  274. case TypeCode.Object:
  275. return toBigInt;
  276. default:
  277. return false;
  278. }
  279. case TypeCode.SByte:
  280. switch (toTC)
  281. {
  282. case TypeCode.SByte:
  283. case TypeCode.Int16:
  284. case TypeCode.Int32:
  285. case TypeCode.Int64:
  286. return true;
  287. case TypeCode.Object:
  288. return toBigInt;
  289. default:
  290. return false;
  291. }
  292. case TypeCode.Int16:
  293. switch (toTC)
  294. {
  295. case TypeCode.Int16:
  296. case TypeCode.Int32:
  297. case TypeCode.Int64:
  298. return true;
  299. case TypeCode.Object:
  300. return toBigInt;
  301. default:
  302. return false;
  303. }
  304. case TypeCode.Int32:
  305. switch (toTC)
  306. {
  307. case TypeCode.Int32:
  308. case TypeCode.Int64:
  309. return true;
  310. case TypeCode.Object:
  311. return toBigInt;
  312. default:
  313. return false;
  314. }
  315. case TypeCode.Int64:
  316. switch (toTC)
  317. {
  318. case TypeCode.Int64:
  319. return true;
  320. case TypeCode.Object:
  321. return toBigInt;
  322. default:
  323. return false;
  324. }
  325. case TypeCode.Object:
  326. if (fromType == typeof(BigInt) || fromType == typeof(BigInteger))
  327. return toBigInt;
  328. else
  329. return false;
  330. default:
  331. return false;
  332. }
  333. }
  334. static bool SpecialClojureConversion(Type fromType, Type toType)
  335. {
  336. if (fromType == typeof(long))
  337. {
  338. if (toType.IsEnum)
  339. return false; // this can be handled later.
  340. switch( Type.GetTypeCode(toType))
  341. {
  342. case TypeCode.Byte:
  343. case TypeCode.SByte:
  344. case TypeCode.Int16:
  345. case TypeCode.Int32:
  346. case TypeCode.Int64:
  347. case TypeCode.Char:
  348. case TypeCode.UInt16:
  349. case TypeCode.UInt32:
  350. case TypeCode.UInt64:
  351. return true;
  352. case TypeCode.Object:
  353. return toType == typeof(BigInt) || toType == typeof(BigInteger);
  354. default:
  355. return false;
  356. }
  357. }
  358. else if (fromType == typeof(double))
  359. {
  360. if (toType == typeof(float))
  361. return true;
  362. }
  363. return false;
  364. }
  365. #endregion
  366. }
  367. public class NumericConvertOverloadResolverFactory : OverloadResolverFactory
  368. {
  369. static readonly NumericConvertOverloadResolverFactory _instance = new NumericConvertOverloadResolverFactory(NumericConvertBinder.Instance);
  370. public static NumericConvertOverloadResolverFactory Instance { get { return _instance; } }
  371. private readonly DefaultBinder _binder;
  372. public NumericConvertOverloadResolverFactory(DefaultBinder binder)
  373. {
  374. Assert.NotNull(binder);
  375. _binder = binder;
  376. }
  377. public override DefaultOverloadResolver CreateOverloadResolver(IList<DynamicMetaObject> args, CallSignature signature, CallTypes callType)
  378. {
  379. return new DefaultOverloadResolver(_binder, args, signature, callType);
  380. }
  381. }
  382. }