/// /// <summary> /// Adds a temporary identifier of the given type and name and initializes /// it to the given initValue /// </summary> /// /// <param name="type">Type of identifier</param> /// <param name="name">Name of identifier</param> /// <param name="initValue">Initial value of identifer</param> /// public static void AddIdent(Type type, string name, dynamic initValue) { dynamic value = (initValue is CseObject ? initValue.Value : initValue); // TODO: Make CseExceptionType for type assign mismatch if (!TypeExp.TypeAssignMatch(type, value)) { throw new Exception(String.Format("Cannot assign {0} to variable of type {1}", value, type.ToString())); } CseObject ident = new CseObject(value) { CompileTimeType = type, IsLiteral = false, CallMod = CallArgMod.VAL }; /* TODO: Add custom error type and message for duplicate ident names * if (tempIdents.ContainsKey(name)) * throw new CseLogicException(CseLogicExceptionType.??, name); * * TODO: Add keyword (maybe with a prefix) that returns a list of all * currently stored temp vars along with their type and value */ tempIdents.Add(name, ident); }
/// /// <summary> /// Parses array expressions /// </summary> /// /// <param name="rootInstance">Environment where the array is implemented</param> /// <param name="arrayIdent">Name of the array</param> /// <param name="index">Object for the index value</param> /// /// <returns>CseObject containing the array element at the specified index</returns> /// /// <exception cref="CseLogicExceptionType.IDENT_IS_NOT_ARRAY" /> /// <exception cref="CseLogicExceptionType.IDENT_NOT_FOUND" /> /// public static CseObject Parse(CseObject rootInstance, string arrayIdent, CseObject index) { BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; Type rootInstanceType = TypeExp.GetTypeObj(rootInstance.Value); object result = null; FieldInfo fieldInfo = rootInstanceType.GetField(arrayIdent, flags); if (fieldInfo != null) { result = fieldInfo.GetValue(rootInstance.Value); } else { PropertyInfo propertyInfo = rootInstanceType.GetProperty(arrayIdent); if (propertyInfo != null) result = propertyInfo.GetValue(rootInstance.Value, null); else throw new CseLogicException(CseLogicExceptionType.IDENT_NOT_FOUND, arrayIdent); } IDictionary dictionary = result as IDictionary; Array array = result as Array; if (dictionary != null) { return new CseObject(dictionary[index.Value]); } else if (array != null) { return new CseObject(array.GetValue(long.Parse(index.Value.ToString()))); } else { throw new CseLogicException(CseLogicExceptionType.IDENT_IS_NOT_ARRAY, arrayIdent); } }
/// /// <summary> /// Applies numeric affirmation (i.e. unary plus) to numeric values /// </summary> /// /// <param name="obj">The CseObject with the value to affirm</param> /// /// <returns>The CseObject after affirmation</returns> /// /// <exception cref="CseLogicExceptionType.CANT_AFFIRM_NON_NUM" /> /// internal static CseObject Affirm(CseObject obj) { CseObject result = (CseObject)obj.Clone(); dynamic value = obj.Value; double numValue; if (value is string) { throw new CseLogicException(CseLogicExceptionType.CANT_AFFIRM_NON_NUM, value.ToString()); } else if (!double.TryParse(value.ToString(), out numValue)) { MethodInfo mi = value.GetType().GetMethod(OpOverloadNames.UPLUS); if (null != mi) { result.Value = value.GetType().InvokeMember(OpOverloadNames.UPLUS, OpOverloadNames.Flags, null, CsEval.evalEnvironment, new object[] { value }); return(result); } else { throw new CseLogicException(CseLogicExceptionType.CANT_AFFIRM_NON_NUM, value.ToString()); } } return(result); }
/// /// <summary> /// Determines if a boxing conversion exists between the passed object and the type /// </summary> /// /// <param name="fromArg">The object to convert</param> /// <param name="to">The type to attempt to convert the object to.</param> /// /// <returns>True if a boxing conversion exists between the object and the type, false otherwise</returns> /// public static bool IsBoxingConversion(CseObject fromArg, Type to) { Type unwrappedBox = fromArg.ValueType; if (IsNullableType(unwrappedBox)) { unwrappedBox = Nullable.GetUnderlyingType(unwrappedBox); } if (to.Equals(typeof(System.ValueType)) || to.Equals(typeof(object))) { return(true); } else if (CrawlThatShit(to.GetHashCode(), unwrappedBox, new List <int>())) { return(true); } else if (unwrappedBox.IsEnum && to.Equals(typeof(System.Enum))) { return(true); } else { return(false); } }
/// /// <summary> /// Determines if it is possible to coerce the given list of arguments /// so a method signature can be matched. /// </summary> /// /// <param name="args">List of arguments to check</param> /// /// <returns> /// True if exactly one argument is of an integral type, false otherwise. /// Idea is to only coerce integral arguments since floating point arguments /// would lose precision when converted to a narrower type and objects don't /// need it due to inheritance (i.e. they will match the method signature without /// it). If there is more than one integral argument, then the chance that the /// wrong overloaded method is called is too great, and so this method returns /// false meaning it can't safely coerce the integral args. If only one is found, /// and a method can't be matched without coercion, then we can safely narrow /// down that one integral arg without worry about matching the wrong method. /// </returns> /// private static bool CanCoerce(List <CseObject> args) { if (args == null) { return(false); } bool foundIntType = false; bool canCoerce = false; for (int i = 0; i < args.Count; i++) { CseObject arg = args[i]; if (LiteralExp.IsIntType(arg)) { if (!foundIntType) { canCoerce = true; foundIntType = true; } else { canCoerce = false; break; } } } return(canCoerce); }
/// /// <summary> /// Parses literals that contain the exponential 'e' character /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A double value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_DBL" /> /// internal static CseObject ParseEType(string data) { double value; Regex r = new Regex(@"(f|m|d)$", RegexOptions.IgnoreCase); Match m = r.Match(data); string suffix = m.Groups[1].Value; string number = data.Substring(0, data.Length - suffix.Length); if (!double.TryParse(number, out value)) { throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_DBL, data); } CseObject result = null; if (suffix != "") { result = ParseFloatType(value.ToString(), suffix); } else { result = new CseObject(value); } result.IsLiteral = true; return(result); }
/// /// <summary> /// Parses integral-type literals /// </summary> /// /// <param name="data">String containing the literal</param> /// <param name="suffix">Optional type suffix</param> /// /// <returns>An integral-type value or float-type if float-type suffix is supplied</returns> /// /// <exception cref="CseLogicExceptionType.NOT_A_NUM" /> /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_INT_TYPE" /> /// internal static CseObject ParseIntType(string data, string suffix) { CseObject result = null; if (suffix != null && suffix != "") { switch (suffix.ToLower()) { case "f": result = new CseObject(float.Parse(data)); break; case "m": result = new CseObject(decimal.Parse(data)); break; case "d": result = new CseObject(double.Parse(data)); break; case "ul": result = new CseObject(ulong.Parse(data)); break; case "u": result = new CseObject(uint.Parse(data)); break; case "l": result = new CseObject(long.Parse(data)); break; } } else { // TODO: if long cannot parse number, it may mean an overflow/underflow not a NaN problem int intAttempt; long longAttempt; if (int.TryParse(data, out intAttempt)) { result = new CseObject(intAttempt); } else if (long.TryParse(data, out longAttempt)) { result = new CseObject(longAttempt); } else { throw new CseLogicException(CseLogicExceptionType.NOT_A_NUM, data); } } if (result == null) { throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_INT_TYPE, data); } result.IsLiteral = true; return(result); }
/// /// <summary> /// Parses conditional expressions /// </summary> /// /// <param name="leftOp">Left operand</param> /// <param name="rightOp">Right operand</param> /// <param name="type">Conditional operator type</param> /// /// <returns>The result of applying the conditional operator</returns> /// public static CseObject Parse(CseObject leftOp, CseObject rightOp, CondType type) { CseObject obj = new CseObject(null) { IsLiteral = leftOp.IsLiteral && rightOp.IsLiteral }; try { switch (type) { case CondType.EQ: obj.Value = leftOp.Value == rightOp.Value; break; case CondType.NEQ: obj.Value = leftOp.Value != rightOp.Value; break; case CondType.GT: obj.Value = leftOp.Value > rightOp.Value; break; case CondType.GTE: obj.Value = leftOp.Value >= rightOp.Value; break; case CondType.LT: obj.Value = leftOp.Value < rightOp.Value; break; case CondType.LTE: obj.Value = leftOp.Value <= rightOp.Value; break; default: throw new System.NotImplementedException("Not implemented."); } } catch { // TODO: Fill this out! } return obj; }
/// /// <summary> /// Parses object construction expression (expressions using "new") /// </summary> /// /// <param name="instance">The root object</param> /// <param name="typeName">Name of the type to create</param> /// <param name="constructorParams"> /// CseObject array containing arguments to be sent to the constructor. /// Each CseObject is one argument /// </param> /// /// <returns>CseObject containing the new object</returns> /// /// <exception cref="CseLogicExceptionType.UNKNOWN_TYPE" /> /// internal static CseObject Parse(CseObject instance, string typeName, List <CseObject> constructorParams) { BindingFlags flags = BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; Type typeFound = TypeExp.GetType(typeName); CseObject result = null; if (typeFound != null) { if (constructorParams != null && constructorParams.Count > 0) { object[] objArgs = new object[constructorParams.Count]; for (int i = 0; i < constructorParams.Count; i++) { objArgs[i] = constructorParams[i].Value; } result = new CseObject(Activator.CreateInstance(typeFound, flags, null, objArgs, null)); } else { result = new CseObject(Activator.CreateInstance(typeFound, flags, null, null, null)); } } else { throw new CseLogicException(CseLogicExceptionType.UNKNOWN_TYPE, typeName); } return(result); }
/// /// <summary> /// Parses object construction expression (expressions using "new") /// </summary> /// /// <param name="instance">The root object</param> /// <param name="typeName">Name of the type to create</param> /// <param name="constructorParams"> /// CseObject array containing arguments to be sent to the constructor. /// Each CseObject is one argument /// </param> /// /// <returns>CseObject containing the new object</returns> /// /// <exception cref="CseLogicExceptionType.UNKNOWN_TYPE" /> /// internal static CseObject Parse(CseObject instance, string typeName, List<CseObject> constructorParams) { BindingFlags flags = BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; Type typeFound = TypeExp.GetType(typeName); CseObject result = null; if (typeFound != null) { if (constructorParams != null && constructorParams.Count > 0) { object[] objArgs = new object[constructorParams.Count]; for (int i = 0; i < constructorParams.Count; i++) objArgs[i] = constructorParams[i].Value; result = new CseObject(Activator.CreateInstance(typeFound, flags, null, objArgs, null)); } else { result = new CseObject(Activator.CreateInstance(typeFound, flags, null, null, null)); } } else { throw new CseLogicException(CseLogicExceptionType.UNKNOWN_TYPE, typeName); } return result; }
/// /// <summary> /// Used for quick evaluation. /// Use this when the evaluation environment changes often. /// If the environment is consistent, set CsEval.EvalEnvironment /// then call CsEval.Eval(string data). /// </summary> /// /// <param name="evalEnvironment"> /// Provides an evaluation environment for the given expression. /// The stored evaluation environment is temporarily replaced /// when the given statement is evaluated and restored afterwards. /// </param> /// <param name="data">The expression to evaluate</param> /// /// <returns>The return result of evaluating the expression</returns> /// public static object Eval(object evalEnvironment, string data) { CseObject saveDebugInstance = CsEval.evalEnvironment; CsEval.evalEnvironment = new CseObject(evalEnvironment); object result = Eval(data); CsEval.evalEnvironment = saveDebugInstance; return result; }
/// /// <summary> /// Parses ternary expressions /// </summary> /// /// <param name="cond">Conditional to test</param> /// <param name="truePart">Expression to return if condition is true</param> /// <param name="falsePart">Expression to return if condition is false</param> /// /// <returns>truePart if cond is true, falsePart otherwise</returns> /// public static CseObject Ternary(CseObject cond, CseObject truePart, CseObject falsePart) { bool? condBool = cond.Value as bool?; if (condBool != null) return condBool.Value ? truePart : falsePart; else { MethodInfo mi = cond.Value.GetType().GetMethod(OpOverloadNames.TRUE); if (mi != null) return new CseObject(cond.Value.GetType().InvokeMember(OpOverloadNames.TRUE, OpOverloadNames.Flags, null, CsEval.evalEnvironment, new object[] { cond.Value })); else return null; //TODO: throw new logic exception for this } }
/// /// <summary> /// Parses float-type literals /// </summary> /// /// <param name="data">String containing the literal</param> /// <param name="hasSuffix">Indicates whether a suffix is present or not</param> /// /// <returns>A float-type value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE" /> /// <exception cref="CseLogicExceptionType.NOT_A_NUM" /> /// internal static CseObject ParseFloatType(string data, bool hasSuffix) { CseObject result = null; if (hasSuffix) { // Determine the suffix Regex r = new Regex(@"((?:\d|\.)+)([A-Za-z]{1,2})$"); Match m = r.Match(data); data = m.Groups[1].Value; string suffix = m.Groups[2].Value; switch (suffix.ToLower()) { case "f": result = new CseObject(float.Parse(data)); break; case "m": result = new CseObject(decimal.Parse(data)); break; case "d": result = new CseObject(double.Parse(data)); break; } } else { double doubleAttempt; if (double.TryParse(data, out doubleAttempt)) { result = new CseObject(doubleAttempt); } else { throw new CseLogicException(CseLogicExceptionType.NOT_A_NUM, data); } } if (result == null) { throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE, data); } result.IsLiteral = true; return(result); }
/// /// <summary> /// Tests for an implicit enum conversion, given a CseObject and a type. /// </summary> /// /// <param name="data">The object to test converting</param> /// <param name="t">The type to attempt to convert the object to</param> /// /// <returns>True if an implicit enum conversion is valid, false otherwise</returns> /// public static bool ImpEnumConv(CseObject data, Type t) { long num = -1; if (data.IsLiteral && t.IsEnum && long.TryParse(data.Value.ToString(), out num)) { if (num == 0) { return(true); } } return(false); }
/// /// <summary> /// Determines whether the xdata's CompileTimeType is an unsigned integral type or not /// </summary> /// /// <param name="xdata">CseObject to examine the CompileTimeType of</param> /// /// <returns>True if xdata's CompileTimeType is an unsigned integral type, false otherwise</returns> /// private static bool IsUnsignedType(CseObject xdata) { List <Type> unsignedTypes = new List <Type> { typeof(byte), typeof(UInt16), typeof(UInt32), typeof(UInt64) }; if (unsignedTypes.Contains(xdata.CompileTimeType)) { return(true); } else { return(false); } }
/// /// <summary> /// Applies numeric negation (i.e. unary minus) to numeric values /// </summary> /// /// <param name="obj">The CseObject with the value to negate</param> /// /// <returns>The CseObject after negation</returns> /// /// <exception cref="CseLogicExceptionType.CANT_NEGATE_NON_NUM" /> /// <exception cref="CseLogicExceptionType.ERROR_NEGATING_VALUE_OF_TYPE" /> /// internal static CseObject Negate(CseObject obj) { CseObject result = (CseObject)obj.Clone(); dynamic value = obj.Value; double numValue; if (value is string) { throw new CseLogicException(CseLogicExceptionType.CANT_NEGATE_NON_NUM, value.ToString()); } else if (!double.TryParse(value.ToString(), out numValue)) { MethodInfo mi = value.GetType().GetMethod(OpOverloadNames.UMINUS); if (null != mi) { result.Value = value.GetType().InvokeMember(OpOverloadNames.UMINUS, OpOverloadNames.Flags, null, CsEval.evalEnvironment, new object[] { value }); return(result); } else { throw new CseLogicException(CseLogicExceptionType.CANT_NEGATE_NON_NUM, value.ToString()); } } numValue *= -1; result.Value = numValue; try { result = CastExp.Parse(CsEval.evalEnvironment, value.GetType().Name, result); } catch { if (value.ToString().ToLower().Contains("e")) { result = LiteralExp.ParseEType(numValue.ToString()); } else if (value.ToString().Contains(".")) { result = LiteralExp.ParseFloatType(numValue.ToString(), null); } else { throw new CseLogicException(CseLogicExceptionType.ERROR_NEGATING_VALUE_OF_TYPE, value.ToString(), value.GetType().Name); } } return(result); }
/// /// <summary> /// Parses char literals /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A char value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_CHAR" /> /// internal static CseObject ParseChar(string data) { data = data.Remove(data.Length - 1, 1).Remove(0, 1); if (data.Length > 1 && !data.StartsWith("\\")) { throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_CHAR, data); } else { ParseEscSeqs(ref data, false); CseObject result = new CseObject(data[0]); result.IsLiteral = true; return(result); } }
/// /// <summary> /// Parses bool literals /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A bool value</returns> /// internal static CseObject ParseBool(string data) { CseObject result = null; if (data.Equals("true")) { result = new CseObject(true); } else { result = new CseObject(false); } result.IsLiteral = true; return(result); }
/// /// <summary> /// Parses identifiers (fields or properties) /// </summary> /// /// <param name="environment">The environment containing the field or property</param> /// <param name="data">The name of the field or property</param> /// /// <returns>An CseObject containing the value of the identifier or containing null if identifier cannot be found</returns> /// /// <exception cref="CseLogicExceptionType.IDENT_NOT_FOUND" /> /// internal static CseObject Parse(CseObject environment, string data) { if (data[0].Equals('@')) data = data.Remove(0, 1); LiteralExp.ParseEscSeqs(ref data, false); CseObject result = null; Type instanceType = TypeExp.GetTypeObj(environment.Value); if (!instanceType.IsEnum) { if (environment == CsEval.EvalEnvironment) { CseObject tempLookup = TempIdentifierExp.Lookup(data); if (tempLookup != null) return tempLookup; } FieldInfo fieldInfo = instanceType.GetField(data, defaultFlags | BindingFlags.GetField); if (fieldInfo != null) { result = new CseObject(fieldInfo.GetValue(environment.Value)); result.CompileTimeType = fieldInfo.FieldType; } else { PropertyInfo propertyInfo = instanceType.GetProperty(data, defaultFlags | BindingFlags.GetProperty); if (propertyInfo != null) { result = new CseObject(propertyInfo.GetValue(environment.Value, null)); result.CompileTimeType = propertyInfo.PropertyType; } else { Type t = TypeExp.GetType(data); if (t != null) { result = new CseObject(t); result.CompileTimeType = t.GetType(); } else { throw new CseLogicException(CseLogicExceptionType.IDENT_NOT_FOUND, data); } } } } else { dynamic resultObj = Enum.Parse(instanceType, data); result = new CseObject(resultObj); result.CompileTimeType = resultObj.GetType(); } return result; }
/// /// <summary> /// Parses string literals /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A string value</returns> /// internal static CseObject ParseStr(string data) { if (data.StartsWith("@")) { data = data.Remove(0, 1); ParseEscSeqs(ref data, true); } else { ParseEscSeqs(ref data, false); } CseObject result = new CseObject(data.Remove(data.Length - 1, 1).Remove(0, 1)); result.IsLiteral = true; return(result); }
/// /// <summary> /// Parses array expressions /// </summary> /// /// <param name="rootInstance">Environment where the array is implemented</param> /// <param name="arrayIdent">Name of the array</param> /// <param name="index">Object for the index value</param> /// /// <returns>CseObject containing the array element at the specified index</returns> /// /// <exception cref="CseLogicExceptionType.IDENT_IS_NOT_ARRAY" /> /// <exception cref="CseLogicExceptionType.IDENT_NOT_FOUND" /> /// public static CseObject Parse(CseObject rootInstance, string arrayIdent, CseObject index) { BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; Type rootInstanceType = TypeExp.GetTypeObj(rootInstance.Value); object result = null; FieldInfo fieldInfo = rootInstanceType.GetField(arrayIdent, flags); if (fieldInfo != null) { result = fieldInfo.GetValue(rootInstance.Value); } else { PropertyInfo propertyInfo = rootInstanceType.GetProperty(arrayIdent); if (propertyInfo != null) { result = propertyInfo.GetValue(rootInstance.Value, null); } else { throw new CseLogicException(CseLogicExceptionType.IDENT_NOT_FOUND, arrayIdent); } } IDictionary dictionary = result as IDictionary; Array array = result as Array; if (dictionary != null) { return(new CseObject(dictionary[index.Value])); } else if (array != null) { return(new CseObject(array.GetValue(long.Parse(index.Value.ToString())))); } else { throw new CseLogicException(CseLogicExceptionType.IDENT_IS_NOT_ARRAY, arrayIdent); } }
/// /// <summary> /// Parses assignment expressions. This method basically just prepares /// the parameters for a call to IdentifierExp.Assign(). Reference that /// method for more information on this method's parameters. /// </summary> /// /// <param name="envChain"> /// List of CseObjects from the chain expression of the lhs. /// This makes up the list of environment objects. /// </param> /// <param name="envNames">List of field/property names in the lhs chain expression.</param> /// <param name="envIndices"> /// List of array indices for each array in the lhs chain expression. /// For each non-array, the envIndices at that index is null. /// </param> /// <param name="rhs">Rhs expression</param> /// /// <returns>The rhs parameter</returns> /// public static CseObject Parse(List<CseObject> envChain, List<string> envNames, List<CseObject> envIndices, CseObject rhs) { Type envType; while (envChain.Count > 1) { envType = envChain[1].Value.GetType(); if (!envType.IsValueType) { envChain.RemoveAt(0); envNames.RemoveAt(0); envIndices.RemoveAt(0); } else { break; } } IdentifierExp.Assign(envChain, envNames, envIndices, rhs); return rhs; }
/// /// <summary> /// Inspects all CseObjects in args and for the literal integral values, it /// converts their type to the most narrow type that can contain their value. /// E.g. if the value is a literal 300 (not the value of an identifier as those /// types aren't changed), then the smallest integral data type that can store /// that would be a short. This increases the chance that the given args will /// match a wider method signature. /// </summary> /// /// <param name="args">Arguments to inspect and convert</param> /// private static void NarrowAllIntTypes(ref List <CseObject> args) { if (args == null) { return; } for (int i = 0; i < args.Count; i++) { CseObject arg = args[i]; if (LiteralExp.IsIntType(arg) && arg.IsLiteral) { args[i] = LiteralExp.NarrowIntType(arg); args[i].CompileTimeType = args[i].Value.GetType(); } } }
/// /// <summary> /// Parses conditional expressions /// </summary> /// /// <param name="leftOp">Left operand</param> /// <param name="rightOp">Right operand</param> /// <param name="type">Conditional operator type</param> /// /// <returns>The result of applying the conditional operator</returns> /// public static CseObject Parse(CseObject leftOp, CseObject rightOp, CondType type) { CseObject obj = new CseObject(null) { IsLiteral = leftOp.IsLiteral && rightOp.IsLiteral }; try { switch (type) { case CondType.EQ: obj.Value = leftOp.Value == rightOp.Value; break; case CondType.NEQ: obj.Value = leftOp.Value != rightOp.Value; break; case CondType.GT: obj.Value = leftOp.Value > rightOp.Value; break; case CondType.GTE: obj.Value = leftOp.Value >= rightOp.Value; break; case CondType.LT: obj.Value = leftOp.Value < rightOp.Value; break; case CondType.LTE: obj.Value = leftOp.Value <= rightOp.Value; break; default: throw new System.NotImplementedException("Not implemented."); } } catch { // TODO: Fill this out! } return(obj); }
/// /// <summary> /// Parses integral-type literals /// </summary> /// /// <param name="data">String containing the literal</param> /// <param name="suffix">Optional type suffix</param> /// /// <returns>An integral-type value or float-type if float-type suffix is supplied</returns> /// /// <exception cref="CseLogicExceptionType.NOT_A_NUM" /> /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_INT_TYPE" /> /// internal static CseObject ParseIntType(string data, string suffix) { CseObject result = null; if (suffix != null && suffix != "") { switch (suffix.ToLower()) { case "f": result = new CseObject(float.Parse(data)); break; case "m": result = new CseObject(decimal.Parse(data)); break; case "d": result = new CseObject(double.Parse(data)); break; case "ul": result = new CseObject(ulong.Parse(data)); break; case "u": result = new CseObject(uint.Parse(data)); break; case "l": result = new CseObject(long.Parse(data)); break; } } else { // TODO: if long cannot parse number, it may mean an overflow/underflow not a NaN problem int intAttempt; long longAttempt; if (int.TryParse(data, out intAttempt)) result = new CseObject(intAttempt); else if (long.TryParse(data, out longAttempt)) result = new CseObject(longAttempt); else throw new CseLogicException(CseLogicExceptionType.NOT_A_NUM, data); } if (result == null) throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_INT_TYPE, data); result.IsLiteral = true; return result; }
/// /// <summary> /// Parses float-type literals /// </summary> /// /// <param name="data">String containing the literal</param> /// <param name="suffix">Optional type suffix</param> /// /// <returns>A float-type value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE" /> /// <exception cref="CseLogicExceptionType.NOT_A_NUM" /> /// internal static CseObject ParseFloatType(string data, string suffix) { CseObject result = null; if (suffix != null && suffix != "") { switch (suffix.ToLower()) { case "f": result = new CseObject(float.Parse(data)); break; case "m": result = new CseObject(decimal.Parse(data)); break; case "d": result = new CseObject(double.Parse(data)); break; } } else { double doubleAttempt; if (double.TryParse(data, out doubleAttempt)) { result = new CseObject(doubleAttempt); } else { throw new CseLogicException(CseLogicExceptionType.NOT_A_NUM, data); } } if (result == null) { throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE, data); } result.IsLiteral = true; return(result); }
/// /// <summary> /// Parses ternary expressions /// </summary> /// /// <param name="cond">Conditional to test</param> /// <param name="truePart">Expression to return if condition is true</param> /// <param name="falsePart">Expression to return if condition is false</param> /// /// <returns>truePart if cond is true, falsePart otherwise</returns> /// public static CseObject Ternary(CseObject cond, CseObject truePart, CseObject falsePart) { bool?condBool = cond.Value as bool?; if (condBool != null) { return(condBool.Value ? truePart : falsePart); } else { MethodInfo mi = cond.Value.GetType().GetMethod(OpOverloadNames.TRUE); if (mi != null) { return(new CseObject(cond.Value.GetType().InvokeMember(OpOverloadNames.TRUE, OpOverloadNames.Flags, null, CsEval.evalEnvironment, new object[] { cond.Value }))); } else { return(null); } //TODO: throw new logic exception for this } }
/// /// <summary> /// Adds a temporary identifier of the given type and name and initializes /// it to the given initValue /// </summary> /// /// <param name="type">Type of identifier</param> /// <param name="name">Name of identifier</param> /// <param name="initValue">Initial value of identifer</param> /// public static void AddIdent(Type type, string name, dynamic initValue) { dynamic value = (initValue is CseObject ? initValue.Value : initValue); // TODO: Make CseExceptionType for type assign mismatch if (!TypeExp.TypeAssignMatch(type, value)) throw new Exception(String.Format("Cannot assign {0} to variable of type {1}", value, type.ToString())); CseObject ident = new CseObject(value) { CompileTimeType = type, IsLiteral = false, CallMod = CallArgMod.VAL }; /* TODO: Add custom error type and message for duplicate ident names * if (tempIdents.ContainsKey(name)) * throw new CseLogicException(CseLogicExceptionType.??, name); * * TODO: Add keyword (maybe with a prefix) that returns a list of all * currently stored temp vars along with their type and value */ tempIdents.Add(name, ident); }
/// /// <summary> /// Parses bool literals /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A bool value</returns> /// internal static CseObject ParseBool(string data) { CseObject result = null; if (data.Equals("true")) result = new CseObject(true); else result = new CseObject(false); result.IsLiteral = true; return result; }
/// /// <summary> /// Parses arithmetic expressions /// </summary> /// /// <param name="leftOperand">Left operand</param> /// <param name="rightOperand">Right operand</param> /// <param name="arithType">Arithmetic operator type</param> /// /// <remarks> /// Only works with operands that evaluate to either numeric or string types. If both operands are strings /// and arithType is ADD, concatenation is performed. Else, if either operand is of a float type, both are /// treated as float type. Else, both are treated as int types. This is to ensure proper behavior of operators /// such as addition and division. If operands are of int type, result is then parsed as a literal expression /// to ensure correct return type. /// </remarks> /// /// <returns>Result of arithmetic operation</returns> /// /// <exception cref="CseLogicExceptionType.ARITH_EXCEPTION" /> /// <exception cref="CseLogicExceptionType.LEFT_OP_NON_NUM" /> /// <exception cref="CseLogicExceptionType.RIGHT_OP_NON_NUM" /> /// internal static CseObject Parse(CseObject leftOperand, CseObject rightOperand, ArithType arithType) { dynamic result = null; dynamic leftOpValue = leftOperand.Value; dynamic rightOpValue = rightOperand.Value; string methOpName = null; try { switch (arithType) { case ArithType.ADD: methOpName = OpOverloadNames.ADD; result = leftOpValue + rightOpValue; break; case ArithType.DIV: methOpName = OpOverloadNames.DIV; result = leftOpValue / rightOpValue; break; case ArithType.MOD: methOpName = OpOverloadNames.MOD; result = leftOpValue % rightOpValue; break; case ArithType.MUL: methOpName = OpOverloadNames.MUL; result = leftOpValue * rightOpValue; break; case ArithType.POW: result = Math.Pow(leftOpValue, rightOpValue); break; case ArithType.SUB: methOpName = OpOverloadNames.SUB; result = leftOpValue - rightOpValue; break; } return new CseObject(result); } catch { if (methOpName == null) throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); MethodInfo leftOpMeth = leftOpValue.GetType().GetMethod(methOpName, OpOverloadNames.Flags); MethodInfo rightOpMeth = rightOpValue.GetType().GetMethod(methOpName, OpOverloadNames.Flags); if (leftOpMeth == null && rightOpMeth == null) throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); else if (leftOpMeth != null) { try { result = new CseObject(leftOpMeth.Invoke(leftOpValue, new object[] { leftOpValue, rightOpValue })); } catch { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } } else if (rightOpMeth != null) { try { result = new CseObject(rightOpMeth.Invoke(rightOpValue, new object[] { leftOpValue, rightOpValue })); } catch { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } } else { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } } return new CseObject(result); }
/// /// <summary> /// Tests for an implicit enum conversion, given a CseObject and a type. /// </summary> /// /// <param name="data">The object to test converting</param> /// <param name="t">The type to attempt to convert the object to</param> /// /// <returns>True if an implicit enum conversion is valid, false otherwise</returns> /// public static bool ImpEnumConv(CseObject data, Type t) { long num = -1; if (data.IsLiteral && t.IsEnum && long.TryParse(data.Value.ToString(), out num)) if (num == 0) return true; return false; }
/// /// <summary> /// Parses casts /// </summary> /// /// <remarks> /// Can only perform a real cast in certain situations. When it can't it /// tries to mimic a cast using parsing or conversion. /// </remarks> /// /// <param name="instance">The root object</param> /// <param name="typeName">Type to cast to</param> /// <param name="cseData">Expression to cast</param> /// /// <returns>The converted object</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CAST_VALUE_TO_TYPE" /> /// <exception cref="CseLogicExceptionType.OVERFLOW_TRYING_TO_CAST" /> /// // TODO: Check if the generic Cast<T> method would work internal static CseObject Parse(CseObject instance, string typeName, CseObject cseData) { dynamic data = cseData.Value; dynamic castedObj = null; Type dataType = TypeExp.GetTypeObj(data); if (dataType.IsEnum) { switch (typeName.ToLower()) { case "bool": castedObj = (bool)data; break; case "byte": castedObj = (byte)data; break; case "decimal": castedObj = (decimal)data; break; case "double": castedObj = (double)data; break; case "float": castedObj = (float)data; break; case "int": castedObj = (int)data; break; case "int16": castedObj = (Int16)data; break; case "int32": castedObj = (Int32)data; break; case "int64": castedObj = (Int64)data; break; case "long": castedObj = (long)data; break; case "sbyte": castedObj = (sbyte)data; break; case "short": castedObj = (short)data; break; case "single": castedObj = (Single)data; break; case "uint": castedObj = (uint)data; break; case "uint16": castedObj = (UInt16)data; break; case "uint32": castedObj = (UInt32)data; break; case "uint64": castedObj = (UInt64)data; break; case "ulong": castedObj = (ulong)data; break; case "ushort": castedObj = (ushort)data; break; default: castedObj = Enum.Parse(dataType, cseData.Value.ToString()); // instance.Value; break; } } else { switch (typeName.ToLower()) { case "bool": bool boolAttempt; if (bool.TryParse(data.ToString(), out boolAttempt)) { castedObj = boolAttempt; } break; case "byte": byte byteAttempt; if (byte.TryParse(data.ToString(), out byteAttempt)) { castedObj = byteAttempt; } break; case "decimal": decimal decimalAttempt; if (decimal.TryParse(data.ToString(), out decimalAttempt)) { castedObj = decimalAttempt; } break; case "double": double doubleAttempt; if (double.TryParse(data.ToString(), out doubleAttempt)) { castedObj = doubleAttempt; } break; case "float": float floatAttempt; if (float.TryParse(data.ToString(), out floatAttempt)) { castedObj = floatAttempt; } break; case "int": int intAttempt; if (int.TryParse(data.ToString(), out intAttempt)) { castedObj = intAttempt; } break; case "int16": Int16 int16Attempt; if (Int16.TryParse(data.ToString(), out int16Attempt)) { castedObj = int16Attempt; } break; case "int32": Int32 int32Attempt; if (Int32.TryParse(data.ToString(), out int32Attempt)) { castedObj = int32Attempt; } break; case "int64": Int64 int64Attempt; if (Int64.TryParse(data.ToString(), out int64Attempt)) { castedObj = int64Attempt; } break; case "long": long longAttempt; if (long.TryParse(data.ToString(), out longAttempt)) { castedObj = longAttempt; } break; case "sbyte": sbyte sbyteAttempt; if (sbyte.TryParse(data.ToString(), out sbyteAttempt)) { castedObj = sbyteAttempt; } break; case "short": short shortAttempt; if (short.TryParse(data.ToString(), out shortAttempt)) { castedObj = shortAttempt; } break; case "single": Single singleAttempt; if (Single.TryParse(data.ToString(), out singleAttempt)) { castedObj = singleAttempt; } break; case "uint": uint uintAttempt; if (uint.TryParse(data.ToString(), out uintAttempt)) { castedObj = uintAttempt; } break; case "uint16": UInt16 uint16Attempt; if (UInt16.TryParse(data.ToString(), out uint16Attempt)) { castedObj = uint16Attempt; } break; case "uint32": UInt32 uint32Attempt; if (UInt32.TryParse(data.ToString(), out uint32Attempt)) { castedObj = uint32Attempt; } break; case "uint64": UInt64 uint64Attempt; if (UInt64.TryParse(data.ToString(), out uint64Attempt)) { castedObj = uint64Attempt; } break; case "ulong": ulong ulongAttempt; if (ulong.TryParse(data.ToString(), out ulongAttempt)) { castedObj = ulongAttempt; } break; case "ushort": ushort ushortAttempt; if (ushort.TryParse(data.ToString(), out ushortAttempt)) { castedObj = ushortAttempt; } break; } if (castedObj == null) { double dataAsDouble; if (double.TryParse(data.ToString(), out dataAsDouble)) { try { switch (typeName.ToLower()) { case "byte": castedObj = checked ((byte)dataAsDouble); break; case "int": castedObj = checked ((int)dataAsDouble); break; case "int16": castedObj = checked ((Int16)dataAsDouble); break; case "int32": castedObj = checked ((Int32)dataAsDouble); break; case "int64": castedObj = checked ((Int64)dataAsDouble); break; case "long": castedObj = checked ((long)dataAsDouble); break; case "sbyte": castedObj = checked ((sbyte)dataAsDouble); break; case "short": castedObj = checked ((short)dataAsDouble); break; case "single": castedObj = checked ((Single)dataAsDouble); break; case "uint": castedObj = checked ((uint)dataAsDouble); break; case "uint16": castedObj = checked ((UInt16)dataAsDouble); break; case "uint32": castedObj = checked ((UInt32)dataAsDouble); break; case "uint64": castedObj = checked ((UInt64)dataAsDouble); break; case "ulong": castedObj = checked ((ulong)dataAsDouble); break; case "ushort": castedObj = checked ((ushort)dataAsDouble); break; } } catch (OverflowException) { throw new CseLogicException(CseLogicExceptionType.OVERFLOW_TRYING_TO_CAST, data.ToString(), typeName); } } } if (castedObj == null) { try { /* If parsing is not possible, try a conversion */ Type typeToCastTo = TypeExp.GetType(typeName); castedObj = Convert.ChangeType(data, typeToCastTo); } catch { throw new CseLogicException(CseLogicExceptionType.CANT_CAST_VALUE_TO_TYPE, data.ToString(), typeName); } } } return(new CseObject(castedObj) { IsLiteral = cseData.IsLiteral }); //return cseData.Clone(castedObj); }
/// /// <summary> /// Changes the given integral-type object to the smallest integral data type /// that can contain its value. If xdata has a CompileTimeType that is /// unsigned, then the smallest unsigned integral type is used. Otherwise /// the smallest signed type is used. The only current exception to this is /// byte vs. sbyte as they are considered special cases and therefore, regardless /// if the original type is unsigned or not, byte is always attempted in place /// of sbyte. /// </summary> /// /// <param name="xdata">Integral-type value to narrow</param> /// /// <returns>An integral-type object with same Value as the xdata param</returns> /// /// <exception cref="System.ArgumentException" /> /// public static CseObject NarrowIntType(CseObject xdata) { dynamic data = xdata.Value; dynamic result = null; if (IsUnsignedType(xdata)) { byte byteAttempt; ushort ushortAttempt; uint uintAttempt; ulong ulongAttempt; if (byte.TryParse(data.ToString(), out byteAttempt)) { result = byteAttempt; } else if (ushort.TryParse(data.ToString(), out ushortAttempt)) { result = ushortAttempt; } else if (uint.TryParse(data.ToString(), out uintAttempt)) { result = uintAttempt; } else if (ulong.TryParse(data.ToString(), out ulongAttempt)) { result = ulongAttempt; } else { throw new ArgumentException(data.ToString() + " is not an integral type"); } } else { byte byteAttempt; short shortAttempt; int intAttempt; long longAttempt; if (byte.TryParse(data.ToString(), out byteAttempt)) { result = byteAttempt; } else if (short.TryParse(data.ToString(), out shortAttempt)) { result = shortAttempt; } else if (int.TryParse(data.ToString(), out intAttempt)) { result = intAttempt; } else if (long.TryParse(data.ToString(), out longAttempt)) { result = longAttempt; } else { throw new ArgumentException(data.ToString() + " is not an integral type"); } } return(new CseObject(result)); }
/// /// <summary> /// Parses literals that contain the exponential 'e' character /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A double value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_DBL" /> /// internal static CseObject ParseEType(string data) { double value; Regex r = new Regex(@"(f|m|d)$", RegexOptions.IgnoreCase); Match m = r.Match(data); string suffix = m.Groups[1].Value; string number = data.Substring(0, data.Length - suffix.Length); if (!double.TryParse(number, out value)) throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_DBL, data); CseObject result = null; if (suffix != "") { result = ParseFloatType(value.ToString(), suffix); } else { result = new CseObject(value); } result.IsLiteral = true; return result; }
/// /// <summary> /// Assigns a value to the specified identifier (field or property). /// Since any field or property that is a ValueType will not have its /// value changed without explicitly being set, this method is given the /// entire list of objects of the lhs chain expression and uses recursion to /// set them right to left. /// </summary> /// /// <example> /// <c language="C#">x.y.z = 3</c> /// This will set z = 3, then y = z (the newly changed z), then x = y. /// If the expression is simply <c language="C#">x = 3</c>, then that is the only assignment /// performed and all List arguments contain only a single value. /// </example> /// /// <param name="envChain"> /// List of CseObjects from the chain expression of the lhs. This makes up the /// list of environment objects. In the case of x = 3, envChain would contain /// x's value. /// </param> /// <param name="envNames">List of field/property names in the lhs chain expression. In the case of x = 3, envNames would contain "x".</param> /// <param name="envIndices"> /// List of array indices for each array in the lhs chain expression. For each non-array, the /// envIndices at that index is null. In the case of x = 3, envIndices would contain null. /// In the case of x[2] = 3, envIndices would contain 2. /// </param> /// <param name="xrhs">Rhs expression</param> /// /// <returns>xrhs's Value property</returns> /// /// <exception cref="CseLogicExceptionType.ARRAY_INDEX_NOT_INT" /> /// <exception cref="CseLogicExceptionType.CANT_FIND_IDENT_IN_ENV" /> /// internal static object Assign(List <CseObject> envChain, List <string> envNames, List <CseObject> envIndices, CseObject xrhs) { object rhs = (xrhs == null ? null : xrhs.Value); if (envChain.Count == 0) { return(rhs); } CseObject env = envChain[envChain.Count - 1]; string envName = envNames[envNames.Count - 1]; CseObject envIndex = envIndices[envIndices.Count - 1]; //Type instanceType = TypeExp.GetTypeObj(env.Value); Type instanceType = env.ValueType; // Arrays if (envIndex != null) { MemberInfo member; ICollection arrMember; member = instanceType.GetField(envName, defaultFlags | BindingFlags.GetField); if (member != null) { arrMember = (ICollection)((FieldInfo)member).GetValue(env.Value); } else { member = instanceType.GetProperty(envName, defaultFlags | BindingFlags.GetProperty); if (member != null) { arrMember = (ICollection)((PropertyInfo)member).GetValue(env.Value, null); } else { throw new CseLogicException(CseLogicExceptionType.CANT_FIND_IDENT_IN_ENV, envName, env.Value.ToString()); } } Array arrMemberAsArray = arrMember as Array; IDictionary arrMemberAsIDict = arrMember as IDictionary; if (arrMemberAsArray != null) { int index; if (int.TryParse(envIndex.Value.ToString(), out index)) { arrMemberAsArray.SetValue(rhs, index); } else { throw new CseLogicException(CseLogicExceptionType.ARRAY_INDEX_NOT_INT); } } else if (arrMemberAsIDict != null) { arrMemberAsIDict[envIndex.Value] = rhs; } } // Nonarrays else { if (env == CsEval.EvalEnvironment) { CseObject tempLookup = TempIdentifierExp.Lookup(envName); if (tempLookup != null) { TempIdentifierExp.Assign(envName, rhs); return(tempLookup); } } FieldInfo fieldInfo = instanceType.GetField(envName, defaultFlags | BindingFlags.GetField); if (fieldInfo != null) { fieldInfo.SetValue(env.Value, rhs); } else { PropertyInfo propertyInfo = instanceType.GetProperty(envName, defaultFlags | BindingFlags.GetProperty); if (propertyInfo != null) { propertyInfo.SetValue(env.Value, rhs, null); } else { throw new CseLogicException(CseLogicExceptionType.CANT_FIND_IDENT_IN_ENV, envName, env.Value.ToString()); } } } envChain.RemoveAt(envChain.Count - 1); envNames.RemoveAt(envNames.Count - 1); envIndices.RemoveAt(envIndices.Count - 1); return(IdentifierExp.Assign(envChain, envNames, envIndices, env)); }
/// /// <summary> /// Parses string literals /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A string value</returns> /// internal static CseObject ParseStr(string data) { if (data.StartsWith("@")) { data = data.Remove(0, 1); ParseEscSeqs(ref data, true); } else { ParseEscSeqs(ref data, false); } CseObject result = new CseObject(data.Remove(data.Length - 1, 1).Remove(0, 1)); result.IsLiteral = true; return result; }
/// /// <summary> /// Parses casts /// </summary> /// /// <remarks> /// Can only perform a real cast in certain situations. When it can't it /// tries to mimic a cast using parsing or conversion. /// </remarks> /// /// <param name="instance">The root object</param> /// <param name="typeName">Type to cast to</param> /// <param name="cseData">Expression to cast</param> /// /// <returns>The converted object</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CAST_VALUE_TO_TYPE" /> /// <exception cref="CseLogicExceptionType.OVERFLOW_TRYING_TO_CAST" /> /// // TODO: Check if the generic Cast<T> method would work internal static CseObject Parse(CseObject instance, string typeName, CseObject cseData) { dynamic data = cseData.Value; dynamic castedObj = null; Type dataType = TypeExp.GetTypeObj(data); if (dataType.IsEnum) { switch (typeName.ToLower()) { case "bool": castedObj = (bool)data; break; case "byte": castedObj = (byte)data; break; case "decimal": castedObj = (decimal)data; break; case "double": castedObj = (double)data; break; case "float": castedObj = (float)data; break; case "int": castedObj = (int)data; break; case "int16": castedObj = (Int16)data; break; case "int32": castedObj = (Int32)data; break; case "int64": castedObj = (Int64)data; break; case "long": castedObj = (long)data; break; case "sbyte": castedObj = (sbyte)data; break; case "short": castedObj = (short)data; break; case "single": castedObj = (Single)data; break; case "uint": castedObj = (uint)data; break; case "uint16": castedObj = (UInt16)data; break; case "uint32": castedObj = (UInt32)data; break; case "uint64": castedObj = (UInt64)data; break; case "ulong": castedObj = (ulong)data; break; case "ushort": castedObj = (ushort)data; break; default: castedObj = Enum.Parse(dataType, cseData.Value.ToString()); // instance.Value; break; } } else { switch (typeName.ToLower()) { case "bool": bool boolAttempt; if (bool.TryParse(data.ToString(), out boolAttempt)) castedObj = boolAttempt; break; case "byte": byte byteAttempt; if (byte.TryParse(data.ToString(), out byteAttempt)) castedObj = byteAttempt; break; case "decimal": decimal decimalAttempt; if (decimal.TryParse(data.ToString(), out decimalAttempt)) castedObj = decimalAttempt; break; case "double": double doubleAttempt; if (double.TryParse(data.ToString(), out doubleAttempt)) castedObj = doubleAttempt; break; case "float": float floatAttempt; if (float.TryParse(data.ToString(), out floatAttempt)) castedObj = floatAttempt; break; case "int": int intAttempt; if (int.TryParse(data.ToString(), out intAttempt)) castedObj = intAttempt; break; case "int16": Int16 int16Attempt; if (Int16.TryParse(data.ToString(), out int16Attempt)) castedObj = int16Attempt; break; case "int32": Int32 int32Attempt; if (Int32.TryParse(data.ToString(), out int32Attempt)) castedObj = int32Attempt; break; case "int64": Int64 int64Attempt; if (Int64.TryParse(data.ToString(), out int64Attempt)) castedObj = int64Attempt; break; case "long": long longAttempt; if (long.TryParse(data.ToString(), out longAttempt)) castedObj = longAttempt; break; case "sbyte": sbyte sbyteAttempt; if (sbyte.TryParse(data.ToString(), out sbyteAttempt)) castedObj = sbyteAttempt; break; case "short": short shortAttempt; if (short.TryParse(data.ToString(), out shortAttempt)) castedObj = shortAttempt; break; case "single": Single singleAttempt; if (Single.TryParse(data.ToString(), out singleAttempt)) castedObj = singleAttempt; break; case "uint": uint uintAttempt; if (uint.TryParse(data.ToString(), out uintAttempt)) castedObj = uintAttempt; break; case "uint16": UInt16 uint16Attempt; if (UInt16.TryParse(data.ToString(), out uint16Attempt)) castedObj = uint16Attempt; break; case "uint32": UInt32 uint32Attempt; if (UInt32.TryParse(data.ToString(), out uint32Attempt)) castedObj = uint32Attempt; break; case "uint64": UInt64 uint64Attempt; if (UInt64.TryParse(data.ToString(), out uint64Attempt)) castedObj = uint64Attempt; break; case "ulong": ulong ulongAttempt; if (ulong.TryParse(data.ToString(), out ulongAttempt)) castedObj = ulongAttempt; break; case "ushort": ushort ushortAttempt; if (ushort.TryParse(data.ToString(), out ushortAttempt)) castedObj = ushortAttempt; break; } if (castedObj == null) { double dataAsDouble; if (double.TryParse(data.ToString(), out dataAsDouble)) { try { switch (typeName.ToLower()) { case "byte": castedObj = checked((byte)dataAsDouble); break; case "int": castedObj = checked((int)dataAsDouble); break; case "int16": castedObj = checked((Int16)dataAsDouble); break; case "int32": castedObj = checked((Int32)dataAsDouble); break; case "int64": castedObj = checked((Int64)dataAsDouble); break; case "long": castedObj = checked((long)dataAsDouble); break; case "sbyte": castedObj = checked((sbyte)dataAsDouble); break; case "short": castedObj = checked((short)dataAsDouble); break; case "single": castedObj = checked((Single)dataAsDouble); break; case "uint": castedObj = checked((uint)dataAsDouble); break; case "uint16": castedObj = checked((UInt16)dataAsDouble); break; case "uint32": castedObj = checked((UInt32)dataAsDouble); break; case "uint64": castedObj = checked((UInt64)dataAsDouble); break; case "ulong": castedObj = checked((ulong)dataAsDouble); break; case "ushort": castedObj = checked((ushort)dataAsDouble); break; } } catch (OverflowException) { throw new CseLogicException(CseLogicExceptionType.OVERFLOW_TRYING_TO_CAST, data.ToString(), typeName); } } } if (castedObj == null) { try { /* If parsing is not possible, try a conversion */ Type typeToCastTo = TypeExp.GetType(typeName); castedObj = Convert.ChangeType(data, typeToCastTo); } catch { throw new CseLogicException(CseLogicExceptionType.CANT_CAST_VALUE_TO_TYPE, data.ToString(), typeName); } } } return new CseObject(castedObj) { IsLiteral = cseData.IsLiteral }; //return cseData.Clone(castedObj); }
// Implicit reference conversions 6.1.6 // TODO: check if both ref, throw exc if not /// /// <summary> /// Tests for an implicit reference conversion, given a CseObject and a type. /// </summary> /// /// <param name="data">The object to test converting</param> /// <param name="t">The type to attempt to convert the object to</param> /// /// <returns>True if an implicit reference conversion is valid, false otherwise</returns> /// public static bool? ImpRefConv(CseObject fromArg, Type to) { bool? success = null; Type from = fromArg.ValueType; if (from == to) // identity success = true; else if (to == typeof(object)) // ref -> object success = true; else if (fromArg.Value == null) // null literal -> Ref-type success = !to.IsValueType; else if (false) // ref -> dynamic (6.1.8) // figure out how to do this ; else if (from.IsArray && to.IsArray) { // Array-type -> Array-type bool sameRank = (from.GetArrayRank() == to.GetArrayRank()); bool bothRef = (!from.GetElementType().IsValueType && !to.GetElementType().IsValueType); bool? impConv = ImpRefConv(new CseObject(-1) { CompileTimeType = from.GetElementType() }, to.GetElementType()); success = (sameRank && bothRef && impConv.GetValueOrDefault(false)); } // Conversion involving type parameters (6.1.10) else if (to.IsGenericParameter) { //if ( fromArg.GetType().Name.Equals(to.Name)) { if (to.GenericParameterAttributes != GenericParameterAttributes.None) { if ((int)(to.GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) { ; } } else { } /*genArg.GetGenericParameterConstraints(); genArg.GenericParameterAttributes;*/ //if( mi.GetGenericArguments()[?] //var t = a.GetType().GetMethod("Foo", BindingFlags.Public | BindingFlags.Instance).GetGenericArguments()[0].GetGenericParameterConstraints();//.GenericParameterAttributes; } // Boxing Conversions (6.1.7) else if (from.IsValueType && !to.IsValueType) { return IsBoxingConversion(fromArg, to); } else if ((from.IsClass && to.IsClass) || (from.IsClass && to.IsInterface) || (from.IsInterface && to.IsInterface)) // class -> class OR class -> interface OR interface -> interface success = CrawlThatShit(to.GetHashCode(), from, new List<int>()); else if (from.IsArray && CrawlThatShit(to.GetHashCode(), typeof(Array), new List<int>())) { // Array-type -> System.array return true; } else if (from.IsArray && from.GetArrayRank() == 1 && to.IsGenericType && CrawlThatShit(to.GetHashCode(), typeof(IList<>), new List<int>())) // Single dim array -> IList<> success = ImpRefConv(new CseObject(-1) { CompileTimeType = from.GetElementType() }, to.GetGenericTypeDefinition()); return success; }
/// /// <summary> /// Parses method calls /// </summary> /// /// <param name="environment">The environment containing the method</param> /// <param name="methName">Name of the method</param> /// <param name="args">CseObject array containing arguments to be sent to the method. Each CseObject is one argument</param> /// /// <returns>CseObject containing the return result of the method call or CseObject containing null if method is void</returns> /// /// <exception cref="CseLogicExceptionType.METHOD_CALL_AMBIGUOUS" /> /// <exception cref="CseLogicExceptionType.METHOD_CANT_IMPLICITLY_COERCE_ARGS" /> /// <exception cref="CseLogicExceptionType.METHOD_DOESNT_EXIST" /> /// <exception cref="CseLogicExceptionType.METHOD_EXISTS_BUT_CANT_BE_INVOKED" /> /// public static CseObject Parse(CseObject environment, string methName, List<CseObject> args) { MethResSettings mrSettings = new MethResSettings() { Args = (args == null ? new CseObject[] { } : args.ToArray()), Env = environment.Value, Name = methName }; List<MethResObject> appMeths = MethRes.GetApplicableMembers(mrSettings); if (appMeths.Count == 0) { mrSettings.IsExtInvocation = true; appMeths = MethRes.GetApplicableMembers(mrSettings); } MethodInfo mi = null; Type envType = TypeExp.GetTypeObj(environment.Value); try { switch (appMeths.Count) { // No methods exist with that name case 0: // TODO: doesn't exist OR no applicable methods can be called throw new CseLogicException(CseLogicExceptionType.METHOD_DOESNT_EXIST, methName); // Only 1 method exists with that name, so try to do what's necessary to coerce args (if they exist) // to match its signature. case 1: MethResObject mrObj = appMeths[0]; if (args != null) { for (int i = 0; i < args.Count; i++) { try { args[i] = CastExp.Parse(environment, mrObj.MethInfo.GetParameters()[i].ParameterType.Name, args[i]); } catch (CseLogicException) { // f**k it, continue } } } //NarrowAllIntTypes(ref args); mi = mrObj.MethInfo; break; // Method is overloaded. // Idea is to simulate C#'s best match algorithm for finding the most appropriate method to call // and if still unsure, throw exception so user can use casting for further clarification. default: Type[] types = GetTypeArray(args); mi = envType.GetMethod(methName, findFlags | BindingFlags.ExactBinding, null, types, null); if (mi != null) break; if (CanCoerce(args)) { NarrowAllIntTypes(ref args); types = GetTypeArray(args); } MethodInfo tryMeth = envType.GetMethod(methName, findFlags, null, types, null); foreach (MethResObject m in appMeths) { if (m.MethInfo == tryMeth) { mi = tryMeth; break; } } if (mi != null) break; // TODO: Attempt to coerce args to formal param types of overloaded methods if (mi == null) throw new CseLogicException(CseLogicExceptionType.METHOD_CALL_AMBIGUOUS, methName); break; } } catch (AmbiguousMatchException) { throw new CseLogicException(CseLogicExceptionType.METHOD_CALL_AMBIGUOUS, methName); } catch (CseLogicException) { throw; } catch { throw new CseLogicException(CseLogicExceptionType.METHOD_EXISTS_BUT_CANT_BE_INVOKED, methName); } if (mi == null) { throw new CseLogicException(CseLogicExceptionType.METHOD_EXISTS_BUT_CANT_BE_INVOKED, methName); } else { dynamic result = mi.Invoke(environment.Value, GetObjArgs(args)); CseObject xo = new CseObject(result); xo.CompileTimeType = mi.ReturnType; if (args != null) { foreach (CseObject arg in args) { if (arg.CallMod != CallArgMod.VAL) { AssignExp.Parse(arg.EnvChain, arg.EnvNames, arg.EnvIndices, arg.Value); } } } return xo; } }
/// /// <summary> /// Parses char literals /// </summary> /// /// <param name="data">String containing the literal</param> /// /// <returns>A char value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_CHAR" /> /// internal static CseObject ParseChar(string data) { data = data.Remove(data.Length - 1, 1).Remove(0, 1); if (data.Length > 1 && !data.StartsWith("\\")) throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_CHAR, data); else { ParseEscSeqs(ref data, false); CseObject result = new CseObject(data[0]); result.IsLiteral = true; return result; } }
/// /// <summary> /// Changes the given integral-type object to the smallest integral data type /// that can contain its value. If xdata has a CompileTimeType that is /// unsigned, then the smallest unsigned integral type is used. Otherwise /// the smallest signed type is used. The only current exception to this is /// byte vs. sbyte as they are considered special cases and therefore, regardless /// if the original type is unsigned or not, byte is always attempted in place /// of sbyte. /// </summary> /// /// <param name="xdata">Integral-type value to narrow</param> /// /// <returns>An integral-type object with same Value as the xdata param</returns> /// /// <exception cref="System.ArgumentException" /> /// public static CseObject NarrowIntType(CseObject xdata) { dynamic data = xdata.Value; dynamic result = null; if (IsUnsignedType(xdata)) { byte byteAttempt; ushort ushortAttempt; uint uintAttempt; ulong ulongAttempt; if (byte.TryParse(data.ToString(), out byteAttempt)) result = byteAttempt; else if (ushort.TryParse(data.ToString(), out ushortAttempt)) result = ushortAttempt; else if (uint.TryParse(data.ToString(), out uintAttempt)) result = uintAttempt; else if (ulong.TryParse(data.ToString(), out ulongAttempt)) result = ulongAttempt; else throw new ArgumentException(data.ToString() + " is not an integral type"); } else { byte byteAttempt; short shortAttempt; int intAttempt; long longAttempt; if (byte.TryParse(data.ToString(), out byteAttempt)) result = byteAttempt; else if (short.TryParse(data.ToString(), out shortAttempt)) result = shortAttempt; else if (int.TryParse(data.ToString(), out intAttempt)) result = intAttempt; else if (long.TryParse(data.ToString(), out longAttempt)) result = longAttempt; else throw new ArgumentException(data.ToString() + " is not an integral type"); } return new CseObject(result); }
/// /// <summary> /// Applies numeric negation (i.e. unary minus) to numeric values /// </summary> /// /// <param name="obj">The CseObject with the value to negate</param> /// /// <returns>The CseObject after negation</returns> /// /// <exception cref="CseLogicExceptionType.CANT_NEGATE_NON_NUM" /> /// <exception cref="CseLogicExceptionType.ERROR_NEGATING_VALUE_OF_TYPE" /> /// internal static CseObject Negate(CseObject obj) { CseObject result = (CseObject)obj.Clone(); dynamic value = obj.Value; double numValue; if (value is string) throw new CseLogicException(CseLogicExceptionType.CANT_NEGATE_NON_NUM, value.ToString()); else if (!double.TryParse(value.ToString(), out numValue)) { MethodInfo mi = value.GetType().GetMethod(OpOverloadNames.UMINUS); if (null != mi) { result.Value = value.GetType().InvokeMember(OpOverloadNames.UMINUS, OpOverloadNames.Flags, null, CsEval.evalEnvironment, new object[] { value }); return result; } else throw new CseLogicException(CseLogicExceptionType.CANT_NEGATE_NON_NUM, value.ToString()); } numValue *= -1; result.Value = numValue; try { result = CastExp.Parse(CsEval.evalEnvironment, value.GetType().Name, result); } catch { if (value.ToString().ToLower().Contains("e")) result = LiteralExp.ParseEType(numValue.ToString()); else if (value.ToString().Contains(".")) result = LiteralExp.ParseFloatType(numValue.ToString(), null); else throw new CseLogicException(CseLogicExceptionType.ERROR_NEGATING_VALUE_OF_TYPE, value.ToString(), value.GetType().Name); } return result; }
/// /// <summary> /// Determines whether the xdata's CompileTimeType is an unsigned integral type or not /// </summary> /// /// <param name="xdata">CseObject to examine the CompileTimeType of</param> /// /// <returns>True if xdata's CompileTimeType is an unsigned integral type, false otherwise</returns> /// private static bool IsUnsignedType(CseObject xdata) { List<Type> unsignedTypes = new List<Type> { typeof(byte), typeof(UInt16), typeof(UInt32), typeof(UInt64) }; if (unsignedTypes.Contains(xdata.CompileTimeType)) return true; else return false; }
/// /// <summary> /// Applies numeric affirmation (i.e. unary plus) to numeric values /// </summary> /// /// <param name="obj">The CseObject with the value to affirm</param> /// /// <returns>The CseObject after affirmation</returns> /// /// <exception cref="CseLogicExceptionType.CANT_AFFIRM_NON_NUM" /> /// internal static CseObject Affirm(CseObject obj) { CseObject result = (CseObject)obj.Clone(); dynamic value = obj.Value; double numValue; if (value is string) throw new CseLogicException(CseLogicExceptionType.CANT_AFFIRM_NON_NUM, value.ToString()); else if (!double.TryParse(value.ToString(), out numValue)) { MethodInfo mi = value.GetType().GetMethod(OpOverloadNames.UPLUS); if (null != mi) { result.Value = value.GetType().InvokeMember(OpOverloadNames.UPLUS, OpOverloadNames.Flags, null, CsEval.evalEnvironment, new object[] { value }); return result; } else throw new CseLogicException(CseLogicExceptionType.CANT_AFFIRM_NON_NUM, value.ToString()); } return result; }
/// /// <summary> /// A holistic method to use for testing coercion of objects /// </summary> /// /// <param name="fromArg">The object to be coerced</param> /// /// <param name="to">The type to convert the object to.</param> /// /// <returns>True if the coercion is valid, false otherwise</returns> /// public static bool CanConvertType(CseObject fromArg, Type to) { CseObject fromData = (CseObject)fromArg.Clone(); if (fromData == null || to == null) { throw new ArgumentException("Param '" + (fromData == null ? "fromData" : "to") + "' must not be null"); } Type from = fromData.ValueType; // null literal conversion 6.1.5 if (fromArg.Value == null) { return IsNullableType(to); } // identity conversion 6.1.1 if (from.GetHashCode().Equals(to.GetHashCode())) return true; // implicit constant expressions 6.1.9 if (fromData.IsLiteral) { bool canConv = false; dynamic num = fromData.Value; if (from == typeof(int)) { switch (Type.GetTypeCode(to)) { case TypeCode.SByte: if (num >= sbyte.MinValue && num <= sbyte.MaxValue) canConv = true; break; case TypeCode.Byte: if (num >= byte.MinValue && num <= byte.MaxValue) canConv = true; break; case TypeCode.Int16: if (num >= short.MinValue && num <= short.MaxValue) canConv = true; break; case TypeCode.UInt16: if (num >= ushort.MinValue && num <= ushort.MaxValue) canConv = true; break; case TypeCode.UInt32: if (num >= uint.MinValue && num <= uint.MaxValue) canConv = true; break; case TypeCode.UInt64: if (num >= 0) canConv = true; break; } } else if (from == typeof(long)) { if (to == typeof(ulong)) { if (num >= 0) canConv = true; } } if (canConv) return true; } // string conversion // TODO: check if this is necessary if (from == typeof(string)) { if (to == typeof(object)) return true; else return false; } // implicit nullable conversion 6.1.4 if (IsNullableType(to)) { if (IsNullableType(fromData.ValueType)) { // If the source value is null, then just return successfully (because the target value is a nullable type) if (fromData.Value == null) { return true; } fromData.CompileTimeType = Nullable.GetUnderlyingType(fromData.ValueType); } return CanConvertType(fromData, Nullable.GetUnderlyingType(to)); } // implicit enumeration conversion 6.1.3 long longTest = -1; if (fromData.IsLiteral && to.IsEnum && long.TryParse(fromData.Value.ToString(), out longTest)) { if (longTest == 0) return true; } // implicit reference conversion 6.1.5 if (!from.IsValueType && !to.IsValueType) { bool? irc = ImpRefConv(fromData, to); if (irc.HasValue) return irc.Value; } // implicit numeric conversion 6.1.2 try { object fromObj = null; double dblTemp; decimal decTemp; char chrTemp; fromObj = Activator.CreateInstance(from); if (char.TryParse(fromObj.ToString(), out chrTemp) || double.TryParse(fromObj.ToString(), out dblTemp) || decimal.TryParse(fromObj.ToString(), out decTemp)) { if (NumConv.ContainsKey(from) && NumConv[from].Contains(to)) return true; else return CrawlThatShit(to.GetHashCode(), from, new List<int>()); } //else { // return CrawlThatShit(to.GetHashCode(), from, new List<int>()); //} } catch { //return CrawlThatShit(to.GetHashCode(), from, new List<int>()); } return false; }
/// /// <summary> /// Determines if value passed in is an integral-type /// </summary> /// /// <param name="data">Value to examine</param> /// /// <returns>True if value is an integral-type, false otherwise</returns> /// public static bool IsIntType(CseObject data) { long longAttempt; return(long.TryParse(data.Value.ToString(), out longAttempt)); }
/// /// <summary> /// Parses method calls /// </summary> /// /// <param name="environment">The environment containing the method</param> /// <param name="methName">Name of the method</param> /// <param name="args">CseObject array containing arguments to be sent to the method. Each CseObject is one argument</param> /// /// <returns>CseObject containing the return result of the method call or CseObject containing null if method is void</returns> /// /// <exception cref="CseLogicExceptionType.METHOD_CALL_AMBIGUOUS" /> /// <exception cref="CseLogicExceptionType.METHOD_CANT_IMPLICITLY_COERCE_ARGS" /> /// <exception cref="CseLogicExceptionType.METHOD_DOESNT_EXIST" /> /// <exception cref="CseLogicExceptionType.METHOD_EXISTS_BUT_CANT_BE_INVOKED" /> /// public static CseObject Parse(CseObject environment, string methName, List <CseObject> args) { MethResSettings mrSettings = new MethResSettings() { Args = (args == null ? new CseObject[] { } : args.ToArray()), Env = environment.Value, Name = methName }; List <MethResObject> appMeths = MethRes.GetApplicableMembers(mrSettings); if (appMeths.Count == 0) { mrSettings.IsExtInvocation = true; appMeths = MethRes.GetApplicableMembers(mrSettings); } MethodInfo mi = null; Type envType = TypeExp.GetTypeObj(environment.Value); try { switch (appMeths.Count) { // No methods exist with that name case 0: // TODO: doesn't exist OR no applicable methods can be called throw new CseLogicException(CseLogicExceptionType.METHOD_DOESNT_EXIST, methName); // Only 1 method exists with that name, so try to do what's necessary to coerce args (if they exist) // to match its signature. case 1: MethResObject mrObj = appMeths[0]; if (args != null) { for (int i = 0; i < args.Count; i++) { try { args[i] = CastExp.Parse(environment, mrObj.MethInfo.GetParameters()[i].ParameterType.Name, args[i]); } catch (CseLogicException) { // f**k it, continue } } } //NarrowAllIntTypes(ref args); mi = mrObj.MethInfo; break; // Method is overloaded. // Idea is to simulate C#'s best match algorithm for finding the most appropriate method to call // and if still unsure, throw exception so user can use casting for further clarification. default: Type[] types = GetTypeArray(args); mi = envType.GetMethod(methName, findFlags | BindingFlags.ExactBinding, null, types, null); if (mi != null) { break; } if (CanCoerce(args)) { NarrowAllIntTypes(ref args); types = GetTypeArray(args); } MethodInfo tryMeth = envType.GetMethod(methName, findFlags, null, types, null); foreach (MethResObject m in appMeths) { if (m.MethInfo == tryMeth) { mi = tryMeth; break; } } if (mi != null) { break; } // TODO: Attempt to coerce args to formal param types of overloaded methods if (mi == null) { throw new CseLogicException(CseLogicExceptionType.METHOD_CALL_AMBIGUOUS, methName); } break; } } catch (AmbiguousMatchException) { throw new CseLogicException(CseLogicExceptionType.METHOD_CALL_AMBIGUOUS, methName); } catch (CseLogicException) { throw; } catch { throw new CseLogicException(CseLogicExceptionType.METHOD_EXISTS_BUT_CANT_BE_INVOKED, methName); } if (mi == null) { throw new CseLogicException(CseLogicExceptionType.METHOD_EXISTS_BUT_CANT_BE_INVOKED, methName); } else { dynamic result = mi.Invoke(environment.Value, GetObjArgs(args)); CseObject xo = new CseObject(result); xo.CompileTimeType = mi.ReturnType; if (args != null) { foreach (CseObject arg in args) { if (arg.CallMod != CallArgMod.VAL) { AssignExp.Parse(arg.EnvChain, arg.EnvNames, arg.EnvIndices, arg.Value); } } } return(xo); } }
/// /// <summary> /// Determines if value passed in is an integral-type /// </summary> /// /// <param name="data">Value to examine</param> /// /// <returns>True if value is an integral-type, false otherwise</returns> /// public static bool IsIntType(CseObject data) { long longAttempt; return long.TryParse(data.Value.ToString(), out longAttempt); }
/// /// <summary> /// A holistic method to use for testing coercion of objects /// </summary> /// /// <param name="fromArg">The object to be coerced</param> /// /// <param name="to">The type to convert the object to.</param> /// /// <returns>True if the coercion is valid, false otherwise</returns> /// public static bool CanConvertType(CseObject fromArg, Type to) { CseObject fromData = (CseObject)fromArg.Clone(); if (fromData == null || to == null) { throw new ArgumentException("Param '" + (fromData == null ? "fromData" : "to") + "' must not be null"); } Type from = fromData.ValueType; // null literal conversion 6.1.5 if (fromArg.Value == null) { return(IsNullableType(to)); } // identity conversion 6.1.1 if (from.GetHashCode().Equals(to.GetHashCode())) { return(true); } // implicit constant expressions 6.1.9 if (fromData.IsLiteral) { bool canConv = false; dynamic num = fromData.Value; if (from == typeof(int)) { switch (Type.GetTypeCode(to)) { case TypeCode.SByte: if (num >= sbyte.MinValue && num <= sbyte.MaxValue) { canConv = true; } break; case TypeCode.Byte: if (num >= byte.MinValue && num <= byte.MaxValue) { canConv = true; } break; case TypeCode.Int16: if (num >= short.MinValue && num <= short.MaxValue) { canConv = true; } break; case TypeCode.UInt16: if (num >= ushort.MinValue && num <= ushort.MaxValue) { canConv = true; } break; case TypeCode.UInt32: if (num >= uint.MinValue && num <= uint.MaxValue) { canConv = true; } break; case TypeCode.UInt64: if (num >= 0) { canConv = true; } break; } } else if (from == typeof(long)) { if (to == typeof(ulong)) { if (num >= 0) { canConv = true; } } } if (canConv) { return(true); } } // string conversion // TODO: check if this is necessary if (from == typeof(string)) { if (to == typeof(object)) { return(true); } else { return(false); } } // implicit nullable conversion 6.1.4 if (IsNullableType(to)) { if (IsNullableType(fromData.ValueType)) { // If the source value is null, then just return successfully (because the target value is a nullable type) if (fromData.Value == null) { return(true); } fromData.CompileTimeType = Nullable.GetUnderlyingType(fromData.ValueType); } return(CanConvertType(fromData, Nullable.GetUnderlyingType(to))); } // implicit enumeration conversion 6.1.3 long longTest = -1; if (fromData.IsLiteral && to.IsEnum && long.TryParse(fromData.Value.ToString(), out longTest)) { if (longTest == 0) { return(true); } } // implicit reference conversion 6.1.5 if (!from.IsValueType && !to.IsValueType) { bool?irc = ImpRefConv(fromData, to); if (irc.HasValue) { return(irc.Value); } } // implicit numeric conversion 6.1.2 try { object fromObj = null; double dblTemp; decimal decTemp; char chrTemp; fromObj = Activator.CreateInstance(from); if (char.TryParse(fromObj.ToString(), out chrTemp) || double.TryParse(fromObj.ToString(), out dblTemp) || decimal.TryParse(fromObj.ToString(), out decTemp)) { if (NumConv.ContainsKey(from) && NumConv[from].Contains(to)) { return(true); } else { return(CrawlThatShit(to.GetHashCode(), from, new List <int>())); } } //else { // return CrawlThatShit(to.GetHashCode(), from, new List<int>()); //} } catch { //return CrawlThatShit(to.GetHashCode(), from, new List<int>()); } return(false); }
/// /// <summary> /// Parses identifiers (fields or properties) /// </summary> /// /// <param name="environment">The environment containing the field or property</param> /// <param name="data">The name of the field or property</param> /// /// <returns>An CseObject containing the value of the identifier or containing null if identifier cannot be found</returns> /// /// <exception cref="CseLogicExceptionType.IDENT_NOT_FOUND" /> /// internal static CseObject Parse(CseObject environment, string data) { if (data[0].Equals('@')) { data = data.Remove(0, 1); } LiteralExp.ParseEscSeqs(ref data, false); CseObject result = null; Type instanceType = TypeExp.GetTypeObj(environment.Value); if (!instanceType.IsEnum) { if (environment == CsEval.EvalEnvironment) { CseObject tempLookup = TempIdentifierExp.Lookup(data); if (tempLookup != null) { return(tempLookup); } } FieldInfo fieldInfo = instanceType.GetField(data, defaultFlags | BindingFlags.GetField); if (fieldInfo != null) { result = new CseObject(fieldInfo.GetValue(environment.Value)); result.CompileTimeType = fieldInfo.FieldType; } else { PropertyInfo propertyInfo = instanceType.GetProperty(data, defaultFlags | BindingFlags.GetProperty); if (propertyInfo != null) { result = new CseObject(propertyInfo.GetValue(environment.Value, null)); result.CompileTimeType = propertyInfo.PropertyType; } else { Type t = TypeExp.GetType(data); if (t != null) { result = new CseObject(t); result.CompileTimeType = t.GetType(); } else { throw new CseLogicException(CseLogicExceptionType.IDENT_NOT_FOUND, data); } } } } else { dynamic resultObj = Enum.Parse(instanceType, data); result = new CseObject(resultObj); result.CompileTimeType = resultObj.GetType(); } return(result); }
// Implicit reference conversions 6.1.6 // TODO: check if both ref, throw exc if not /// /// <summary> /// Tests for an implicit reference conversion, given a CseObject and a type. /// </summary> /// /// <param name="data">The object to test converting</param> /// <param name="t">The type to attempt to convert the object to</param> /// /// <returns>True if an implicit reference conversion is valid, false otherwise</returns> /// public static bool?ImpRefConv(CseObject fromArg, Type to) { bool?success = null; Type from = fromArg.ValueType; if (from == to) { // identity success = true; } else if (to == typeof(object)) { // ref -> object success = true; } else if (fromArg.Value == null) { // null literal -> Ref-type success = !to.IsValueType; } else if (false) { // ref -> dynamic (6.1.8) // figure out how to do this ; } else if (from.IsArray && to.IsArray) { // Array-type -> Array-type bool sameRank = (from.GetArrayRank() == to.GetArrayRank()); bool bothRef = (!from.GetElementType().IsValueType&& !to.GetElementType().IsValueType); bool?impConv = ImpRefConv(new CseObject(-1) { CompileTimeType = from.GetElementType() }, to.GetElementType()); success = (sameRank && bothRef && impConv.GetValueOrDefault(false)); } // Conversion involving type parameters (6.1.10) else if (to.IsGenericParameter) { //if ( fromArg.GetType().Name.Equals(to.Name)) { if (to.GenericParameterAttributes != GenericParameterAttributes.None) { if ((int)(to.GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) { ; } } else { } /*genArg.GetGenericParameterConstraints(); * genArg.GenericParameterAttributes;*/ //if( mi.GetGenericArguments()[?] //var t = a.GetType().GetMethod("Foo", BindingFlags.Public | BindingFlags.Instance).GetGenericArguments()[0].GetGenericParameterConstraints();//.GenericParameterAttributes; } // Boxing Conversions (6.1.7) else if (from.IsValueType && !to.IsValueType) { return(IsBoxingConversion(fromArg, to)); } else if ((from.IsClass && to.IsClass) || (from.IsClass && to.IsInterface) || (from.IsInterface && to.IsInterface)) { // class -> class OR class -> interface OR interface -> interface success = CrawlThatShit(to.GetHashCode(), from, new List <int>()); } else if (from.IsArray && CrawlThatShit(to.GetHashCode(), typeof(Array), new List <int>())) { // Array-type -> System.array return(true); } else if (from.IsArray && from.GetArrayRank() == 1 && to.IsGenericType && CrawlThatShit(to.GetHashCode(), typeof(IList <>), new List <int>())) { // Single dim array -> IList<> success = ImpRefConv(new CseObject(-1) { CompileTimeType = from.GetElementType() }, to.GetGenericTypeDefinition()); } return(success); }
/// /// <summary> /// Determines if a boxing conversion exists between the passed object and the type /// </summary> /// /// <param name="fromArg">The object to convert</param> /// <param name="to">The type to attempt to convert the object to.</param> /// /// <returns>True if a boxing conversion exists between the object and the type, false otherwise</returns> /// public static bool IsBoxingConversion(CseObject fromArg, Type to) { Type unwrappedBox = fromArg.ValueType; if (IsNullableType(unwrappedBox)) { unwrappedBox = Nullable.GetUnderlyingType(unwrappedBox); } if (to.Equals(typeof(System.ValueType)) || to.Equals(typeof(object))) { return true; } else if (CrawlThatShit(to.GetHashCode(), unwrappedBox, new List<int>())) { return true; } else if (unwrappedBox.IsEnum && to.Equals(typeof(System.Enum))) { return true; } else { return false; } }
/// /// <summary> /// Parses arithmetic expressions /// </summary> /// /// <param name="leftOperand">Left operand</param> /// <param name="rightOperand">Right operand</param> /// <param name="arithType">Arithmetic operator type</param> /// /// <remarks> /// Only works with operands that evaluate to either numeric or string types. If both operands are strings /// and arithType is ADD, concatenation is performed. Else, if either operand is of a float type, both are /// treated as float type. Else, both are treated as int types. This is to ensure proper behavior of operators /// such as addition and division. If operands are of int type, result is then parsed as a literal expression /// to ensure correct return type. /// </remarks> /// /// <returns>Result of arithmetic operation</returns> /// /// <exception cref="CseLogicExceptionType.ARITH_EXCEPTION" /> /// <exception cref="CseLogicExceptionType.LEFT_OP_NON_NUM" /> /// <exception cref="CseLogicExceptionType.RIGHT_OP_NON_NUM" /> /// internal static CseObject Parse(CseObject leftOperand, CseObject rightOperand, ArithType arithType) { dynamic result = null; dynamic leftOpValue = leftOperand.Value; dynamic rightOpValue = rightOperand.Value; string methOpName = null; try { switch (arithType) { case ArithType.ADD: methOpName = OpOverloadNames.ADD; result = leftOpValue + rightOpValue; break; case ArithType.DIV: methOpName = OpOverloadNames.DIV; result = leftOpValue / rightOpValue; break; case ArithType.MOD: methOpName = OpOverloadNames.MOD; result = leftOpValue % rightOpValue; break; case ArithType.MUL: methOpName = OpOverloadNames.MUL; result = leftOpValue * rightOpValue; break; case ArithType.POW: result = Math.Pow(leftOpValue, rightOpValue); break; case ArithType.SUB: methOpName = OpOverloadNames.SUB; result = leftOpValue - rightOpValue; break; } return(new CseObject(result)); } catch { if (methOpName == null) { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } MethodInfo leftOpMeth = leftOpValue.GetType().GetMethod(methOpName, OpOverloadNames.Flags); MethodInfo rightOpMeth = rightOpValue.GetType().GetMethod(methOpName, OpOverloadNames.Flags); if (leftOpMeth == null && rightOpMeth == null) { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } else if (leftOpMeth != null) { try { result = new CseObject(leftOpMeth.Invoke(leftOpValue, new object[] { leftOpValue, rightOpValue })); } catch { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } } else if (rightOpMeth != null) { try { result = new CseObject(rightOpMeth.Invoke(rightOpValue, new object[] { leftOpValue, rightOpValue })); } catch { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } } else { throw new CseLogicException(CseLogicExceptionType.ARITH_EXCEPTION, leftOpValue, rightOpValue); } } return(new CseObject(result)); }
/// /// <summary> /// Assigns a value to the specified identifier (field or property). /// Since any field or property that is a ValueType will not have its /// value changed without explicitly being set, this method is given the /// entire list of objects of the lhs chain expression and uses recursion to /// set them right to left. /// </summary> /// /// <example> /// <c language="C#">x.y.z = 3</c> /// This will set z = 3, then y = z (the newly changed z), then x = y. /// If the expression is simply <c language="C#">x = 3</c>, then that is the only assignment /// performed and all List arguments contain only a single value. /// </example> /// /// <param name="envChain"> /// List of CseObjects from the chain expression of the lhs. This makes up the /// list of environment objects. In the case of x = 3, envChain would contain /// x's value. /// </param> /// <param name="envNames">List of field/property names in the lhs chain expression. In the case of x = 3, envNames would contain "x".</param> /// <param name="envIndices"> /// List of array indices for each array in the lhs chain expression. For each non-array, the /// envIndices at that index is null. In the case of x = 3, envIndices would contain null. /// In the case of x[2] = 3, envIndices would contain 2. /// </param> /// <param name="xrhs">Rhs expression</param> /// /// <returns>xrhs's Value property</returns> /// /// <exception cref="CseLogicExceptionType.ARRAY_INDEX_NOT_INT" /> /// <exception cref="CseLogicExceptionType.CANT_FIND_IDENT_IN_ENV" /> /// internal static object Assign(List<CseObject> envChain, List<string> envNames, List<CseObject> envIndices, CseObject xrhs) { object rhs = (xrhs == null ? null : xrhs.Value); if (envChain.Count == 0) return rhs; CseObject env = envChain[envChain.Count - 1]; string envName = envNames[envNames.Count - 1]; CseObject envIndex = envIndices[envIndices.Count - 1]; //Type instanceType = TypeExp.GetTypeObj(env.Value); Type instanceType = env.ValueType; // Arrays if (envIndex != null) { MemberInfo member; ICollection arrMember; member = instanceType.GetField(envName, defaultFlags | BindingFlags.GetField); if (member != null) { arrMember = (ICollection)((FieldInfo)member).GetValue(env.Value); } else { member = instanceType.GetProperty(envName, defaultFlags | BindingFlags.GetProperty); if (member != null) arrMember = (ICollection)((PropertyInfo)member).GetValue(env.Value, null); else throw new CseLogicException(CseLogicExceptionType.CANT_FIND_IDENT_IN_ENV, envName, env.Value.ToString()); } Array arrMemberAsArray = arrMember as Array; IDictionary arrMemberAsIDict = arrMember as IDictionary; if (arrMemberAsArray != null) { int index; if (int.TryParse(envIndex.Value.ToString(), out index)) arrMemberAsArray.SetValue(rhs, index); else throw new CseLogicException(CseLogicExceptionType.ARRAY_INDEX_NOT_INT); } else if (arrMemberAsIDict != null) { arrMemberAsIDict[envIndex.Value] = rhs; } } // Nonarrays else { if (env == CsEval.EvalEnvironment) { CseObject tempLookup = TempIdentifierExp.Lookup(envName); if (tempLookup != null) { TempIdentifierExp.Assign(envName, rhs); return tempLookup; } } FieldInfo fieldInfo = instanceType.GetField(envName, defaultFlags | BindingFlags.GetField); if (fieldInfo != null) { fieldInfo.SetValue(env.Value, rhs); } else { PropertyInfo propertyInfo = instanceType.GetProperty(envName, defaultFlags | BindingFlags.GetProperty); if (propertyInfo != null) propertyInfo.SetValue(env.Value, rhs, null); else throw new CseLogicException(CseLogicExceptionType.CANT_FIND_IDENT_IN_ENV, envName, env.Value.ToString()); } } envChain.RemoveAt(envChain.Count - 1); envNames.RemoveAt(envNames.Count - 1); envIndices.RemoveAt(envIndices.Count - 1); return IdentifierExp.Assign(envChain, envNames, envIndices, env); }
/// /// <summary> /// Parses float-type literals /// </summary> /// /// <param name="data">String containing the literal</param> /// <param name="suffix">Optional type suffix</param> /// /// <returns>A float-type value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE" /> /// <exception cref="CseLogicExceptionType.NOT_A_NUM" /> /// internal static CseObject ParseFloatType(string data, string suffix) { CseObject result = null; if (suffix != null && suffix != "") { switch (suffix.ToLower()) { case "f": result = new CseObject(float.Parse(data)); break; case "m": result = new CseObject(decimal.Parse(data)); break; case "d": result = new CseObject(double.Parse(data)); break; } } else { double doubleAttempt; if (double.TryParse(data, out doubleAttempt)) result = new CseObject(doubleAttempt); else throw new CseLogicException(CseLogicExceptionType.NOT_A_NUM, data); } if (result == null) throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE, data); result.IsLiteral = true; return result; }
/// /// <summary> /// Parses assignment expressions. This method basically just prepares /// the parameters for a call to IdentifierExp.Assign(). Reference that /// method for more information on this method's parameters. /// </summary> /// /// <param name="envChain"> /// List of CseObjects from the chain expression of the lhs. /// This makes up the list of environment objects. /// </param> /// <param name="envNames">List of field/property names in the lhs chain expression.</param> /// <param name="envIndices"> /// List of array indices for each array in the lhs chain expression. /// For each non-array, the envIndices at that index is null. /// </param> /// <param name="rhs">Rhs expression</param> /// /// <returns>The rhs parameter</returns> /// public static CseObject Parse(List <CseObject> envChain, List <string> envNames, List <CseObject> envIndices, CseObject rhs) { Type envType; while (envChain.Count > 1) { envType = envChain[1].Value.GetType(); if (!envType.IsValueType) { envChain.RemoveAt(0); envNames.RemoveAt(0); envIndices.RemoveAt(0); } else { break; } } IdentifierExp.Assign(envChain, envNames, envIndices, rhs); return(rhs); }
/// /// <summary> /// Parses float-type literals /// </summary> /// /// <param name="data">String containing the literal</param> /// <param name="hasSuffix">Indicates whether a suffix is present or not</param> /// /// <returns>A float-type value</returns> /// /// <exception cref="CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE" /> /// <exception cref="CseLogicExceptionType.NOT_A_NUM" /> /// internal static CseObject ParseFloatType(string data, bool hasSuffix) { CseObject result = null; if (hasSuffix) { // Determine the suffix Regex r = new Regex(@"((?:\d|\.)+)([A-Za-z]{1,2})$"); Match m = r.Match(data); data = m.Groups[1].Value; string suffix = m.Groups[2].Value; switch (suffix.ToLower()) { case "f": result = new CseObject(float.Parse(data)); break; case "m": result = new CseObject(decimal.Parse(data)); break; case "d": result = new CseObject(double.Parse(data)); break; } } else { double doubleAttempt; if (double.TryParse(data, out doubleAttempt)) result = new CseObject(doubleAttempt); else throw new CseLogicException(CseLogicExceptionType.NOT_A_NUM, data); } if (result == null) throw new CseLogicException(CseLogicExceptionType.CANT_CONVERT_TO_FLT_TYPE, data); result.IsLiteral = true; return result; }