Пример #1
0
            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);
            }
Пример #2
0
        public static void Register(Engine engine)
        {
            //@ class Dictionary<K, V>
            TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("Dictionary", new ArgList {
                IntrinsicTypeDefs.TEMPLATE_0, IntrinsicTypeDefs.TEMPLATE_1
            }, false);

            ClassDef classDef = engine.defaultContext.CreateClass("Dictionary", ourType, null, new List <string> {
                "K", "V"
            });

            classDef.childAllocator = () => {
                return(new PebbleDictionary());
            };
            classDef.Initialize();

            //@ Dictionary<K, V> Add(K key, V value)
            //  Adds a new element to the dictionary.
            //   Cannot be used in a foreach loop.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    object key   = args[0];
                    object value = args[1];

                    PebbleDictionary scope = thisScope as PebbleDictionary;
                    if (scope.enumeratingCount > 0)
                    {
                        context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Add: Attempt to modify a dictionary that is being enumerated by a foreach loop.");
                        return(null);
                    }

                    var dictionary     = scope.dictionary;
                    var dictionaryType = (TypeDef_Class)scope.classDef.typeDef;
                    var valueType      = dictionaryType.genericTypes[1];
                    if (dictionary.ContainsKey(key))
                    {
                        context.SetRuntimeError(RuntimeErrorType.KeyAlreadyExists, "Dictionary already contains key '" + key + "'.");
                        return(null);
                    }

                    dictionary.Add(key, new Variable(null, valueType, value));

                    return(thisScope);
                };

                FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList {
                    IntrinsicTypeDefs.TEMPLATE_0, IntrinsicTypeDefs.TEMPLATE_1
                }, eval, false, ourType);
                classDef.AddMemberLiteral("Add", newValue.valType, newValue);
            }

            //@ Dictionary<K, V> Clear()
            //  Removes all elements from the dictionary.
            //   Cannot be used in a foreach loop.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    PebbleDictionary pebDict = thisScope as PebbleDictionary;
                    var dictionary           = pebDict.dictionary;
                    if (pebDict.enumeratingCount > 0)
                    {
                        context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Clear: Attempt to modify a dictionary that is being enumerated by a foreach loop.");
                        return(null);
                    }

                    dictionary.Clear();
                    return(thisScope);
                };

                FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList {
                }, eval, false, ourType);
                classDef.AddMemberLiteral("Clear", newValue.valType, newValue);
            }

            //@ bool ContainsKey(K)
            //  Returns true iff the dictionary contains an element with the given key.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    object key = args[0];

                    PebbleDictionary pebDict = thisScope as PebbleDictionary;
                    var dictionary           = pebDict.dictionary;

                    return(dictionary.ContainsKey(key));
                };

                FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList {
                    IntrinsicTypeDefs.TEMPLATE_0
                }, eval, false, ourType);
                classDef.AddMemberLiteral("ContainsKey", newValue.valType, newValue);
            }

            //@ num Count()
            //  Returns number of elements in the dictionary.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    var dictionary = (thisScope as PebbleDictionary).dictionary;
                    return(System.Convert.ToDouble(dictionary.Count));
                };

                FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList {
                }, eval, false, ourType);
                classDef.AddMemberLiteral("Count", newValue.valType, newValue);
            }

            //@ V Get(K)
            //  Returns the value of the element with the given key.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    object key = args[0];

                    PebbleDictionary pebDict = thisScope as PebbleDictionary;

                    // Bounds checking.
                    var dictionary = pebDict.dictionary;
                    if (!dictionary.ContainsKey(key))
                    {
                        context.SetRuntimeError(RuntimeErrorType.KeyNotFound, "Get: Key '" + key + "' not in dictionary.");
                        return(null);
                    }

                    return(dictionary[key].value);
                };

                FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.TEMPLATE_1, new ArgList {
                    IntrinsicTypeDefs.TEMPLATE_0
                }, eval, false, ourType);
                classDef.AddMemberLiteral("Get", newValue.valType, newValue);
            }

            //@ Dictionary<K, V> Remove(K key)
            //   Removes element with given key.
            //   Cannot be used in a foreach loop.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    object key = args[0];

                    PebbleDictionary pebDict = thisScope as PebbleDictionary;
                    var dictionary           = pebDict.dictionary;
                    if (pebDict.enumeratingCount > 0)
                    {
                        context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Remove: Attempt to modify a dictionary that is being enumerated by a foreach loop.");
                        return(null);
                    }

                    if (!dictionary.ContainsKey(key))
                    {
                        context.SetRuntimeError(RuntimeErrorType.KeyNotFound, "Remove: Key '" + key + "' not in dictionary.");
                        return(null);
                    }

                    dictionary.Remove(key);
                    return(thisScope);
                };

                FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList {
                    IntrinsicTypeDefs.TEMPLATE_0
                }, eval, false, ourType);
                classDef.AddMemberLiteral("Remove", newValue.valType, newValue);
            }

            //@ Dictionary<K, V> Set(K key, V newValue)
            //   Replaces value of existing element with the given key.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    object key   = args[0];
                    object value = args[1];

                    var dictionary = (thisScope as PebbleDictionary).dictionary;

                    // Bounds checking.
                    if (!dictionary.ContainsKey(key))
                    {
                        context.SetRuntimeError(RuntimeErrorType.KeyNotFound, "Get: Key '" + key + "' not in dictionary.");
                        return(null);
                    }

                    dictionary[key].value = value;

                    return(thisScope);
                };

                FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList {
                    IntrinsicTypeDefs.TEMPLATE_0, IntrinsicTypeDefs.TEMPLATE_1
                }, eval, false, ourType);
                classDef.AddMemberLiteral("Set", newValue.valType, newValue);
            }

            //@ string ThisToScript(string prefix)
            //   ThisToScript is used by Serialize. A classes' ThisToScript function should return code which can rebuild the class.
            //   Note that it's only the content of the class, not the "new A" part. ie., it's the code that goes in the defstructor.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    string result = "";
                    string prefix = (string)args[0] + "\t";

                    var dictionary = (thisScope as PebbleDictionary).dictionary;
                    //bool first = true;
                    foreach (KeyValuePair <object, Variable> kvp in dictionary)
                    {
                        result += prefix + "Add(" + CoreLib.ValueToScript(context, kvp.Key, prefix + "\t", false) + ", " + CoreLib.ValueToScript(context, kvp.Value.value, prefix + "\t", false) + ");\n";
                    }

                    return(result);
                };

                FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList {
                    IntrinsicTypeDefs.STRING
                }, eval, false, ourType);
                classDef.AddMemberLiteral("ThisToScript", newValue.valType, newValue);
            }

            //@ string ToString()
            //   Returns a human readable version of at least the first few elements of the dictionary.
            {
                FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => {
                    var dictionary = (thisScope as PebbleDictionary).dictionary;

                    string result = "Dictionary(" + dictionary.Count + ")[";
                    int    count  = 0;
                    foreach (KeyValuePair <object, Variable> kvp in dictionary)
                    {
                        if (count != 0)
                        {
                            result += ", ";
                        }
                        result += "(" + CoreLib.ValueToString(context, kvp.Key, true) + ", " + CoreLib.ValueToString(context, kvp.Value.value, true) + ")";

                        if (++count >= 4)
                        {
                            break;
                        }
                    }
                    if (dictionary.Count > 4)
                    {
                        result += ", ...";
                    }
                    return(result + "]");
                };

                FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList {
                }, eval, false, ourType);
                classDef.AddMemberLiteral("ThisToString", newValue.valType, newValue);
            }

            bool error = false;

            classDef.FinalizeClass(engine.defaultContext);
            Pb.Assert(!error);

            UnitTests.testFuncDelegates.Add("CoreDictionary", RunTests);
        }
Пример #3
0
            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);
            }