public object EvaluateValues(ExecContext context) { bool isNum = valueType.CanStoreValue(context, IntrinsicTypeDefs.NUMBER); double nextInteger = 0; bool isString = valueType.CanStoreValue(context, IntrinsicTypeDefs.STRING); for (int ii = 0; ii < _values.Count; ++ii) { ClassValue_Enum val = (ClassValue_Enum)_classDef.Allocate(context); val.Get(mrName).value = _values[ii].name; _classDef.staticVars[ii].value = val; if (null == _values[ii].initializer) { if (isNum) { // If number, use next consecutive integer. val.Get(mrValue).value = nextInteger; nextInteger = Math.Floor(nextInteger + 1); } else if (isString) { // If string, just use name. val.Get(mrValue).value = _values[ii].name; } else { // Use the default value. val.Get(mrValue).value = valueType.GetDefaultValue(context); } } else { object init = _values[ii].initializer.Evaluate(context); if (context.IsRuntimeErrorSet()) { return(null); } val.Get(mrValue).value = init; if (isNum) { nextInteger = Math.Floor((double)init + 1); } } } return(false); }
public static void Register(Engine engine) { PebbleEnum consoleColorEnum = new PebbleEnum(engine.defaultContext, "ConsoleColor", IntrinsicTypeDefs.CONST_NUMBER); consoleColorEnum.AddValue_Literal(engine.defaultContext, "Black", 0.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "DarkBlue", 1.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "DarkGreen", 2.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "DarkCyan", 3.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "DarkRed", 4.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "DarkMagenta", 5.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "DarkYellow", 6.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "LightGray", 7.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "DarkGray", 8.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "Blue", 9.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "Green", 10.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "Cyan", 11.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "Red", 12.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "Magenta", 13.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "Yellow", 14.0); consoleColorEnum.AddValue_Literal(engine.defaultContext, "White", 15.0); // Finalize. consoleColorEnum.EvaluateValues(engine.defaultContext); // ********************************** Regex colorRegex = new Regex(@"(#\d+b?#)", RegexOptions.Compiled); TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("Console", null, false); ClassDef classDef = engine.defaultContext.CreateClass("Console", ourType, null, null, true); classDef.Initialize(); //@ global void Clear() // Clears the screen. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { Console.Clear(); return(null); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.VOID, new ArgList { }, eval, false); classDef.AddMemberLiteral("Clear", newValue.valType, newValue, true); } //@ global string GetCh() // Waits for the user to press a key and returns it. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ConsoleKeyInfo cki = Console.ReadKey(true); return(cki.KeyChar.ToString()); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false); classDef.AddMemberLiteral("GetCh", newValue.valType, newValue, true); } //@ global string Print(...) // Alias of WriteLine. //@ global string ReadLine() // Reads a line of input and returns it. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { return(Console.ReadLine()); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false); classDef.AddMemberLiteral("ReadLine", newValue.valType, newValue, true); } //@ global void ResetColor() // Resets foreground and background colors to their defaults. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { Console.ResetColor(); return(null); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.VOID, new ArgList { }, eval, false); classDef.AddMemberLiteral("ResetColor", newValue.valType, newValue, true); } //@ global num SetBackgroundColor(ConsoleColor color) // Sets the background color to the given value. // Returns the previous color. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue_Enum enumVal = args[0] as ClassValue_Enum; int iColor = Convert.ToInt32((double)enumVal.GetValue()); int iPrevColor = Convert.ToInt32(Console.BackgroundColor); Console.BackgroundColor = (ConsoleColor)iColor; return(consoleColorEnum._classDef.staticVars[iPrevColor].value); }; FunctionValue newValue = new FunctionValue_Host(consoleColorEnum.enumType, new ArgList { consoleColorEnum.enumType }, eval, false); classDef.AddMemberLiteral("SetBackgroundColor", newValue.valType, newValue, true); } //@ global num SetForegroundColor(num color) // Sets the foreground color to the given value. The valid values are 0-15. // Returns the previous color. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue_Enum enumVal = args[0] as ClassValue_Enum; int iColor = Convert.ToInt32((double)enumVal.GetValue()); int iPrevColor = Convert.ToInt32(Console.ForegroundColor); Console.ForegroundColor = (ConsoleColor)iColor; return(consoleColorEnum._classDef.staticVars[iPrevColor].value); }; FunctionValue newValue = new FunctionValue_Host(consoleColorEnum.enumType, new ArgList { consoleColorEnum.enumType }, eval, false); classDef.AddMemberLiteral("SetForegroundColor", newValue.valType, newValue, true); } //@ global string Write(...) // Works much like Print but doesn't automatically include a newline, so you can write partial lines. // Also, if an argument is a ConsoleColor, rather than writing it the function temporarily sets the foreground color to the given color. // Colors can be inserted into the string by using, for example, #1#, which will set the foreground color to 1, or #11b# which sets the background color to 11. // This function restores the colors to what they were before the function was called. // Returns the aggregated string, minus any provided colors. FunctionValue_Host.EvaluateDelegate evalWrite; { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ConsoleColor startingForeground = Console.ForegroundColor; ConsoleColor startingBackground = Console.BackgroundColor; string result = ""; foreach (object val in args) { if (val is ClassValue_Enum) { ClassValue_Enum cve = (ClassValue_Enum)val; if (cve.classDef == consoleColorEnum._classDef) { int color = Convert.ToInt32((double)cve.GetValue()); Console.ForegroundColor = (ConsoleColor)color; continue; } } if (val is string) { string v = val as string; string[] splits = colorRegex.Split(v); foreach (string str in splits) { if (str.Length > 2 && '#' == str[0] && '#' == str[str.Length - 1]) { int iColor; bool background = false; if ('b' == str[str.Length - 2]) { iColor = Convert.ToInt32(str.Substring(1, str.Length - 3)); background = true; } else { iColor = Convert.ToInt32(str.Substring(1, str.Length - 2)); } if (iColor < 0 || iColor > 15) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Write: Color escapes must be between 0 and 15."); Console.ForegroundColor = startingForeground; Console.BackgroundColor = startingBackground; return(null); } if (background) { Console.BackgroundColor = (ConsoleColor)iColor; } else { Console.ForegroundColor = (ConsoleColor)iColor; } } else { result += str; Console.Write(str); } } } else { string s = CoreLib.ValueToString(context, val, false); result += s; Console.Write(s); } } Console.ForegroundColor = startingForeground; Console.BackgroundColor = startingBackground; return(result); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.ANY }, eval, true); classDef.AddMemberLiteral("Write", newValue.valType, newValue, true); evalWrite = eval; } //@ global string WriteLine(...) // Works exactly like Write but just adds a newline at the end. // Returns the aggregated string, minus any provided colors. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object result = evalWrite(context, args, thisScope); Console.Write("\n"); return(result); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.ANY }, eval, true); classDef.AddMemberLiteral("WriteLine", newValue.valType, newValue, true); classDef.AddMemberLiteral("Print", newValue.valType, newValue, true); } classDef.FinalizeClass(engine.defaultContext); }
public static void Register(Engine engine) { //***************************** // Create ScriptError enum. scriptErrorEnum = new PebbleEnum(engine.defaultContext, "ScriptError", IntrinsicTypeDefs.CONST_STRING); // Add a value for "no error" since enums can't be null. scriptErrorEnum.AddValue_Literal(engine.defaultContext, "NoError", "NoError"); // Add both Parse and Runtime errors to the list. foreach (string name in Enum.GetNames(typeof(ParseErrorType))) { scriptErrorEnum.AddValue_Literal(engine.defaultContext, name, name); } foreach (string name in Enum.GetNames(typeof(RuntimeErrorType))) { scriptErrorEnum.AddValue_Literal(engine.defaultContext, name, name); } // Finalize. scriptErrorEnum.EvaluateValues(engine.defaultContext); // Save the value for NoError for convenience. scriptErrorEnum_noErrorValue = scriptErrorEnum.GetValue("NoError"); //******************************* //@ class Result<T> // This was added just in case users might have a need for a templated class that encapsulates a value and a status code. { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("Result", new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, false); ClassDef classDef = engine.defaultContext.CreateClass("Result", ourType, null, new List <string> { "T" }); classDef.Initialize(); //@ T value; // The resultant value IF there was no error. classDef.AddMember("value", IntrinsicTypeDefs.TEMPLATE_0); //@ num status; // A numeric status code. By convention, 0 means no error and anything else means error. classDef.AddMemberLiteral("status", IntrinsicTypeDefs.NUMBER, 0.0); //@ string message; // A place to store error messages if desired. classDef.AddMember("message", IntrinsicTypeDefs.STRING); //@ bool IsSuccess() // Returns true iff status == 0. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; return((double)scope.GetByName("status").value == 0.0); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("IsSuccess", newValue.valType, newValue); } //@ string ToString() // Returns a string representation of the Result. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; double status = (double)scope.GetByName("status").value; string result = scope.classDef.typeDef.ToString() + "["; if (0.0 == status) { result += CoreLib.ValueToString(context, scope.GetByName("value").value, true); } else { result += status + ": \"" + (string)scope.GetByName("message").value + "\""; } return(result + "]"); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("ThisToString", newValue.valType, newValue); } classDef.FinalizeClass(engine.defaultContext); resultClassDef = classDef; } //******************************* //@ class ScriptResult<T> // For returning the result of something that can error, like an Exec call. { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("ScriptResult", new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, false); ClassDef classDef = engine.defaultContext.CreateClass("ScriptResult", ourType, null, new List <string> { "T" }); classDef.Initialize(); //@ T value; // The return value if there was no error. classDef.AddMember("value", IntrinsicTypeDefs.TEMPLATE_0); //@ ScriptError error; // ScriptError.NoError if no error. classDef.AddMemberLiteral("error", CoreLib.scriptErrorEnum._classDef.typeDef, CoreLib.scriptErrorEnum_noErrorValue); //@ string message; // Optional error message. classDef.AddMember("message", IntrinsicTypeDefs.STRING); //@ bool IsSuccess() // Returns true iff error == ScriptError.NoError. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; return(scope.GetByName("error").value == CoreLib.scriptErrorEnum_noErrorValue); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("IsSuccess", newValue.valType, newValue); } //@ string ToString() // Returns a string representation of the ScriptError. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; var error = (ClassValue_Enum)scope.GetByName("error").value; string result = scope.classDef.typeDef.ToString() + "["; if (CoreLib.scriptErrorEnum_noErrorValue == error) { result += CoreLib.ValueToString(context, scope.GetByName("value").value, true); } else { result += error.GetName() + ": \"" + (string)scope.GetByName("message").value + "\""; } return(result + "]"); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("ThisToString", newValue.valType, newValue); } classDef.FinalizeClass(engine.defaultContext); resultClassDef = classDef; } // This code makes sure that Result<bool> is a registered class and type. List <ITypeDef> genericTypes = new ArgList(); genericTypes.Add(IntrinsicTypeDefs.BOOL); scriptResultBoolTypeDef = TypeFactory.GetTypeDef_Class("ScriptResult", genericTypes, false); scriptResultBoolClassDef = engine.defaultContext.RegisterIfUnregisteredTemplate(scriptResultBoolTypeDef); Pb.Assert(null != scriptResultBoolTypeDef && null != scriptResultBoolClassDef, "Error initializing ScriptResult<bool>."); //////////////////////////////////////////////////////////////////////////// // Register non-optional libraries. //CoreResult.Register(engine); // List and Dictionary probably need to be first because other libraries sometimes use them. CoreList.Register(engine); CoreDictionary.Register(engine); MathLib.Register(engine); RegexLib.Register(engine); StringLib.Register(engine); StreamLib.Register(engine); //@ global const num FORMAX; // The highest value a for iterator can be. Attempting to exceed it generates an error. engine.defaultContext.CreateGlobal("FORMAX", IntrinsicTypeDefs.CONST_NUMBER, Expr_For.MAX); //////////////////////////////////////////////////////////////////////////// // Library functions //@ global ScriptResult<bool> Exec(string script) // Executes the supplied script. // Since this is not running "interactive" (or inline), the only way the script can // have an external effect is if it affects global things (variables, class definitions). // The returned ScriptResult's value is only true(success) or false (error). { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string script = (string)args[0]; ClassValue scriptResultInst = scriptResultBoolClassDef.Allocate(context); Variable value = scriptResultInst.GetByName("value"); Variable error = scriptResultInst.GetByName("error"); Variable message = scriptResultInst.GetByName("message"); ScriptResult result = context.engine.RunScript(script, false, null, true); if (null != result.parseErrors) { value.value = false; error.value = scriptErrorEnum.GetValue(result.parseErrors[0].type.ToString());; message.value = result.parseErrors[0].ToString(); } else if (null != result.runtimeError) { value.value = false; error.value = result.value; message.value = result.runtimeError.ToString(); } else { value.value = true; error.value = scriptErrorEnum_noErrorValue; message.value = ""; } return(scriptResultInst); }; FunctionValue newValue = new FunctionValue_Host(scriptResultBoolTypeDef, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); engine.AddBuiltInFunction(newValue, "Exec"); } //@ global ScriptResult<bool> ExecInline(string) // This executes the given script in the current scope. This is different from Exec, because Exec exists in its own scope. // The returned ScriptResult's value is only true(success) or false (error). { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string script = (string)args[0]; ClassValue scriptResultInst = scriptResultBoolClassDef.Allocate(context); Variable value = scriptResultInst.GetByName("value"); Variable error = scriptResultInst.GetByName("error"); Variable message = scriptResultInst.GetByName("message"); ScriptResult result = context.engine.RunInteractiveScript(script, false); if (null != result.parseErrors) { value.value = false; error.value = scriptErrorEnum.GetValue(result.parseErrors[0].type.ToString());; message.value = result.parseErrors[0].ToString(); } else if (null != result.runtimeError) { value.value = false; error.value = result.value; message.value = result.runtimeError.ToString(); } else { value.value = true; error.value = scriptErrorEnum_noErrorValue; message.value = ""; } return(scriptResultInst); }; FunctionValue newValue = new FunctionValue_Host(scriptResultBoolTypeDef, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); engine.AddBuiltInFunction(newValue, "ExecInline"); } //@ global string Print(...) // Converts all arguments to strings, concatenates them, then outputs the result using the Engine' Log function. // This function can be set to whatever the host program likes: see Engine.Log { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string result = StandardPrintFunction(context, args); context.engine.Log(result); return(result); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new List <ITypeDef> { IntrinsicTypeDefs.ANY }, eval, true); engine.AddBuiltInFunction(newValue, "Print"); } //@ global string ToScript(any) // Returns a script which, when run, returns a value equal to the value passed into ToScript. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object val0 = args[0]; return(ValueToScript(context, val0)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.ANY }, eval, false); engine.AddBuiltInFunction(newValue, "ToScript"); } ///////////////////////////////////////////////////////////////// // Type Conversion //@ global bool ToBool(any) // Attempts to convert input into a boolean value. // 0 and null are false. != 0 and non-null references are true. Strings are handled by Convert.ToBoolean, // which can throw an exception if it doesn't know how to convert the string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object val = args[0]; if (null == val) { return(false); } else if (val is bool) { return((bool)val); } else if (val is double) { return(Convert.ToBoolean((double)val)); } else if (val is string) { try { return(Convert.ToBoolean((string)val)); // this loves to throw errors } catch (Exception e) { context.SetRuntimeError(RuntimeErrorType.ConversionInvalid, "ToBool - C# error: " + e.ToString()); return(null); } } else { return(true); } }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.ANY }, eval, false); engine.AddBuiltInFunction(newValue, "ToBool"); } //@ global num ToNum(any) // Attempts to convert input to a num. // true -> 1, false -> 0, null -> 0, non-null object reference -> 1. Strings are handled by Convert.ToDouble, // which can throw an error if it doesn't know how to convert the string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object val = args[0]; if (null == val) { return(0.0); } else if (val is double) { return((double)val); } else if (val is bool) { return((bool)val ? 1.0 : 0.0); } else if (val is string) { try { return(Convert.ToDouble((string)val)); // this loves to throw errors } catch { context.SetRuntimeError(RuntimeErrorType.ConversionInvalid, "ToNum - Cannot convert string \"" + ((string)val) + "\" to number."); return(null); } } else { return(1.0); } }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.ANY }, eval, false); engine.AddBuiltInFunction(newValue, "ToNum"); } //@ global string ToString(...) // Converts all arguments to strings, concatenates them, and returns the result. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { return(StandardPrintFunction(context, args)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.ANY }, eval, true); engine.AddBuiltInFunction(newValue, "ToString"); } UnitTests.testFuncDelegates.Add("CoreLib", RunTests); }
public bool Write(ExecContext context, PebbleStreamHelper stream, object value) { Pb.Assert(!(value is Variable)); if (null != textWriter) { string s = CoreLib.ValueToString(context, value, false); textWriter.Write(s); return(true); } if (null == value) { writer.Write("null"); } else if (value is FunctionValue) { context.SetRuntimeError(RuntimeErrorType.SerializeUnknownType, "Cannot serialize functions."); return(false); } else if (value is bool) { writer.Write((bool)value); } else if (value is double) { writer.Write((double)value); } else if (value is string) { writer.Write((string)value); } else if (value is PebbleList) { PebbleList plist = value as PebbleList; // - Serialize full type, ie "List<string>". writer.Write(plist.classDef.name); // - Serialize count. writer.Write(plist.list.Count); // - Finally, serialize each object. foreach (Variable listvar in plist.list) { if (!Write(context, stream, listvar.value)) { return(false); } } } else if (value is PebbleDictionary) { PebbleDictionary dic = value as PebbleDictionary; // - class name writer.Write(dic.classDef.name); // - count writer.Write((Int32)dic.dictionary.Count); // - each key, value foreach (var kvp in dic.dictionary) { if (!Write(context, stream, kvp.Key)) { return(false); } if (!Write(context, stream, kvp.Value.value)) { return(false); } } } else if (value is ClassValue_Enum) { ClassValue_Enum enumVal = value as ClassValue_Enum; writer.Write(enumVal.classDef.name); writer.Write(enumVal.GetName()); } else if (value is ClassValue) { ClassValue classVal = value as ClassValue; MemberRef serMemRef = classVal.classDef.GetMemberRef(null, "Serialize", ClassDef.SEARCH.NORMAL); if (serMemRef.isInvalid) { context.SetRuntimeError(RuntimeErrorType.SerializeInvalidClass, "Class '" + classVal.classDef.name + "' cannot be serialized because it doesn't implement a serialization function."); return(false); } writer.Write(classVal.classDef.name); Variable serVar = classVal.Get(serMemRef); FunctionValue serFunc = serVar.value as FunctionValue; object result = serFunc.Evaluate(context, new List <object> { stream }, classVal); if (context.IsRuntimeErrorSet()) { return(false); } if (result is bool && false == (bool)result) { context.SetRuntimeError(RuntimeErrorType.SerializeFailed, "Serialize function of class '" + classVal.classDef.name + "' returned false."); return(false); } } else { throw new Exception("Internal error: Unexpected type of value in stream Write."); } return(true); }
public bool Read(ExecContext context, PebbleStreamHelper stream, Variable variable) { if (variable.type.IsConst()) { context.SetRuntimeError(RuntimeErrorType.SerializeIntoConst, "Cannot serialize into const variables."); return(false); } if (variable.type.CanStoreValue(context, IntrinsicTypeDefs.BOOL)) { variable.value = reader.ReadBoolean(); } else if (variable.type == IntrinsicTypeDefs.NUMBER) { variable.value = reader.ReadDouble(); } else if (variable.type == IntrinsicTypeDefs.STRING) { variable.value = reader.ReadString(); } else if (variable.type.GetName().StartsWith("List<")) { string listTypeName = reader.ReadString(); if ("null" == listTypeName) { variable.value = null; return(true); } // Is it possible that the specific generic class isn't registered yet. if (!EnsureGenericIsRegistered(context, listTypeName)) { return(false); } ClassDef listDef = context.GetClass(listTypeName); if (null == listDef) { context.SetRuntimeError(RuntimeErrorType.SerializeUnknownType, "Cannot deserialize list type '" + listTypeName + "' because it is unknown."); return(false); } ClassValue listValue = listDef.Allocate(context); PebbleList newlist = listValue as PebbleList; variable.value = listValue; ITypeDef elementType = listDef.typeDef.genericTypes[0]; int count = reader.ReadInt32(); for (int ii = 0; ii < count; ++ii) { Variable newelem = new Variable(null, elementType); if (!Read(context, stream, newelem)) { return(false); } newlist.list.Add(newelem); } } else if (variable.type.GetName().StartsWith("Dictionary<")) { string listTypeName = reader.ReadString(); if ("null" == listTypeName) { variable.value = null; return(true); } // Is it possible that the specific generic class isn't registered yet. if (!EnsureGenericIsRegistered(context, listTypeName)) { return(false); } ClassDef listDef = context.GetClass(listTypeName); if (null == listDef) { context.SetRuntimeError(RuntimeErrorType.SerializeUnknownType, "Cannot deserialize list type '" + listTypeName + "' because it is unknown."); return(false); } ClassValue listValue = listDef.Allocate(context); PebbleDictionary newlist = listValue as PebbleDictionary; variable.value = listValue; ITypeDef keyType = listDef.typeDef.genericTypes[0]; ITypeDef valueType = listDef.typeDef.genericTypes[1]; int count = reader.ReadInt32(); Variable tempKeyVar = new Variable("tempKeyVar", keyType); for (int ii = 0; ii < count; ++ii) { if (!Read(context, stream, tempKeyVar)) { return(false); } Variable newelem = new Variable(null, valueType); if (!Read(context, stream, newelem)) { return(false); } newlist.dictionary.Add(tempKeyVar.value, newelem); } } else if (variable.type is TypeDef_Enum) { string enumName = reader.ReadString(); string valueName = reader.ReadString(); // This happens. ITypeDef streamedType = context.GetTypeByName(enumName); if (null == streamedType) { context.SetRuntimeError(RuntimeErrorType.SerializeUnknownType, "Attempt to load saved enum of unknown type '" + enumName + "'."); return(false); } // I can't get this to happen. if (!(streamedType is TypeDef_Enum)) { context.SetRuntimeError(RuntimeErrorType.SerializeUnknownType, "Type '" + enumName + "' saved as something other than an enum, but attempted to stream into an enum variable."); return(false); } ClassDef enumClassDef = context.GetClass(enumName); Pb.Assert(null != enumClassDef, "Somehow we got a type for an enum but not the def."); ClassDef_Enum enumDef = enumClassDef as ClassDef_Enum; Pb.Assert(null != enumClassDef, "Registered type is enum but def is classdef."); // This happens. ClassValue_Enum cve = enumDef.enumDef.GetValue(valueName); if (null == cve) { context.SetRuntimeError(RuntimeErrorType.EnumValueNotFound, "Enum '" + enumName + "' does not have saved value '" + valueName + "'."); return(false); } variable.value = cve; } else if (variable.type is TypeDef_Class) { TypeDef_Class varType = variable.type as TypeDef_Class; // Get class name. string streamClassName = reader.ReadString(); if ("null" == streamClassName) { variable.value = null; return(true); } ITypeDef streamedType = context.GetTypeByName(streamClassName); if (null == streamedType) { context.SetRuntimeError(RuntimeErrorType.SerializeUnknownType, "Serialized type '" + streamClassName + "' not found."); return(false); } if (!varType.CanStoreValue(context, streamedType)) { context.SetRuntimeError(RuntimeErrorType.SerializeTypeMismatch, "Cannot deserialize a '" + streamClassName + "' into a variable of type '" + varType.GetName() + "'."); return(false); } TypeDef_Class streamedClassType = streamedType as TypeDef_Class; Pb.Assert(null != streamedClassType, "Somehow a streamed type is not a class but *can* be stored in a class type?!"); ClassDef streamedClassDef = context.GetClass(streamClassName); Pb.Assert(null != streamedClassDef, "Somehow we got a type for a class but not the def."); MemberRef serMemRef = streamedClassDef.GetMemberRef(null, "Serialize", ClassDef.SEARCH.NORMAL); if (serMemRef.isInvalid) { context.SetRuntimeError(RuntimeErrorType.SerializeInvalidClass, "Serialize function of class '" + streamClassName + "' not found."); return(false); } ClassValue streamedClassValue = streamedClassDef.Allocate(context); Variable serFuncVar = streamedClassValue.Get(serMemRef); FunctionValue serFuncVal = serFuncVar.value as FunctionValue; serFuncVal.Evaluate(context, new List <object>() { stream }, streamedClassValue); if (context.IsRuntimeErrorSet()) { return(false); } variable.value = streamedClassValue; } else { throw new Exception("Internal error: Unexpected type of value in stream Read."); } return(true); }