public virtual string ToString(ExecContext context) { Variable var = GetByName("ThisToString"); if (null != var && var.type is TypeDef_Function) { TypeDef_Function tdf = (TypeDef_Function)var.type; if (null != var.value && tdf.retType.Equals(IntrinsicTypeDefs.STRING) && tdf.argTypes.Count == 0) { FunctionValue funcVal = (FunctionValue)var.value; return((string)funcVal.Evaluate(context, new List <object>(), this)); } } return("[" + classDef.name + " instance]"); }
public static void Register(Engine engine) { //@ class List<T> TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("List", new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, false); ClassDef classDef = engine.defaultContext.CreateClass("List", ourType, null, new List <string> { "T" }); classDef.childAllocator = () => { return(new PebbleList()); }; classDef.Initialize(); //@ List<T> Add(T newValue, ...) or List<T> Push(T newValue, ...) // Adds one or more elements to the end of the list. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { PebbleList scope = thisScope as PebbleList; if (scope.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Add: Attempt to modify a list that is being enumerated by a foreach loop."); return(null); } var list = scope.list; var listType = (TypeDef_Class)scope.classDef.typeDef; var elementType = listType.genericTypes[0]; for (int ii = 0; ii < args.Count; ++ii) { object ret = args[ii]; list.Add(new Variable(null, elementType, ret)); } return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, eval, true, ourType); classDef.AddMemberLiteral("Add", newValue.valType, newValue); classDef.AddMemberLiteral("Push", newValue.valType, newValue); } //@ List<T> Clear() // Removes all elements from the list. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { PebbleList pebList = thisScope as PebbleList; if (pebList.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Clear: Attempt to modify a list that is being enumerated by a foreach loop."); return(null); } var list = pebList.list; list.Clear(); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("Clear", newValue.valType, newValue); } //@ num Count() // Returns the number of elements in the list. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { var list = (thisScope as PebbleList).list; return(System.Convert.ToDouble(list.Count)); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("Count", newValue.valType, newValue); } //@ T Get(num index) // Returns the value of the element of the list at the given index. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { double dix = (double)args[0]; int ix = (int)dix; var list = (thisScope as PebbleList).list; // Bounds checking. if (ix < 0 || ix >= list.Count) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "Get: Index " + ix + " out of bounds of array of length " + list.Count + "."); return(null); } return(list[ix].value); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.TEMPLATE_0, new ArgList { IntrinsicTypeDefs.NUMBER }, eval, false, ourType); classDef.AddMemberLiteral("Get", newValue.valType, newValue); } //@ List<T> Insert(num index, T item) // Inserts a new element into the list at the given index. Existing elements at and after the given index are pushed further down the list. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { PebbleList scope = thisScope as PebbleList; if (scope.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Insert: Attempt to modify a list that is being enumerated by a foreach loop."); return(null); } var list = scope.list; var listType = (TypeDef_Class)scope.classDef.typeDef; var elementType = listType.genericTypes[0]; var indexDouble = (double)args[0]; var item = args[1]; var index = Convert.ToInt32(indexDouble); if (index < 0 || index > list.Count) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "Insert: array index out of bounds."); return(null); } list.Insert(index, new Variable(null, elementType, item)); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.NUMBER, IntrinsicTypeDefs.TEMPLATE_0 }, eval, false, ourType); classDef.AddMemberLiteral("Insert", newValue.valType, newValue); } //@ T Pop() // Returns the value of the last element of the list and removes it from the list. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { PebbleList pebList = thisScope as PebbleList; if (pebList.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Pop: Attempt to remove an element from a list that is being enumerated in a foreach loop."); return(null); } var list = pebList.list; int ix = list.Count - 1; if (ix < 0) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "Pop: List is empty."); return(null); } var result = list[ix].value; list.RemoveAt(ix); return(result); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.TEMPLATE_0, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("Pop", newValue.valType, newValue); } //@ List<T> RemoveAt(num index) // Removes element at the given index, and returns the list. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { double dix = (double)args[0]; int ix = (int)dix; PebbleList pebList = thisScope as PebbleList; if (pebList.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "RemoveAt: Attempt to modify a list that is being enumerated by a foreach loop."); return(null); } var list = pebList.list; if (ix < 0 || ix >= list.Count) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "RemoveAt: Index " + ix + " out of bounds of array of length " + list.Count + "."); return(null); } list.RemoveAt(ix); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.NUMBER }, eval, false, ourType); classDef.AddMemberLiteral("RemoveAt", newValue.valType, newValue); } //@ List<T> RemoveRange(num start, num count) // Removes elements in the given range of indices, and returns the list. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { double dstart = (double)args[0]; int start = (int)dstart; double dcount = (double)args[1]; int count = (int)dcount; PebbleList pebList = thisScope as PebbleList; if (pebList.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "RemoveRange: Attempt to modify a list that is being enumerated by a foreach loop."); return(null); } var list = pebList.list; if (start < 0 || start >= list.Count) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "RemoveRange: Start " + start + " out of bounds of array of length " + list.Count + "."); return(null); } if (count < 0) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "RemoveRange: Count (" + count + ") cannot be negative."); return(null); } if ((start + count) >= list.Count) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "RemoveRange: Count " + count + " exceeds array length (" + list.Count + ")."); return(null); } list.RemoveRange(start, count); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.NUMBER, IntrinsicTypeDefs.NUMBER }, eval, false, ourType); classDef.AddMemberLiteral("RemoveRange", newValue.valType, newValue); } //@ List<T> Reverse() // Reverses the list and returns it. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { var list = (thisScope as PebbleList).list; list.Reverse(); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("Reverse", newValue.valType, newValue); } //@ List<T> Set(num index, T newValue) // Changes the value of the element at the given index, and returns the list. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { double dix = (double)args[0]; int ix = (int)dix; object value = args[1]; var list = (thisScope as PebbleList).list; // Bounds checking. if (ix < 0 || ix >= list.Count) { context.SetRuntimeError(RuntimeErrorType.ArrayIndexOutOfBounds, "Set: Index " + ix + " out of bounds of array of length " + list.Count + "."); return(null); } list[ix].value = value; return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.NUMBER, IntrinsicTypeDefs.TEMPLATE_0 }, eval, false, ourType); classDef.AddMemberLiteral("Set", newValue.valType, newValue); } //@ List<T> Shuffle() // Shuffles the list, putting the elements in random order. // Returns the list. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { var list = (thisScope as PebbleList).list; Random rng = new Random(); int n = list.Count; while (n > 1) { --n; int k = rng.Next(n + 1); Variable value = list[k]; list[k] = list[n]; list[n] = value; } return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("Shuffle", newValue.valType, newValue); } //@ List<T> Sort(functype<num(T, T>)> comparator) // Sorts the list using the given comparator function. // The comparator should behave the same as a C# Comparer. The first argument should be earlier in the // list than the second, return a number < 0. If It should be later, return a number > 0. If their order // is irrelevant, return 0. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { var list = (thisScope as PebbleList).list; if (null == args[0]) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Sort: comparator may not be null."); return(null); } FunctionValue comparator = (FunctionValue)args[0]; List <object> argvals = new List <object>(); argvals.Add(0); argvals.Add(0); Comparison <Variable> hostComparator = new Comparison <Variable>( (a, b) => { argvals[0] = a.value; argvals[1] = b.value; // Note we use null instead of thisScope here. There is no way the sort function could be a // class member because Sort's signature only takes function's whose type has no class. double result = (double)comparator.Evaluate(context, argvals, null); return(Convert.ToInt32(result)); } ); list.Sort(hostComparator); return(thisScope); }; TypeDef_Function comparatorType = TypeFactory.GetTypeDef_Function(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.TEMPLATE_0, IntrinsicTypeDefs.TEMPLATE_0 }, -1, false, null, false, false); FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { comparatorType }, eval, false, ourType); classDef.AddMemberLiteral("Sort", 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 list = (thisScope as PebbleList).list; for (int ii = 0; ii < list.Count; ++ii) { result += prefix + "Add(" + CoreLib.ValueToScript(context, list[ii].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 string representation of at least the first few elements of the list. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { var list = (thisScope as PebbleList).list; string result = "List(" + list.Count + ")["; for (int ii = 0; ii < Math.Min(4, list.Count); ++ii) { if (ii > 0) { result += ", "; } result += CoreLib.ValueToString(context, list[ii].value, true); } if (list.Count > 4) { result += ", ..."; } 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); UnitTests.testFuncDelegates.Add("CoreList", RunTests); }
public static string ValueToScript(ExecContext context, object value, string prefix = "", bool topLevel = true) { string result = ""; string postfix = topLevel ? ";" : ""; if (value is double) { return(Convert.ToString((double)value) + postfix); } else if (value is bool) { return(((bool)value ? "true" : "false") + postfix); } else if (value is string) { return("\"" + (string)value + "\"" + postfix); } else if (value is ClassValue_Enum) // is enum { return(((ClassValue_Enum)value).ToString() + postfix); } else if (value is ClassValue) { ClassValue table = value as ClassValue; result = "new " + table.classDef.typeDef.ToString() + " {\n"; // ThisToScript must not be static. It's going to be printing info about a class instance, right? MemberRef toStrMem = table.classDef.GetMemberRef(null, "ThisToScript", ClassDef.SEARCH.NORMAL); Variable funcVar = null; if (!toStrMem.isInvalid) { funcVar = table.Get(toStrMem) as Variable; } if (null != funcVar) { FunctionValue funcVal = funcVar.value as FunctionValue; result += funcVal.Evaluate(context, new List <object> { prefix }, table) as string; } else { foreach (Variable kvp in table.fieldVars) { if (!(kvp.type is TypeDef_Function) && !kvp.type.IsConst()) { result += prefix + "\t" + kvp.name + " = " + ValueToScript(context, kvp.value, prefix + "\t", false); if (!result.EndsWith(";")) { result += ";"; } result += "\n"; } } } result += prefix + "}" + postfix; } else if (null == value) { result = "null" + postfix; } return(result); }
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); }