//Execute block with stream and error catching data
        public object Execute(MlfBlock block, SnekScope scope, out string stream, out Exception error, params object[] arguments)
        {
            stream = null;
            error  = null;

            if (scope == null)
            {
                scope = defaultScope;
            }

            SnekScriptSource source = (SnekScriptSource)block.GetFormatData("scriptSource");

            if (source == null)
            {
                return(null);
            }

            scope.scriptScope.SetVariable("block", block);

            AddContextsToScope(block.contexts, scope, block, arguments);

            object output = Execute(source.ScriptSource, scope, out stream, out error);

            RemoveContextsFromScope(block.contexts, scope, block);

            scope.scriptScope.RemoveVariable("block");

            return(output);
        }
        public static string GetStackTrace(ScriptSource source, MlfBlock block, Exception e = null)
        {
            string stackTrace = "";

            if (block == null)
            {
                return(null);
            }

            //Print error
            if (e != null)
            {
                foreach (var frame in PythonOps.GetDynamicStackFrames(e))
                {
                    stackTrace += "\nCode: " + source.GetCodeLine(frame.GetFileLineNumber()) + "";
                    stackTrace += "\nLine: " + (frame.GetFileLineNumber() + block.line);
                }
            }

            //Block's MLF path
            if (block.path != null)
            {
                stackTrace += "\nPath: " + block.path;
            }

            return(stackTrace);
        }
        //Execute source snippet with stream and error catching handling
        public object ExecuteSnippet(ScriptSource source, MlfBlock owningBlock, SnekScope scope, out string stream, out Exception error, params object[] arguments)
        {
            stream = null;
            error  = null;

            if (scope == null)
            {
                scope = defaultScope;
            }

            if (owningBlock != null)
            {
                ApplyPropertyList(scope, owningBlock.MlfProperties);
                ApplyArgumentList(owningBlock, scope, arguments);
                AddContextsToScope(owningBlock.contexts, scope, owningBlock, arguments);
            }


            object output = Execute(source, scope, out stream, out error);

            if (owningBlock != null)
            {
                RemoveContextsFromScope(owningBlock.contexts, scope, owningBlock);
                RemoveArgumentList(owningBlock, scope, arguments);
                RemovePropertyList(scope, owningBlock.MlfProperties);
            }

            return(output);
        }
        //Execute a source snippet
        public object ExecuteSnippet(ScriptSource source, MlfBlock owningBlock, SnekScope scope, params object[] arguments)
        {
            string    stream = null;
            Exception error  = null;

            if (scope == null)
            {
                scope = defaultScope;
            }

            object output = ExecuteSnippet(source, owningBlock, scope, out stream, out error, arguments);

            if (stream != null)
            {
                string stackTrace = GetStackTrace(source, owningBlock);
                Log(stream, StackTraceLogType.None, stackTrace);
            }

            if (error != null)
            {
                string stackTrace = GetStackTrace(source, owningBlock, error);
                LogError(error.Message, StackTraceLogType.None, stackTrace);
            }

            return(output);
        }
        //Special snippet which automatically wraps the expression in a function and handles returning
        public T ExecuteReturningSnippet <T>(string expression, MlfBlock owningBlock, SnekScope scope, params object[] arguments)
        {
            if (scope == null)
            {
                scope = defaultScope;
            }

            if (!expression.Contains("return"))
            {
                expression = "return(" + expression + ")";
            }

            //Replace newlines with tabbed newline
            expression = expression.Replace("\n", "\n\t");
            expression = expression.Replace("\r", "\r\t");

            //Wrap in function
            expression = "def _smartsnippet():\n\t" + expression + "\n_smartsnippetreturn = _smartsnippet()";

            scope.scriptScope.SetVariable("_smartsnippetreturn", default(T));

            ExecuteSnippet(expression, owningBlock, scope, arguments);

            T returnValue = scope.scriptScope.GetVariable <T>("_smartsnippetreturn");

            scope.scriptScope.RemoveVariable("_smartsnippetreturn");

            return(returnValue);
        }
        //Executes a python formatted block
        public static object ExecuteMlfBlock(MlfObject mlfObject, SnekScope scope, string id = null, string tag = null, params object[] arguments)
        {
            mlfObject.MlfInstance.InterpretIfDirty();

            MlfBlock block = mlfObject.MlfInstance.GetBlock(id, tag, "python");

            if (block != null)
            {
                return(Instance.Execute(block, scope, mlfObject.MlfProperties, arguments));
            }
            return(null);
        }
        //Execute a snippet owned by a block
        public object ExecuteSnippet(string expression, MlfBlock owningBlock, SnekScope scope, params object[] arguments)
        {
            if (scope == null)
            {
                scope = defaultScope;
            }

            if (owningBlock != null)
            {
                return(ExecuteSnippet(engine.CreateScriptSourceFromString(expression, owningBlock.path), owningBlock, scope, arguments));
            }
            else
            {
                return(ExecuteSnippet(engine.CreateScriptSourceFromString(expression), owningBlock, scope, arguments));
            }
        }
        private void RemoveArgumentList(MlfBlock block, SnekScope scope, object[] arguments)
        {
            if (arguments == null)
            {
                return;
            }

            for (int i = 0; i < arguments.Length; i++)
            {
                if (block.arguments.Count <= i)
                {
                    continue;
                }

                scope.scriptScope.RemoveVariable(block.arguments[i]);
            }
        }
        //Execute a block
        public object Execute(MlfBlock block, SnekScope scope, Dictionary <string, MlfProperty> properties = null, params object[] arguments)
        {
            string    stream = null;
            Exception error  = null;

            if (scope == null)
            {
                scope = defaultScope;
            }

            //Find source
            SnekScriptSource source = (SnekScriptSource)block.GetFormatData("scriptSource");

            if (source == null)
            {
                return(null);
            }

            ApplyPropertyList(scope, properties);
            ApplyArgumentList(block, scope, arguments);

            object output = Execute(block, scope, out stream, out error, arguments);

            RemoveArgumentList(block, scope, arguments);
            RemovePropertyList(scope, properties);

            if (stream != null)
            {
                string stackTrace = GetStackTrace(source.ScriptSource, block);
                Log(stream, StackTraceLogType.None, stackTrace);
            }

            if (error != null)
            {
                string stackTrace = GetStackTrace(source.ScriptSource, block, error);
                LogError(error.Message, StackTraceLogType.None, stackTrace);
            }

            return(output);
        }
        public void RemoveContextsFromScope(List <MlfContextReference> contexts, SnekScope scope = null, MlfBlock block = null)
        {
            if (contexts == null)
            {
                return;
            }

            if (scope == null)
            {
                scope = defaultScope;
            }

            foreach (MlfContextReference context in contexts)
            {
                ScriptScope contextScope = GetContext(context.id).scriptScope;

                //Remove local arguments
                if (block != null)
                {
                    for (int i = 0; i < block.arguments.Count; i++)
                    {
                        contextScope.RemoveVariable(block.arguments[i]);
                    }
                }

                //Wildcard mode (Not recommended)
                if (context.key == "*")
                {
                    //Currently wildcard mode cannot remove variables, since it would include crazy variables such as "False"
                    //Possible (yucky) solution: when a context is defined, add the variable to a dictionary
                }
                else
                {
                    scope.scriptScope.RemoveVariable(context.key);
                }
            }
        }
        public void AddContextsToScope(List <MlfContextReference> contexts, SnekScope scope = null, MlfBlock block = null, params object[] arguments)
        {
            if (contexts == null)
            {
                return;
            }

            if (scope == null)
            {
                scope = defaultScope;
            }

            foreach (MlfContextReference context in contexts)
            {
                ScriptScope contextScope = GetContext(context.id).scriptScope;

                //Add local arguments
                if (block != null)
                {
                    for (int i = 0; i < block.arguments.Count; i++)
                    {
                        if (arguments.Length <= i)
                        {
                            break;
                        }

                        contextScope.SetVariable(block.arguments[i], arguments[i]);
                    }
                }
                //Wildcard mode (Not recommended)
                if (context.key == "*")
                {
                    //Detect adding wildcard to default scope, which is currently not allowed
                    if (scope == defaultScope)
                    {
                        Debug.LogError("Warning: Adding Wildcard Contexts to default scope is not allowed, since currently wildcard contexts cannot be removed.");
                        continue;
                    }

                    foreach (string var in contextScope.GetVariableNames())
                    {
                        object value = contextScope.GetVariable(var);

                        if (value != null)
                        {
                            scope.scriptScope.SetVariable(var, value);
                        }
                    }
                }
                else
                {
                    scope.scriptScope.SetVariable(context.key, contextScope);
                }
            }
        }