// Type 2: a reference to a static member of a class. // * type is STATIC or FUNCTION (FUNCTION implying non-static function) // * ix is index into the given ClassDef's _statics or _memberFuncs public MemberRef(ClassDef staticOwnerIn, ClassDef.MemberType typeIn, int ixIn) { Pb.Assert(ClassDef.MemberType.STATIC == typeIn || ClassDef.MemberType.FUNCTION == typeIn); memberType = typeIn; ix = ixIn; classDef = staticOwnerIn; }
public void PopScope() { #if PEBBLE_DEBUG Pb.Assert(_callCount > 0, "PopScope called but no scopes to pop."); #endif int lastToRemove = _callStack[_callCount - 1].varStackStart; while (_varCount > lastToRemove) { Variable var = _varStack[_varCount - 1]; if (null != var) { if (var.unique) { _varStack[_varCount - 1] = null; } else { var.name = "<deleted>"; var.value = null; } } --_varCount; } --_callCount; #if PEBBLE_TRACESTACK TraceLog("PopScope"); #endif }
public void RegisterClass(ClassDef def) { // AddClass() Pb.Assert(!_classes.ContainsKey(def.name), "ExecContext::AddClass - Class already exists!"); _classes.Add(def.name, def); _types.Add(def.name, TypeFactory.GetTypeDef_Class(def.name, null, false)); }
protected void BuildArgHasDefaults(int argCount) { minArgs = argCount; if (null != argDefaultValues && 0 == argDefaultValues.Count) { argDefaultValues = null; } if (null == argDefaultValues) { return; } Pb.Assert(argDefaultValues.Count == argCount, "Default values array length doesn't match arg array length."); bool defaultFound = false; argHasDefaults = new List <bool>(); for (int ii = 0; ii < argCount; ++ii) { argHasDefaults.Add(null != argDefaultValues[ii]); if (!defaultFound && null != argDefaultValues[ii]) { defaultFound = true; minArgs = ii; } } }
public void PopToCallDepth(int length) { #if PEBBLE_DEBUG Pb.Assert(length > _callCount, "Why are you setting length to something longer than the current length?"); #endif _varStack.RemoveRange(length, _varStack.Count - length); }
// Call at the beginning of compilation to save current state. public void BeginCompile() { Pb.Assert(null == _preCompileState); _preCompileState = new PreCompileState(); _preCompileState.stackState = stack.GetState(); _types.Apply(); _classes.Apply(); }
public TypeRef(string nameIn, List <ITypeRef> templateTypes = null) { Pb.Assert(null != nameIn, "TypeRef constructor passed null name."); name = nameIn; if (null != templateTypes && templateTypes.Count > 0) { _templateTypes = templateTypes; } }
public Variable Set(string nameIn, ITypeDef typeIn, object valueIn = null) { Pb.Assert(null != typeIn, "Can't have a variable without a type."); name = nameIn; type = typeIn; value = valueIn; unique = false; return(this); }
public ITypeDef ResolveTemplateTypes(List <ITypeDef> genericTypesIn, ref bool modified) { if (null != _genericTypes && genericTypes.Count > 0) { modified = true; Pb.Assert(_genericTypes.Count == genericTypesIn.Count); return(TypeFactory.GetTypeDef_Class(className, genericTypesIn, _isConst)); } return(this); }
// This creates an invalid VarStackRef. public VarStackRef(ErrorType error) { Pb.Assert(error != ErrorType.None); errorType = error; callIndexOffset = -9000; varIndex = -1; isGlobal = false; memberRef = MemberRef.invalid; typeDef = null; variable = null; }
internal TypeDef_Function(ITypeDef _retType, List <ITypeDef> _argTypes, int _minArgs, bool _varargs, TypeDef_Class classTypeIn, bool _isConst, bool _isStaticFunction) { retType = _retType; argTypes = _argTypes; minArgs = _minArgs; varargs = _varargs; isConst = _isConst; classType = classTypeIn; isStaticMember = _isStaticFunction; Pb.Assert(!_varargs || minArgs == _argTypes.Count, "internal error: function cannot be both varArgs and have arguments with default values."); }
// Call this after creating the class and adding members to it. public bool FinalizeClass(ExecContext context) { Pb.Assert(0 == context.control.flags); // Only initializing the members that this class has added is a cool idea, but // leaves us f****d when it comes to overrridden functions. // So, I'm being lazy here and initializing them all. for (int ii = 0; ii < _memberFuncs.Count; ++ii) { ClassMember member = _memberFuncs.Get(ii); if (null != member.initializer && (member.initializer is Expr_Literal || member.initializer is Expr_Value)) { // Make sure vftableVars has a slot for this function. while (vftableVars.Count < ii + 1) { vftableVars.Add(null); } object initValue = member.initializer.Evaluate(context); if (context.IsRuntimeErrorSet()) { return(false); } // Create the variable for the function. vftableVars[ii] = new Variable(member.name, member.typeDef, initValue); } } // Initialize the static members. Populates the staticVars list. for (int ii = 0; ii < _statics.Count; ++ii) { ClassMember member = _statics.Get(ii); object initValue = null; if (null != member.initializer) { initValue = member.initializer.Evaluate(context); if (context.IsRuntimeErrorSet()) { context.engine.LogCompileError(ParseErrorType.StaticMemberEvaluationError, name + "::" + member.name + " - " + context.GetRuntimeErrorString()); return(false); } } staticVars[ii] = new Variable(member.name, member.typeDef, initValue); } return(true); }
public bool PushDefstructorScope(ClassValue instance, ExecContext context) { Pb.Assert(_callCount < CALLSTACKMAXDEPTH); if (CALLSTACKMAXDEPTH == _callCount) { return(false); } #if PEBBLE_TRACESTACK TraceLog("PushDefstructorScope " + instance.classDef.name + " '" + instance.debugName + "'"); #endif _callStack[_callCount].Set(_varCount, instance, false); ++_callCount; return(true); }
// Runtime access of static variable, having only the ClassDef. public Variable GetVariable(MemberRef memRef) { switch (memRef.memberType) { case MemberType.FUNCTION: return(vftableVars[memRef.ix]); case MemberType.STATIC: return(memRef.classDef.staticVars[memRef.ix]); default: Pb.Assert(false, "Attempt to get Variable for nonstatic field from ClassDef."); break; } return(null); }
public static TypeDef GetTypeDef(Type type, object defaultValue, bool isConst = false) { string name = ""; if (null == type) { name = "null"; } else { if (typeof(double) == type) { name = "num"; } else if (typeof(string) == type) { name = "string"; } else if (typeof(bool) == type) { name = "bool"; } else { Pb.Assert(false, "Unknown intrinsic type."); } if (isConst) { name = name + " const"; } } if (_typeRegistry.ContainsKey(name)) { return(_typeRegistry[name] as TypeDef); } TypeDef newDef = new TypeDef(type, defaultValue, isConst); #if PEBBLE_TRACETYPES Console.WriteLine("Registering new type: " + name); #endif _typeRegistry.Add(name, newDef); return(newDef); }
public static TypeDef_Enum GetTypeDef_Enum(string className, bool isConst) { string name; name = className; if (isConst) { name += " const"; } if (_typeRegistry.ContainsKey(name)) { Pb.Assert(_typeRegistry[name] is TypeDef_Enum, "Messed up enum in typeRegistry."); return(_typeRegistry[name] as TypeDef_Enum); } TypeDef_Enum newDef = new TypeDef_Enum(className); _typeRegistry.Add(name, newDef); return(newDef); }
public bool RestoreState(StackState state) { #if PEBBLE_DEBUG Pb.Assert(state.varCount <= _varCount || state.callCount <= _callCount, "TODO: StackState invalid."); #endif while (_varCount > state.varCount) { Variable var = _varStack[_varCount - 1]; if (null != var) { if (var.unique) { _varStack[_varCount - 1] = null; } else { var.value = null; var.name = "<deleted>"; } } --_varCount; } while (state.callCount < _callCount) { PopScope(); } #if PEBBLE_DEBUG Pb.Assert(state.callCount == _callCount, "whoops, call count different."); #endif #if PEBBLE_TRACESTACK TraceLog("RestoreState"); TraceLog(""); #endif return(true); }
// Register a template type, creating the ClassDef if necessary. public ClassDef RegisterIfUnregisteredTemplate(TypeDef_Class classType) { string fullName = classType.GetName(); if (_classes.ContainsKey(fullName)) { return(_classes[fullName]); } ClassDef parent = _classes[classType.className]; Pb.Assert(null != parent, "Parent template not registered?"); if (parent.isSealed) { engine.LogCompileError(ParseErrorType.ClassParentSealed, "Cannot derive from sealed class."); return(null); } // NOTE: All this code is assuming that the generic parent has no parents. // This is fine as long as 1) all generic classes are only defined in C#, and // 2) none of them have parents. if (parent.genericTypeNames.Count != classType.genericTypes.Count) { engine.LogCompileError(ParseErrorType.TemplateCountMismatch, "Template count mismatch: expected " + parent.genericTypeNames.Count + ", got " + classType.genericTypes.Count + "."); return(null); } ClassDef newClass = new ClassDef(classType.GetName(), classType, parent); _classes.Add(newClass.name, newClass); newClass.Initialize(); newClass.FinalizeClass(this); return(newClass); }
public static void Register(Engine engine) { //***************************** // Create ScriptError enum. scriptErrorEnum = new PebbleEnum(engine.defaultContext, "ScriptError", IntrinsicTypeDefs.CONST_STRING); // Add a value for "no error" since enums can't be null. scriptErrorEnum.AddValue_Literal(engine.defaultContext, "NoError", "NoError"); // Add both Parse and Runtime errors to the list. foreach (string name in Enum.GetNames(typeof(ParseErrorType))) { scriptErrorEnum.AddValue_Literal(engine.defaultContext, name, name); } foreach (string name in Enum.GetNames(typeof(RuntimeErrorType))) { scriptErrorEnum.AddValue_Literal(engine.defaultContext, name, name); } // Finalize. scriptErrorEnum.EvaluateValues(engine.defaultContext); // Save the value for NoError for convenience. scriptErrorEnum_noErrorValue = scriptErrorEnum.GetValue("NoError"); //******************************* //@ class Result<T> // This was added just in case users might have a need for a templated class that encapsulates a value and a status code. { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("Result", new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, false); ClassDef classDef = engine.defaultContext.CreateClass("Result", ourType, null, new List <string> { "T" }); classDef.Initialize(); //@ T value; // The resultant value IF there was no error. classDef.AddMember("value", IntrinsicTypeDefs.TEMPLATE_0); //@ num status; // A numeric status code. By convention, 0 means no error and anything else means error. classDef.AddMemberLiteral("status", IntrinsicTypeDefs.NUMBER, 0.0); //@ string message; // A place to store error messages if desired. classDef.AddMember("message", IntrinsicTypeDefs.STRING); //@ bool IsSuccess() // Returns true iff status == 0. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; return((double)scope.GetByName("status").value == 0.0); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("IsSuccess", newValue.valType, newValue); } //@ string ToString() // Returns a string representation of the Result. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; double status = (double)scope.GetByName("status").value; string result = scope.classDef.typeDef.ToString() + "["; if (0.0 == status) { result += CoreLib.ValueToString(context, scope.GetByName("value").value, true); } else { result += status + ": \"" + (string)scope.GetByName("message").value + "\""; } return(result + "]"); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("ThisToString", newValue.valType, newValue); } classDef.FinalizeClass(engine.defaultContext); resultClassDef = classDef; } //******************************* //@ class ScriptResult<T> // For returning the result of something that can error, like an Exec call. { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("ScriptResult", new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, false); ClassDef classDef = engine.defaultContext.CreateClass("ScriptResult", ourType, null, new List <string> { "T" }); classDef.Initialize(); //@ T value; // The return value if there was no error. classDef.AddMember("value", IntrinsicTypeDefs.TEMPLATE_0); //@ ScriptError error; // ScriptError.NoError if no error. classDef.AddMemberLiteral("error", CoreLib.scriptErrorEnum._classDef.typeDef, CoreLib.scriptErrorEnum_noErrorValue); //@ string message; // Optional error message. classDef.AddMember("message", IntrinsicTypeDefs.STRING); //@ bool IsSuccess() // Returns true iff error == ScriptError.NoError. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; return(scope.GetByName("error").value == CoreLib.scriptErrorEnum_noErrorValue); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("IsSuccess", newValue.valType, newValue); } //@ string ToString() // Returns a string representation of the ScriptError. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { ClassValue scope = thisScope as ClassValue; var error = (ClassValue_Enum)scope.GetByName("error").value; string result = scope.classDef.typeDef.ToString() + "["; if (CoreLib.scriptErrorEnum_noErrorValue == error) { result += CoreLib.ValueToString(context, scope.GetByName("value").value, true); } else { result += error.GetName() + ": \"" + (string)scope.GetByName("message").value + "\""; } return(result + "]"); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("ThisToString", newValue.valType, newValue); } classDef.FinalizeClass(engine.defaultContext); resultClassDef = classDef; } // This code makes sure that Result<bool> is a registered class and type. List <ITypeDef> genericTypes = new ArgList(); genericTypes.Add(IntrinsicTypeDefs.BOOL); scriptResultBoolTypeDef = TypeFactory.GetTypeDef_Class("ScriptResult", genericTypes, false); scriptResultBoolClassDef = engine.defaultContext.RegisterIfUnregisteredTemplate(scriptResultBoolTypeDef); Pb.Assert(null != scriptResultBoolTypeDef && null != scriptResultBoolClassDef, "Error initializing ScriptResult<bool>."); //////////////////////////////////////////////////////////////////////////// // Register non-optional libraries. //CoreResult.Register(engine); // List and Dictionary probably need to be first because other libraries sometimes use them. CoreList.Register(engine); CoreDictionary.Register(engine); MathLib.Register(engine); RegexLib.Register(engine); StringLib.Register(engine); StreamLib.Register(engine); //@ global const num FORMAX; // The highest value a for iterator can be. Attempting to exceed it generates an error. engine.defaultContext.CreateGlobal("FORMAX", IntrinsicTypeDefs.CONST_NUMBER, Expr_For.MAX); //////////////////////////////////////////////////////////////////////////// // Library functions //@ global ScriptResult<bool> Exec(string script) // Executes the supplied script. // Since this is not running "interactive" (or inline), the only way the script can // have an external effect is if it affects global things (variables, class definitions). // The returned ScriptResult's value is only true(success) or false (error). { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string script = (string)args[0]; ClassValue scriptResultInst = scriptResultBoolClassDef.Allocate(context); Variable value = scriptResultInst.GetByName("value"); Variable error = scriptResultInst.GetByName("error"); Variable message = scriptResultInst.GetByName("message"); ScriptResult result = context.engine.RunScript(script, false, null, true); if (null != result.parseErrors) { value.value = false; error.value = scriptErrorEnum.GetValue(result.parseErrors[0].type.ToString());; message.value = result.parseErrors[0].ToString(); } else if (null != result.runtimeError) { value.value = false; error.value = result.value; message.value = result.runtimeError.ToString(); } else { value.value = true; error.value = scriptErrorEnum_noErrorValue; message.value = ""; } return(scriptResultInst); }; FunctionValue newValue = new FunctionValue_Host(scriptResultBoolTypeDef, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); engine.AddBuiltInFunction(newValue, "Exec"); } //@ global ScriptResult<bool> ExecInline(string) // This executes the given script in the current scope. This is different from Exec, because Exec exists in its own scope. // The returned ScriptResult's value is only true(success) or false (error). { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string script = (string)args[0]; ClassValue scriptResultInst = scriptResultBoolClassDef.Allocate(context); Variable value = scriptResultInst.GetByName("value"); Variable error = scriptResultInst.GetByName("error"); Variable message = scriptResultInst.GetByName("message"); ScriptResult result = context.engine.RunInteractiveScript(script, false); if (null != result.parseErrors) { value.value = false; error.value = scriptErrorEnum.GetValue(result.parseErrors[0].type.ToString());; message.value = result.parseErrors[0].ToString(); } else if (null != result.runtimeError) { value.value = false; error.value = result.value; message.value = result.runtimeError.ToString(); } else { value.value = true; error.value = scriptErrorEnum_noErrorValue; message.value = ""; } return(scriptResultInst); }; FunctionValue newValue = new FunctionValue_Host(scriptResultBoolTypeDef, new ArgList { IntrinsicTypeDefs.STRING }, eval, false); engine.AddBuiltInFunction(newValue, "ExecInline"); } //@ global string Print(...) // Converts all arguments to strings, concatenates them, then outputs the result using the Engine' Log function. // This function can be set to whatever the host program likes: see Engine.Log { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string result = StandardPrintFunction(context, args); context.engine.Log(result); return(result); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new List <ITypeDef> { IntrinsicTypeDefs.ANY }, eval, true); engine.AddBuiltInFunction(newValue, "Print"); } //@ global string ToScript(any) // Returns a script which, when run, returns a value equal to the value passed into ToScript. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object val0 = args[0]; return(ValueToScript(context, val0)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.ANY }, eval, false); engine.AddBuiltInFunction(newValue, "ToScript"); } ///////////////////////////////////////////////////////////////// // Type Conversion //@ global bool ToBool(any) // Attempts to convert input into a boolean value. // 0 and null are false. != 0 and non-null references are true. Strings are handled by Convert.ToBoolean, // which can throw an exception if it doesn't know how to convert the string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object val = args[0]; if (null == val) { return(false); } else if (val is bool) { return((bool)val); } else if (val is double) { return(Convert.ToBoolean((double)val)); } else if (val is string) { try { return(Convert.ToBoolean((string)val)); // this loves to throw errors } catch (Exception e) { context.SetRuntimeError(RuntimeErrorType.ConversionInvalid, "ToBool - C# error: " + e.ToString()); return(null); } } else { return(true); } }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.ANY }, eval, false); engine.AddBuiltInFunction(newValue, "ToBool"); } //@ global num ToNum(any) // Attempts to convert input to a num. // true -> 1, false -> 0, null -> 0, non-null object reference -> 1. Strings are handled by Convert.ToDouble, // which can throw an error if it doesn't know how to convert the string. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object val = args[0]; if (null == val) { return(0.0); } else if (val is double) { return((double)val); } else if (val is bool) { return((bool)val ? 1.0 : 0.0); } else if (val is string) { try { return(Convert.ToDouble((string)val)); // this loves to throw errors } catch { context.SetRuntimeError(RuntimeErrorType.ConversionInvalid, "ToNum - Cannot convert string \"" + ((string)val) + "\" to number."); return(null); } } else { return(1.0); } }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.ANY }, eval, false); engine.AddBuiltInFunction(newValue, "ToNum"); } //@ global string ToString(...) // Converts all arguments to strings, concatenates them, and returns the result. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { return(StandardPrintFunction(context, args)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.ANY }, eval, true); engine.AddBuiltInFunction(newValue, "ToString"); } UnitTests.testFuncDelegates.Add("CoreLib", RunTests); }
/* * Registers a script function. * If any names are already used, returns false. */ public bool AddBuiltInFunction(FunctionValue def, string name) { Pb.Assert(name != null); Pb.Assert(def.valType.isConst); return(CreateGlobalVariable(name, def.valType, def)); }
public static void Register(Engine engine) { TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("Debug", null, false); ClassDef classDef = engine.defaultContext.CreateClass("Debug", ourType, null, null, true); classDef.Initialize(); // string DumpClass(string className = "") // Returns a string dump of the fields of a class with the given name. // If no argument is given (or string is empty), instead returns a list of all classes. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string className = (string)args[0]; if ("" == className) { return(context.GetDebugTypeString(true, false, false)); } else { ClassDef def = context.GetClass(className); if (null == def) { return("Class '" + className + "' not found."); } return(def.GetDebugString()); } }; // Note: Here is an example of how you provide default argument values for a host function. List <Expr_Literal> defaultArgVals = new List <Expr_Literal>(); defaultArgVals.Add(new Expr_Literal(null, "", IntrinsicTypeDefs.STRING)); FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING }, eval, false, null, false, defaultArgVals); classDef.AddMemberLiteral("DumpClass", newValue.valType, newValue, true); } // string DumpStack() // Returns a string printout of the stack at the point the function is called. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { return(context.ToString()); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false); classDef.AddMemberLiteral("DumpStack", newValue.valType, newValue, true); } // string DumpTypes() // Returns a string printout of the registered types. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { return(context.GetDebugTypeString()); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false); classDef.AddMemberLiteral("DumpTypes", newValue.valType, newValue, true); } // string GetTotalMemory() // Wraps GC.GetTotalMemory, which returns the number of bytes estimated to be allocated by C#. // Note that this is NOT just memory allocated by Pebble. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { bool forceFullCorrection = (bool)args[0]; return(Convert.ToDouble(GC.GetTotalMemory(forceFullCorrection))); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { IntrinsicTypeDefs.BOOL }, eval, false); classDef.AddMemberLiteral("GetTotalMemory", newValue.valType, newValue, true); } // functype<bool(bool, string)> SetAssertCallback(functype<bool(bool, string)>) // Sets callback for assert results, returns previous callback. // Callback gets success as first variable, message as second. If returns false, system throws an Assert runtime exception. { TypeRef_Function handlerTypeRef = new TypeRef_Function(new TypeRef("bool"), new List <ITypeRef>() { new TypeRef("bool"), new TypeRef("string") }); bool error = false; TypeDef_Function handlerTypeDef = (TypeDef_Function)handlerTypeRef.Resolve(engine.defaultContext, ref error); Pb.Assert(!error, "Internal error: SetAssertHandler initialization."); FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { FunctionValue newHandler = (FunctionValue)args[0]; FunctionValue oldHandler = Expr_Assert.handler; Expr_Assert.handler = newHandler; return(oldHandler); }; FunctionValue newValue = new FunctionValue_Host(handlerTypeDef, new ArgList { handlerTypeDef }, eval, false); classDef.AddMemberLiteral("SetAssertCallback", newValue.valType, newValue, true); } //@ bool SetLogCompileErrors(bool log) // Sets whether or not compile errors should be logged to the Engine's log function. // Returns the previous value. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { bool oldValue = context.engine.logCompileErrors; bool newVal = (bool)args[0]; context.engine.logCompileErrors = newVal; return(oldValue); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.BOOL }, eval, false); classDef.AddMemberLiteral("SetLogCompileErrors", newValue.valType, newValue, true); } // void TimerStart() // Starts a debug timer. If one is already running it will be set to this new start time. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { timer = Stopwatch.StartNew(); return(null); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.VOID, new ArgList { }, eval, false); classDef.AddMemberLiteral("TimerStart", newValue.valType, newValue, true); } // num TimerGet() // Returns elapsed ms since TimerStart called, or -1 if TimerStart wasn't previously called. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { if (null == timer) { return(-1.0); } return(Convert.ToDouble(timer.ElapsedMilliseconds)); }; FunctionValue newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { }, eval, false); classDef.AddMemberLiteral("TimerGet", newValue.valType, newValue, true); } classDef.FinalizeClass(engine.defaultContext); }
public virtual bool Comparable(ExecContext context, ITypeDef other) { Pb.Assert(false, "Can't call Comparable on Template."); return(false); }
// Returns true if a variable of this type can store a value of valueType. public virtual bool CanStoreValue(ExecContext context, ITypeDef valueType) { Pb.Assert(false, "Can't CanStoreValue on Template."); return(false); }
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 FunctionValue_Script(string name, IExpr _expr, ITypeRef _retType, List <ITypeRef> _argTypes, List <Expr_Literal> defaultVals = null) { argDefaultValues = defaultVals; BuildArgHasDefaults(_argTypes.Count); originalName = name; expr = _expr; typeRef = new TypeRef_Function(_retType, _argTypes, argHasDefaults, false); Evaluate = (context, args, thisScope) => { Pb.Assert(null != originalName); Pb.Assert(args.Count == _argTypes.Count, "Internal error: Don't have enough arguments!"); if (context.IsRuntimeErrorSet()) { return(null); } object result; bool pushResult = false; if (null != staticClassDef) { pushResult = context.stack.PushClassCall_StaticOrTypeCheck(typeDef, staticClassDef, true, context); } else { pushResult = context.stack.PushCall(typeDef, originalName, thisScope, false, context); } if (!pushResult) { context.SetRuntimeError(RuntimeErrorType.StackOverflow, "FunctionValue_Script.Evaluate : stack overflow"); return(null); } { // Add name of this function as first variable in function scope. // This condition loosely mirrors the check in Expr_Literal. It's shitty that the code that // runs during TypeCheck is so far removed from this code that runs during Execute. if (null == typeDef.classType) { context.stack.AddVariable(originalName, false, typeDef, this); } // Add argument variables to function scope. if (null != args) { for (int ii = 0; ii < args.Count; ++ii) { context.AddLocalVariable(argNames[ii], valType.argTypes[ii], args[ii]); } } result = _EvaluateInternal(context); } context.stack.PopScope(); if (context.IsRuntimeErrorSet()) { return(null); } if (0 != (context.control.flags & ControlInfo.RETURN)) { result = context.control.result; context.control.result = null; context.control.flags -= ControlInfo.RETURN; } Pb.Assert(0 == context.control.flags); return(result); }; }
public static void Register(Engine engine) { //@ class Dictionary<K, V> TypeDef_Class ourType = TypeFactory.GetTypeDef_Class("Dictionary", new ArgList { IntrinsicTypeDefs.TEMPLATE_0, IntrinsicTypeDefs.TEMPLATE_1 }, false); ClassDef classDef = engine.defaultContext.CreateClass("Dictionary", ourType, null, new List <string> { "K", "V" }); classDef.childAllocator = () => { return(new PebbleDictionary()); }; classDef.Initialize(); //@ Dictionary<K, V> Add(K key, V value) // Adds a new element to the dictionary. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object key = args[0]; object value = args[1]; PebbleDictionary scope = thisScope as PebbleDictionary; if (scope.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Add: Attempt to modify a dictionary that is being enumerated by a foreach loop."); return(null); } var dictionary = scope.dictionary; var dictionaryType = (TypeDef_Class)scope.classDef.typeDef; var valueType = dictionaryType.genericTypes[1]; if (dictionary.ContainsKey(key)) { context.SetRuntimeError(RuntimeErrorType.KeyAlreadyExists, "Dictionary already contains key '" + key + "'."); return(null); } dictionary.Add(key, new Variable(null, valueType, value)); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.TEMPLATE_0, IntrinsicTypeDefs.TEMPLATE_1 }, eval, false, ourType); classDef.AddMemberLiteral("Add", newValue.valType, newValue); } //@ Dictionary<K, V> Clear() // Removes all elements from the dictionary. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { PebbleDictionary pebDict = thisScope as PebbleDictionary; var dictionary = pebDict.dictionary; if (pebDict.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Clear: Attempt to modify a dictionary that is being enumerated by a foreach loop."); return(null); } dictionary.Clear(); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("Clear", newValue.valType, newValue); } //@ bool ContainsKey(K) // Returns true iff the dictionary contains an element with the given key. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object key = args[0]; PebbleDictionary pebDict = thisScope as PebbleDictionary; var dictionary = pebDict.dictionary; return(dictionary.ContainsKey(key)); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.BOOL, new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, eval, false, ourType); classDef.AddMemberLiteral("ContainsKey", newValue.valType, newValue); } //@ num Count() // Returns number of elements in the dictionary. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { var dictionary = (thisScope as PebbleDictionary).dictionary; return(System.Convert.ToDouble(dictionary.Count)); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.NUMBER, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("Count", newValue.valType, newValue); } //@ V Get(K) // Returns the value of the element with the given key. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object key = args[0]; PebbleDictionary pebDict = thisScope as PebbleDictionary; // Bounds checking. var dictionary = pebDict.dictionary; if (!dictionary.ContainsKey(key)) { context.SetRuntimeError(RuntimeErrorType.KeyNotFound, "Get: Key '" + key + "' not in dictionary."); return(null); } return(dictionary[key].value); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.TEMPLATE_1, new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, eval, false, ourType); classDef.AddMemberLiteral("Get", newValue.valType, newValue); } //@ Dictionary<K, V> Remove(K key) // Removes element with given key. // Cannot be used in a foreach loop. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object key = args[0]; PebbleDictionary pebDict = thisScope as PebbleDictionary; var dictionary = pebDict.dictionary; if (pebDict.enumeratingCount > 0) { context.SetRuntimeError(RuntimeErrorType.ForeachModifyingContainer, "Remove: Attempt to modify a dictionary that is being enumerated by a foreach loop."); return(null); } if (!dictionary.ContainsKey(key)) { context.SetRuntimeError(RuntimeErrorType.KeyNotFound, "Remove: Key '" + key + "' not in dictionary."); return(null); } dictionary.Remove(key); return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.TEMPLATE_0 }, eval, false, ourType); classDef.AddMemberLiteral("Remove", newValue.valType, newValue); } //@ Dictionary<K, V> Set(K key, V newValue) // Replaces value of existing element with the given key. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { object key = args[0]; object value = args[1]; var dictionary = (thisScope as PebbleDictionary).dictionary; // Bounds checking. if (!dictionary.ContainsKey(key)) { context.SetRuntimeError(RuntimeErrorType.KeyNotFound, "Get: Key '" + key + "' not in dictionary."); return(null); } dictionary[key].value = value; return(thisScope); }; FunctionValue_Host newValue = new FunctionValue_Host(ourType, new ArgList { IntrinsicTypeDefs.TEMPLATE_0, IntrinsicTypeDefs.TEMPLATE_1 }, eval, false, ourType); classDef.AddMemberLiteral("Set", newValue.valType, newValue); } //@ string ThisToScript(string prefix) // ThisToScript is used by Serialize. A classes' ThisToScript function should return code which can rebuild the class. // Note that it's only the content of the class, not the "new A" part. ie., it's the code that goes in the defstructor. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { string result = ""; string prefix = (string)args[0] + "\t"; var dictionary = (thisScope as PebbleDictionary).dictionary; //bool first = true; foreach (KeyValuePair <object, Variable> kvp in dictionary) { result += prefix + "Add(" + CoreLib.ValueToScript(context, kvp.Key, prefix + "\t", false) + ", " + CoreLib.ValueToScript(context, kvp.Value.value, prefix + "\t", false) + ");\n"; } return(result); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { IntrinsicTypeDefs.STRING }, eval, false, ourType); classDef.AddMemberLiteral("ThisToScript", newValue.valType, newValue); } //@ string ToString() // Returns a human readable version of at least the first few elements of the dictionary. { FunctionValue_Host.EvaluateDelegate eval = (context, args, thisScope) => { var dictionary = (thisScope as PebbleDictionary).dictionary; string result = "Dictionary(" + dictionary.Count + ")["; int count = 0; foreach (KeyValuePair <object, Variable> kvp in dictionary) { if (count != 0) { result += ", "; } result += "(" + CoreLib.ValueToString(context, kvp.Key, true) + ", " + CoreLib.ValueToString(context, kvp.Value.value, true) + ")"; if (++count >= 4) { break; } } if (dictionary.Count > 4) { result += ", ..."; } return(result + "]"); }; FunctionValue_Host newValue = new FunctionValue_Host(IntrinsicTypeDefs.STRING, new ArgList { }, eval, false, ourType); classDef.AddMemberLiteral("ThisToString", newValue.valType, newValue); } bool error = false; classDef.FinalizeClass(engine.defaultContext); Pb.Assert(!error); UnitTests.testFuncDelegates.Add("CoreDictionary", RunTests); }
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); }
public ClassValue_Enum GetDefaultValue() { Pb.Assert(_classDef.staticVars.Count > 0); return(_classDef.staticVars[0].value as ClassValue_Enum); }
public virtual bool IsNull() { Pb.Assert(false); return(false); }
// Convenience function for adding members when you know their literal // value at compile time. public bool AddMemberLiteral(string name, ITypeDef typeDef, object value, bool isStatic = false) { Pb.Assert(null != typeDef); return(AddMember(name, typeDef, new Expr_Value(null, value, typeDef), isStatic)); }