public static PebbleList AllocateListString(ExecContext context, string debugName = "(List<string> inst)") { if (null == _listClassDef) { _listClassDef = context.GetClass("List<string>"); } PebbleList listinst = (PebbleList)_listClassDef.childAllocator(); listinst.classDef = _listClassDef; listinst.debugName = debugName; return(listinst); }
public static void Register(Engine engine) { //@ class RegexGroup // Stores information about a Regex group match. Basically a wrapper for System.Text.RegularExpressions.Group. { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("RegexGroup", null, false); ClassDef classDef = engine.defaultContext.CreateClass("RegexGroup", ourType, null, null); classDef.Initialize(); //@ const num index; // The index of the character at the start of this match. classDef.AddMember("index", IntrinsicTypeDefs.CONST_NUMBER); //@ const num length; // The length of the substring of this match. classDef.AddMember("length", IntrinsicTypeDefs.CONST_NUMBER); //@ const string value; // The substring of this match. classDef.AddMember("value", IntrinsicTypeDefs.CONST_STRING); classDef.FinalizeClass(engine.defaultContext); regexGroupClassDef = classDef; } // Make sure the List<RegexGroup> type is registered. listRegexGroupClassDef = engine.defaultContext.RegisterIfUnregisteredList(regexGroupClassDef.typeDef); //@ class RegexMatch // Stores information about a single Regex substring match. Basically a wrapper for System.Text.RegularExpressions.Match. { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("RegexMatch", null, false); ClassDef classDef = engine.defaultContext.CreateClass("RegexMatch", ourType, null, null); classDef.Initialize(); //@ const num index; // The index of the character at the start of this match. classDef.AddMember("index", IntrinsicTypeDefs.CONST_NUMBER); //@ const num length; // The length of the substring of this match. classDef.AddMember("length", IntrinsicTypeDefs.CONST_NUMBER); //@ const string value; // The substring of this match. classDef.AddMember("value", IntrinsicTypeDefs.CONST_STRING); //@ List<RegexGroup> groups; // The regex groups of this match. If there are no groups this will be null. classDef.AddMember("groups", listRegexGroupClassDef.typeDef); classDef.FinalizeClass(engine.defaultContext); regexMatchClassDef = classDef; } // **************************************************************** // Make sure the List<string> type is registered. ClassDef listStringClassDef = engine.defaultContext.RegisterIfUnregisteredList(IntrinsicTypeDefs.STRING); // Make sure List<RegexMatch> is registered. listMatchClassDef = engine.defaultContext.RegisterIfUnregisteredList(regexMatchClassDef.typeDef); // **************************************************************** //@ class Regex // Provides static functions that implement regular expression matching for strings. Basically a wrapper for System.Text.RegularExpressions.Regex. { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("Regex", null, false); ClassDef classDef = engine.defaultContext.CreateClass("Regex", ourType, null, null, true, true); classDef.Initialize(); // *** //@ static bool IsMatch(string input, string expression) // Returns true if input matches the given regular expression. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; return(Regex.IsMatch(a, b)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("IsMatch", newValue.valType, newValue, true); } //@ static RegexMatch Match(string input, string pattern); // Returns the first match of the given pattern in the input string, or null if no match. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string input = (string)args[0]; string pattern = (string)args[1]; Match match = Regex.Match(input, pattern); if (match.Success) { ClassValue matchInst = regexMatchClassDef.Allocate(context); Variable index = matchInst.GetByName("index"); Variable length = matchInst.GetByName("length"); Variable value = matchInst.GetByName("value"); index.value = Convert.ToDouble(match.Index); length.value = Convert.ToDouble(match.Length); value.value = match.Value; return(matchInst); } return(null); }; FunctionValue newValue = new FunctionValue_Host(regexMatchClassDef.typeDef, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Match", newValue.valType, newValue, true); } //@ static List<RegexMatch> Matches(string input, string pattern); // Returns a list of all the matches of the given regular expression in the input string, or null if no matches found. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string input = (string)args[0]; string pattern = (string)args[1]; MatchCollection matchCol = Regex.Matches(input, pattern); if (0 == matchCol.Count) { return(null); } ClassValue matchListInst = listMatchClassDef.Allocate(context); PebbleList pebbleList = matchListInst as PebbleList; foreach (Match match in matchCol) { ClassValue matchInst = regexMatchClassDef.Allocate(context); Variable index = matchInst.GetByName("index"); Variable length = matchInst.GetByName("length"); Variable value = matchInst.GetByName("value"); index.value = Convert.ToDouble(match.Index); length.value = Convert.ToDouble(match.Length); value.value = match.Value; // Note: In this C# regex library, 0 is always the default group (it is the whole match). // That doesn't seem to be a regex standard, and it's entirely rendundant, so I'm only // taking the non-default groups. match.groups is 0 when there are no non-default groups. if (match.Groups.Count > 1) { ClassValue groupListInst = listRegexGroupClassDef.Allocate(context); PebbleList groupList = groupListInst as PebbleList; matchInst.GetByName("groups").value = groupListInst; for (int ii = 1; ii < match.Groups.Count; ++ii) { Group group = match.Groups[ii]; ClassValue groupInst = regexGroupClassDef.Allocate(context); groupInst.GetByName("index").value = Convert.ToDouble(group.Index); groupInst.GetByName("length").value = Convert.ToDouble(group.Length); groupInst.GetByName("value").value = group.Value; groupList.list.Add(new Variable("(made my Regex::Matches)", groupInst.classDef.typeDef, groupInst)); } } pebbleList.list.Add(new Variable("(Regex::Matches)", regexMatchClassDef.typeDef, matchInst)); } return(matchListInst); }; FunctionValue newValue = new FunctionValue_Host(listMatchClassDef.typeDef, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Matches", newValue.valType, newValue, true); } //@ static string Replace(string input, string pattern, string replacement); // Replace any matches of the given pattern in the input string with the replacement string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string input = (string)args[0]; string pattern = (string)args[1]; string replacement = (string)args[2]; return(Regex.Replace(input, pattern, replacement)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Replace", newValue.valType, newValue, true); } //@ static List<string> Split(string input, string pattern); // Splits an input string into an array of substrings at the positions defined by a regular expression match. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string input = (string)args[0]; string pattern = (string)args[1]; string[] splitArray = Regex.Split(input, pattern); ClassValue inst = listStringClassDef.Allocate(context); inst.debugName = "(Regex::Split result)"; PebbleList list = (PebbleList)inst; foreach (string str in splitArray) { list.list.Add(new Variable(null, IntrinsicTypeDefs.STRING, str)); } return(inst); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.LIST_STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Split", newValue.valType, newValue, true); } // *** classDef.FinalizeClass(engine.defaultContext); } UnitTests.testFuncDelegates.Add("RegexLib", RunTests); }
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 void Register(Engine engine) { //@ class String TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("String", null, false); ClassDef classDef = engine.defaultContext.CreateClass("String", ourType, null, null, true); classDef.Initialize(); // This makes sure List<num> is registered in the type library, as this class uses it and we can't rely // scripts to register it. engine.defaultContext.RegisterIfUnregisteredList(IntrinsicTypeDefs.NUMBER); //@ static num CompareTo(string, string) // Wraps C# CompareTo function, which essentially returns a number < 0 if a comes before b // alphabetically, > 0 if a comes after b, and 0 if they are identical. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; return(Convert.ToDouble(a.CompareTo(b))); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("CompareTo", newValue.valType, newValue, true); } //@ static string Concat(any[, ...]) // Converts all arguments to strings and concatenates them. Same as ToString. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { return(CoreLib.StandardPrintFunction(context, args)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.ANY }, eval, true); classDef.AddMemberLiteral("Concat", newValue.valType, newValue, true); } //@ static bool EndsWith(string s, string search) // Wrapper for C# EndsWith. Returns true if s ends with search. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; return(a.EndsWith(b)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("EndsWith", newValue.valType, newValue, true); } //@ static bool Equals(string, string) // Returns true iff the strings are exactly equal. The same thing as using the == operator. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; return(a.Equals(b)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Equals", newValue.valType, newValue, true); } //@ static bool EqualsI(string, string) // Returns true if the strings are equal, ignoring case. Equivalent to the ~= operator. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; return(a.ToLower().Equals(b.ToLower())); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("EqualsI", newValue.valType, newValue, true); } //@ static string Format(string[, any, ...]) // Generated formatted strings. Wrapper for C# String.Format(string, object[]). See documentation of that function for details. // Putting weird things like Lists or functions into the args will produce undefined results. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string source = (string)args[0]; Object[] a = new Object[args.Count - 1]; args.CopyTo(1, a, 0, args.Count - 1); string result = ""; try { result = String.Format(source, a); } catch (Exception e) { context.SetRuntimeError(RuntimeErrorType.NativeException, e.ToString()); return(null); } return(result); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.ANY }, eval, true); classDef.AddMemberLiteral("Format", newValue.valType, newValue, true); } //@ static List<string> GetCharList(string) // Returns a list of strings containing one character of the input string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string source = (string)args[0]; PebbleList list = PebbleList.AllocateListString(context, "String::GetCharList result"); foreach (char c in source.ToCharArray()) { list.list.Add(new Variable(null, IntrinsicTypeDefs.STRING, Convert.ToString(c))); } return(list); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.LIST_STRING, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("GetCharList", newValue.valType, newValue, true); } //@ static List<num> GetUnicode(string) // Returns the Unicode numeric values for the characters in the input string. // Returns an empty list if the string is empty. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string source = (string)args[0]; PebbleList list = PebbleList.AllocateListString(context, "String::GetUnicode result"); foreach (char c in source.ToCharArray()) { list.list.Add(new Variable(null, IntrinsicTypeDefs.NUMBER, Convert.ToDouble(Convert.ToInt32(c)))); } return(list); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.LIST_NUMBER, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("GetUnicode", newValue.valType, newValue, true); } //@ static num IndexOfChar(string toBeSearched, string searchChars, num startIndex = 0) // Returns the index of the first instance of any of the characters in search. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; int startIndex = Convert.ToInt32((double)args[2]); if (0 == a.Length || 0 == b.Length) { return(-1.0); } int lowestIx = Int32.MaxValue; foreach (char c in b) { int ix = a.IndexOf(c, startIndex); if (ix >= 0 && ix < lowestIx) { lowestIx = ix; } } if (lowestIx < Int32.MaxValue) { return(Convert.ToDouble(lowestIx)); } return(-1.0); }; List <Expr_Literal> defaultArgVals = new List <Expr_Literal>(); defaultArgVals.Add(null); defaultArgVals.Add(null); defaultArgVals.Add(new Expr_Literal(null, 0.0, IntrinsicTypeDefs.NUMBER)); FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER }, eval, false, null, true, defaultArgVals); classDef.AddMemberLiteral("IndexOfChar", newValue.valType, newValue, true); } //@ static num IndexOfString(string toBeSearched, string searchString, num startIndex = 0) // Returns the index of the first instance of the entire search string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; int startIndex = Convert.ToInt32((double)args[2]); if (0 == a.Length || 0 == b.Length) { return(-1.0); } int ix = a.IndexOf(b, startIndex); return(Convert.ToDouble(ix)); }; List <Expr_Literal> defaultArgVals = new List <Expr_Literal>(); defaultArgVals.Add(null); defaultArgVals.Add(null); defaultArgVals.Add(new Expr_Literal(null, 0.0, IntrinsicTypeDefs.NUMBER)); FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER }, eval, false, null, true, defaultArgVals); classDef.AddMemberLiteral("IndexOfString", newValue.valType, newValue, true); } //@ static num LastIndexOfChar(string toBeSearched, string searchChars, num startIndex = -1) // Returns the index of the last instance of the entire search string, // If startIndex is >= 0, starts searching backwards from the given index. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; int startIndex = Convert.ToInt32((double)args[2]); if (0 == a.Length || 0 == b.Length) { return(-1.0); } if (startIndex < 0) { startIndex = a.Length - 1; } else if (startIndex >= a.Length) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "LastIndexOfChar startIndex argument is greater than the length of the string."); return(null); } char[] chars = b.ToCharArray(); int lowestIx = Int32.MaxValue; foreach (char c in b) { int ix = a.LastIndexOfAny(chars, startIndex); if (ix >= 0 && ix < lowestIx) { lowestIx = ix; } } if (lowestIx < Int32.MaxValue) { return(Convert.ToDouble(lowestIx)); } return(-1.0); }; List <Expr_Literal> defaultArgVals = new List <Expr_Literal>(); defaultArgVals.Add(null); defaultArgVals.Add(null); defaultArgVals.Add(new Expr_Literal(null, -1.0, IntrinsicTypeDefs.NUMBER)); FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER }, eval, false, null, true, defaultArgVals); classDef.AddMemberLiteral("LastIndexOfChar", newValue.valType, newValue, true); } //@ static num LastIndexOfString(string toBeSearched, string searchString, num startIndex = -1) // Returns the index of the last instance of the entire search string, // If startIndex is > 0, starts searching backwards from the given index. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; int startIndex = Convert.ToInt32((double)args[2]); if (0 == a.Length || 0 == b.Length) { return(-1.0); } if (startIndex < 0) { startIndex = a.Length - 1; } else if (startIndex >= a.Length) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "LastIndexOfString startIndex argument is greater than the length of the string."); return(null); } int ix = a.LastIndexOf(b, startIndex); return(Convert.ToDouble(ix)); }; List <Expr_Literal> defaultArgVals = new List <Expr_Literal>(); defaultArgVals.Add(null); defaultArgVals.Add(null); defaultArgVals.Add(new Expr_Literal(null, -1.0, IntrinsicTypeDefs.NUMBER)); FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER }, eval, false, null, true, defaultArgVals); classDef.AddMemberLiteral("LastIndexOfString", newValue.valType, newValue, true); } //@ static num Length(string) // Returns the length of the string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; return(Convert.ToDouble(a.Length)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Length", newValue.valType, newValue, true); } //@ static string PadLeft(string, num n, string pad) // Returns s with n instances of string pad to the left. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; double nd = (double)args[1]; int n = Convert.ToInt32(nd); string p = (string)args[2]; if (0 == p.Length) { p = " "; } char c = p[0]; return(a.PadLeft(n, c)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("PadLeft", newValue.valType, newValue, true); } //@ static string PadRight(string s, num n, string pad) // Returns s with n instances of string pad to the left. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; double nd = (double)args[1]; int n = Convert.ToInt32(nd); string p = (string)args[2]; if (0 == p.Length) { p = " "; } char c = p[0]; return(a.PadRight(n, c)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("PadRight", newValue.valType, newValue, true); } //@ static string Replace(string str, string find, string replace) // Replaces all instances of the given string with the replacement string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string find = (string)args[1]; string replace = (string)args[2]; if (0 == find.Length) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Find argument to Replace() cannot be the empty string."); return(null); } return(a.Replace(find, replace)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Replace", newValue.valType, newValue, true); } //@ static List<string> Split(string str, List<string> separators = null) // Splits input string into a list of strings given the provided separators. // If no separators are provided, uses the newline character. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string[] splitted; if (null == args[1]) { splitted = a.Split(_defaultSeparators, StringSplitOptions.None); } else { PebbleList delimsList = args[1] as PebbleList; if (0 == delimsList.list.Count) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "String::Split : Separators list cannot be empty."); return(null); } List <string> dlist = delimsList.GetNativeList(); // Usually I like to wrap native functions with a try-catch but I couldn't find a // way to make this throw an exception. splitted = a.Split(dlist.ToArray(), StringSplitOptions.None); } PebbleList list = PebbleList.AllocateListString(context, "String::Split result"); foreach (string s in splitted) { list.list.Add(new Variable(null, IntrinsicTypeDefs.STRING, s)); } return(list); }; List <Expr_Literal> defaults = new List <Expr_Literal>(); defaults.Add(null); defaults.Add(new Expr_Literal(null, null, IntrinsicTypeDefs.NULL)); FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.LIST_STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.LIST_STRING }, eval, false, null, true, defaults); classDef.AddMemberLiteral("Split", newValue.valType, newValue, true); } //@ static bool StartsWith(string s, string start) // Returns true if s starts with start. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; string b = (string)args[1]; return(a.StartsWith(b)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("StartsWith", newValue.valType, newValue, true); } //@ static string Substring(string, startIx, length) // Returns a substring of the input, starting at startIx, that is length characters long. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; double b = (double)args[1]; double c = (double)args[2]; int start = Convert.ToInt32(b); int len = Convert.ToInt32(c); if (start < 0 || len < 0) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Numeric arguments to Substring cannot be negative."); return(null); } if (start + len > a.Length) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Substring attempting to read past end string."); return(null); } return(a.Substring(start, len)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER, IntrinsicTypeDefs.NUMBER }, eval, false); classDef.AddMemberLiteral("Substring", newValue.valType, newValue, true); } //@ static string SubstringLeft(string str, num length) // Returns the left 'length' characters of str. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; double b = (double)args[1]; int len = Convert.ToInt32(b); if (len < 0) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Numeric arguments to Substring cannot be negative."); return(null); } if (len > a.Length) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Substring attempting to read past end string."); return(null); } return(a.Substring(0, len)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER }, eval, false); classDef.AddMemberLiteral("SubstringLeft", newValue.valType, newValue, true); } //@ static string SubstringRight(string, start) // Returns the right part of the string starting at 'start'. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; double b = (double)args[1]; int start = Convert.ToInt32(b); if (start < 0) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Numeric arguments to Substring cannot be negative."); return(null); } if (start >= a.Length) { context.SetRuntimeError(RuntimeErrorType.ArgumentInvalid, "Substring attempting to read past end string."); return(null); } return(a.Substring(a.Length - start)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.NUMBER }, eval, false); classDef.AddMemberLiteral("SubstringRight", newValue.valType, newValue, true); } //@ static string ToLower(string) // Converts the string to lowercase. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; return(a.ToLower()); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("ToLower", newValue.valType, newValue, true); } //@ static string ToUpper(string) // Converts the string to uppercase. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; return(a.ToUpper()); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("ToUpper", newValue.valType, newValue, true); } //@ static string Trim(string) // Removes leading and trailing whitespace characters. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string a = (string)args[0]; return(a.Trim()); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Trim", newValue.valType, newValue, true); } //@ static string UnicodeToString(List<num>) // Takes a list of numeric Unicode character codes, converts them to characters, concatenates them, and returns the resultant string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { PebbleList list = args[0] as PebbleList; string result = ""; try { foreach (Variable v in list.list) { double d = (double)v.value; result += Convert.ToChar(Convert.ToInt32(d)); } } catch (Exception e) { context.SetRuntimeError(RuntimeErrorType.NativeException, e.ToString()); return(null); } return(result); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.LIST_NUMBER }, eval, false); classDef.AddMemberLiteral("UnicodeToString", newValue.valType, newValue, true); } classDef.FinalizeClass(engine.defaultContext); UnitTests.testFuncDelegates.Add("StringLib", RunTests); }
public static void Register(Engine engine) { // *** Register required types. List <ITypeDef> genericTypes = new ArgList(); genericTypes.Add(IntrinsicTypeDefs.STRING); // This code makes sure that Result<string> is a registered class and type. TypeDef_Class resultTypeDef = TypeFactory.GetTypeDef_Class("Result", genericTypes, false); ClassDef resultClassDef = engine.defaultContext.RegisterIfUnregisteredTemplate(resultTypeDef); // This does List<string>. ClassDef listStringClassDef = engine.defaultContext.RegisterIfUnregisteredList(IntrinsicTypeDefs.STRING); // *** //@ class File TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("File", null, false); ClassDef classDef = engine.defaultContext.CreateClass("File", ourType, null, null, true); classDef.Initialize(); //@ const string lastError; // Stores the message of the last error generated by this library. classDef.AddMemberLiteral("lastError", IntrinsicTypeDefs.CONST_STRING, "", true); Variable lastErrorVar = null; //@ static string ClearLastError() // Clears File::lastError, returning its previous value. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string lastError = (string)lastErrorVar.value; lastErrorVar.value = ""; return(lastError); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false); classDef.AddMemberLiteral("ClearLastError", newValue.valType, newValue, true); } //@ static bool Copy(string source, string dest) // Copies a file from source to dest. // Returns false and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string source = (string)args[0]; string dest = (string)args[1]; lastErrorVar.value = ""; try { File.Copy(source, dest); } catch (Exception e) { lastErrorVar.value = "Copy: " + e.ToString(); return(false); } return(true); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Copy", newValue.valType, newValue, true); } //@ static bool CreateDir(string path) // Creates directory. // Returns false and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; lastErrorVar.value = ""; // Doesn't throw exceptions. if (Directory.Exists(path)) { lastErrorVar.value = "CreateDir: Directory already exists."; return(false); } try { Directory.CreateDirectory(path); } catch (Exception e) { lastErrorVar.value = "CreateDir: " + e.ToString(); return(false); } return(true); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("CreateDir", newValue.valType, newValue, true); } //@ static bool Delete(string path) // Returns true if file deleted. // Returns false and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; // Doesn't throw exceptions. if (!File.Exists(path)) { lastErrorVar.value = "Delete: File not found."; return(false); } try { File.Delete(path); } catch (Exception e) { lastErrorVar.value = "Delete: " + e.ToString(); return(false); } lastErrorVar.value = ""; return(true); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Delete", newValue.valType, newValue, true); } //@ static bool DeleteDir(string path) // Returns true if file deleted. // Returns false and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; // Doesn't throw exceptions. if (!Directory.Exists(path)) { lastErrorVar.value = "DeleteDir: Directory not found."; return(false); } try { Directory.Delete(path); } catch (Exception e) { lastErrorVar.value = "DeleteDir: " + e.ToString(); return(false); } lastErrorVar.value = ""; return(true); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("DeleteDir", newValue.valType, newValue, true); } //@ static string GetCurrentDirectory() // Returns the full path of the current directory. // Returns "" and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string result; try { result = Directory.GetCurrentDirectory(); } catch (Exception e) { lastErrorVar.value = "GetCurrentDirectory: " + e.ToString(); return(""); } lastErrorVar.value = ""; return(result); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false); classDef.AddMemberLiteral("GetCurrentDirectory", newValue.valType, newValue, true); } //@ static List<string> GetDirs(string path) // Returns list of subdirectories of path. // Returns null and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; // Doesn't throw exceptions. if (!Directory.Exists(path)) { lastErrorVar.value = "GetDirs: Directory not found."; return(null); } string[] files; try { files = Directory.GetDirectories(path); } catch (Exception e) { lastErrorVar.value = "GetDirs: " + e.ToString(); return(null); } ClassValue inst = listStringClassDef.Allocate(context); inst.debugName = "(GetDirs result)"; PebbleList list = (PebbleList)inst; foreach (string file in files) { list.list.Add(new Variable(null, IntrinsicTypeDefs.STRING, file)); } lastErrorVar.value = ""; return(inst); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.LIST_STRING, new List <ITypeDef> { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("GetDirs", newValue.valType, newValue, true); } //@ static List<string> GetFiles(string path) // Returns list of all files in the given directory path. // Filenames are NOT prefaced by path. // Returns null and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; if (!Directory.Exists(path)) { lastErrorVar.value = "GetFiles: Directory not found."; return(null); } string[] files; try { files = Directory.GetFiles(path); } catch (Exception e) { lastErrorVar.value = "GetFiles: " + e.ToString(); return(null); } ClassValue inst = listStringClassDef.Allocate(context); inst.debugName = "(GetFiles result)"; PebbleList list = (PebbleList)inst; int pathLen = path.Length + 1; foreach (string file in files) { string justFile = file.Substring(pathLen); list.list.Add(new Variable(null, IntrinsicTypeDefs.STRING, justFile)); } lastErrorVar.value = ""; return(inst); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.LIST_STRING, new List <ITypeDef> { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("GetFiles", newValue.valType, newValue, true); } //@ static bool Exists(string path) // Returns true if file exists, false if it doesn't. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; // Doesn't throw exceptions. return(File.Exists(path)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Exists", newValue.valType, newValue, true); } //@ static bool Move(string source, string dest) // Move a file from source to dest. // Returns false and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string source = (string)args[0]; string dest = (string)args[1]; lastErrorVar.value = ""; try { File.Move(source, dest); } catch (Exception e) { lastErrorVar.value = "Move: " + e.ToString(); return(false); } return(true); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Move", newValue.valType, newValue, true); } //@ static Result<string> Read(string path) // Reads content of file as text. Returns an instance of Result<string>. // If succeeded, status = 0, value = file contents. // On error, status != 0, message = an error message. { // Note that this function cannot use lastError because strings cannot be null. // This is why ReadLines can just return a List<string>: the list can indicate error by being null. FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; //ClassValue inst = resultClassDef.childAllocator(); ClassValue inst = resultClassDef.Allocate(context); Variable status = inst.GetByName("status"); Variable value = inst.GetByName("value"); Variable message = inst.GetByName("message"); string result; try { result = File.ReadAllText(path); } catch (Exception e) { status.value = -1.0; message.value = "Read: " + e.ToString(); return(inst); } // The docs don't say this ever returns null, but just in case. if (null == result) { status.value = -1.0; message.value = "Read: File.ReadAllText returned null."; } value.value = result; return(inst); }; FunctionValue newValue = new FunctionValue_Host(resultClassDef.typeDef, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Read", newValue.valType, newValue, true); } //@ static List<string> ReadLines(string path) // Returns a list containing the lines of the given file. // On error, returns null and sets last error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; IEnumerable <string> result; try { // Note: ReadLines is a little more efficient on memory than ReadAllLines, but the .NET used by Unity 2017 doesn't support it. // Shouldn't really matter. If you're processing huge files, probably Pebble isn't the way to go. //result = File.ReadLines(path); result = File.ReadAllLines(path); } catch (Exception e) { lastErrorVar.value = "ReadLines: " + e.ToString(); return(null); } // The docs don't say this ever returns null, but just in case. if (null == result) { lastErrorVar.value = "ReadLines: File.ReadLines returned null."; return(null); } ClassDef listClassDef = context.GetClass("List<string>"); ClassValue listinst = listClassDef.childAllocator(); listinst.classDef = listClassDef; listinst.debugName = "(ReadLines result)"; PebbleList list = (PebbleList)listinst; foreach (string line in result) { list.list.Add(new Variable(null, IntrinsicTypeDefs.STRING, line)); } lastErrorVar.value = ""; return(listinst); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.LIST_STRING, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("ReadLines", newValue.valType, newValue, true); } //@ static bool SetCurrentDirectory(string newDir) // Changes the current directory. Returns true on success. // Returns false and sets lastError on error. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; try { Directory.SetCurrentDirectory(path); } catch (Exception e) { lastErrorVar.value = "SetCurrentDirectory: " + e.ToString(); return(false); } lastErrorVar.value = ""; return(true); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("SetCurrentDirectory", newValue.valType, newValue, true); } //@ static bool Write(string path, string contents) // Writes contents to file. If file exists it is overwritten. // Returns true on success. // On error returns false and sets lastError. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string path = (string)args[0]; string contents = (string)args[1]; try { File.WriteAllText(path, contents); } catch (Exception e) { lastErrorVar.value = "Write: " + e.ToString(); return(false); } lastErrorVar.value = ""; return(true); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.STRING, IntrinsicTypeDefs.STRING }, eval, false); classDef.AddMemberLiteral("Write", newValue.valType, newValue, true); } classDef.FinalizeClass(engine.defaultContext); lastErrorVar = classDef.staticVars[0]; UnitTests.testFuncDelegates.Add("FileLib", 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); }