// NOTE: isStatic = true has never been tested. Library functions are static but are neither flagged as static nor as class members, // so references can be saved to them by users already. public FunctionValue_Host(ITypeDef _retType, List <ITypeDef> _argTypes, EvaluateDelegate _Evaluate, bool _varargs = false, TypeDef_Class classType = null, bool isStatic = false, List <Expr_Literal> defaultArgVals = null) { argDefaultValues = defaultArgVals; BuildArgHasDefaults(_argTypes.Count); valType = TypeFactory.GetTypeDef_Function(_retType, _argTypes, minArgs, _varargs, classType, true, isStatic); Evaluate = _Evaluate; }
public virtual ITypeDef ResolveTemplateTypes(List <ITypeDef> genericTypes, ref bool modified) { List <ITypeDef> args = new List <ITypeDef>(); for (int ii = 0; ii < argTypes.Count; ++ii) { args.Add(argTypes[ii].ResolveTemplateTypes(genericTypes, ref modified)); } ITypeDef newRetType = retType.ResolveTemplateTypes(genericTypes, ref modified); if (null != classType) { classType.ResolveTemplateTypes(genericTypes, ref modified); } return(TypeFactory.GetTypeDef_Function(newRetType, args, minArgs, varargs, classType, isConst, isStaticMember)); }
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 override string ToString() { * string argString = "ARGS"; * return (_isConst ? "const " : "") + "<" + retType + "(" + argString + ")>"; * } */ public override ITypeDef Resolve(ExecContext context, ref bool error) { ITypeDef retValType = retType.Resolve(context, ref error); if (null == retValType) { context.engine.LogCompileError(ParseErrorType.TypeNotFound, "Type '" + retType + "' not found."); error = true; return(null); } if (null != argHasDefaults && argHasDefaults.Count < argTypes.Count) { context.engine.LogCompileError(ParseErrorType.DefaultArgGap, "An argument with a default value is followed by an argument without one."); error = true; return(null); } int minArgs = argTypes.Count; if (null != argHasDefaults) { int firstDefault = -1; for (int ii = 0; ii < argHasDefaults.Count; ++ii) { if (argHasDefaults[ii]) { if (firstDefault < 0) { firstDefault = ii; minArgs = ii; } } else if (firstDefault >= 0) { context.engine.LogCompileError(ParseErrorType.DefaultArgGap, "An argument with a default value is followed by an argument without one."); error = true; return(null); } } } List <ITypeDef> argValTypes = new List <ITypeDef>(); for (int ii = 0; ii < argTypes.Count; ++ii) { ITypeDef argValType = argTypes[ii].Resolve(context, ref error); if (null == argValType) { context.engine.LogCompileError(ParseErrorType.TypeNotFound, "Type '" + argTypes[ii] + "' not found."); error = true; return(null); } argValTypes.Add(argValType); } TypeDef_Class classType = null; if (null != className) { ClassDef classDef = context.GetClass(className); if (null == classDef) { Pb.Assert(false, "Internal error: error resolving class type."); } classType = classDef.typeDef; } var ret = TypeFactory.GetTypeDef_Function(retValType, argValTypes, minArgs, varArgs, classType, _isConst, false); return(ret); }