Эх сурвалжийг харах

coroutines - not quite working yet

Xanathar 11 жил өмнө
parent
commit
502bcb3781

+ 54 - 0
src/MoonSharp.Interpreter.Tests/EndToEnd/CoroutineTests.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace MoonSharp.Interpreter.Tests.EndToEnd
+{
+	[TestFixture]
+	class CoroutineTests
+	{
+		[Test]
+		public void Coroutine_Basic()
+		{
+			string script = @"
+				s = ''
+
+				function foo()
+					for i = 1, 4 do
+						s = s .. i;
+						coroutine.yield();
+					end
+				end
+
+				function bar()
+					for i = 5, 9 do
+						s = s .. i;
+						coroutine.yield();
+					end
+				end
+
+				cf = coroutine.create(foo);
+				cb = coroutine.create(bar);
+
+				for i = 1, 4 do
+					coroutine.resume(cf);
+					s = s .. '-';
+					coroutine.resume(cb);
+					s = s .. ';';
+				end
+
+				return s;
+				";
+
+			DynValue res = Script.RunString(script);
+
+			Assert.AreEqual(DataType.String, res.Type);
+			Assert.AreEqual("1-5;2-6;3-7;4-8;", res.String);
+
+		}
+
+
+	}
+}

+ 1 - 0
src/MoonSharp.Interpreter.Tests/MoonSharp.Interpreter.Tests.csproj

@@ -73,6 +73,7 @@
   </Choose>
   <ItemGroup>
     <Compile Include="EndToEnd\ClosureTests.cs" />
+    <Compile Include="EndToEnd\CoroutineTests.cs" />
     <Compile Include="EndToEnd\LuaTestSuiteExtract.cs" />
     <Compile Include="EndToEnd\MetatableTests.cs">
       <SubType>Code</SubType>

+ 110 - 0
src/MoonSharp.Interpreter/CoreLib/CoroutineMethods.cs

@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter.Execution;
+using MoonSharp.Interpreter.Execution.VM;
+
+namespace MoonSharp.Interpreter.CoreLib
+{
+	[MoonSharpModule(Namespace = "coroutine")]
+	public class CoroutineMethods
+	{
+		[MoonSharpMethod]
+		public static DynValue create(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue v = args.AsType(0, "coroutine.create", DataType.Function);
+			return executionContext.CoroutineCreate(v.Function);
+		}
+
+		[MoonSharpMethod]
+		public static DynValue wrap(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue v = args.AsType(0, "coroutine.wrap", DataType.Function);
+			DynValue c = DynValue.NewCallback(__wrap_wrapper);
+			c.Callback.Closure.Set("_HANDLE", v);
+			return c;
+		}
+
+
+		public static DynValue __wrap_wrapper(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue handle = executionContext.Closure.Get("_HANDLE");
+			return executionContext.CoroutineResume(handle, args.List.ToArray());
+		}
+
+
+		[MoonSharpMethod]
+		public static DynValue resume(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue handle = args.AsType(0, "coroutine.resume", DataType.Thread);
+
+			try
+			{
+				DynValue ret = executionContext.CoroutineResume(handle, args.List.Skip(1).ToArray());
+
+				List<DynValue> retval = new List<DynValue>();
+				retval.Add(DynValue.True);
+
+				if (ret.Type == DataType.Tuple)
+					DynValue.ExpandArgumentsToList(ret.Tuple, retval);
+				else
+					retval.Add(ret);
+
+				return DynValue.NewTuple(retval.ToArray());
+			}
+			catch (ScriptRuntimeException ex)
+			{
+				return DynValue.NewTuple(
+					DynValue.False,
+					DynValue.NewString(ex.Message));
+			}
+		}
+
+		[MoonSharpMethod]
+		public static DynValue yield(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			return DynValue.NewYieldReq(args.List.ToArray());
+		}
+
+
+
+		[MoonSharpMethod]
+		public static DynValue running(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue handle = executionContext.CoroutineRunning();
+			CoroutineState cs = executionContext.CoroutineGetState(handle);
+
+			return DynValue.NewTuple(
+				handle,
+				DynValue.NewBoolean(cs == CoroutineState.Main));
+		}
+
+		[MoonSharpMethod]
+		public static DynValue status(ScriptExecutionContext executionContext, CallbackArguments args)
+		{
+			DynValue handle = args.AsType(0, "coroutine.status", DataType.Thread);
+			CoroutineState cs = executionContext.CoroutineGetState(handle);
+
+			switch (cs)
+			{
+				case CoroutineState.Main:
+				case CoroutineState.Running:
+					return (executionContext.CoroutineIsRunning(handle)) ?
+						DynValue.NewString("running") :
+						DynValue.NewString("normal");
+				case CoroutineState.NotStarted:
+				case CoroutineState.Suspended:
+					return DynValue.NewString("suspended");
+				case CoroutineState.Dead:
+					return DynValue.NewString("dead");
+				default:
+					throw new InternalErrorException("Unexpected coroutine state {0}", cs);
+			}
+	
+		}
+
+
+	}
+}

+ 6 - 4
src/MoonSharp.Interpreter/DataTypes/DataType.cs

@@ -57,11 +57,10 @@ namespace MoonSharp.Interpreter
 		/// A request to execute a tail call
 		/// </summary>
 		TailCallRequest,
-
 		/// <summary>
-		/// Indicates the maximum index of types supporting metatables
+		/// A request to coroutine.yield
 		/// </summary>
-		MaxMetaTypes = Table
+		YieldRequest,
 	}
 
 	/// <summary>
@@ -69,6 +68,8 @@ namespace MoonSharp.Interpreter
 	/// </summary>
 	public static class LuaTypeExtensions
 	{
+		public const DataType MaxMetaTypes = DataType.Table;
+
 		/// <summary>
 		/// Determines whether this data type can have type metatables.
 		/// </summary>
@@ -76,7 +77,7 @@ namespace MoonSharp.Interpreter
 		/// <returns></returns>
 		public static bool CanHaveTypeMetatables(this DataType type)
 		{
-			return (int)type < (int)DataType.MaxMetaTypes;
+			return (int)type < (int)MaxMetaTypes;
 		}
 
 
@@ -110,6 +111,7 @@ namespace MoonSharp.Interpreter
 					return "thread";
 				case DataType.Tuple:
 				case DataType.TailCallRequest:
+				case DataType.YieldRequest:
 				default:
 					throw new ScriptRuntimeException("Unexpected LuaType {0}", type);
 			}

+ 76 - 1
src/MoonSharp.Interpreter/DataTypes/DynValue.cs

@@ -49,6 +49,10 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		public DynValue[] Tuple { get { return m_Object as DynValue[]; } }
 		/// <summary>
+		/// Gets the coroutine handle. (valid only if the <seealso cref="Type"/> is Thread).
+		/// </summary>
+		public int CoroutineHandle { get { return (int)m_Object; } }
+		/// <summary>
 		/// Gets the table (valid only if the <seealso cref="Type"/> is <seealso cref="DataType.Table"/>)
 		/// </summary>
 		public Table Table { get { return m_Object as Table; } }
@@ -69,6 +73,10 @@ namespace MoonSharp.Interpreter
 		/// </summary>
 		public TailCallData TailCallData { get { return m_Object as TailCallData; } }
 		/// <summary>
+		/// Gets the yield request data.
+		/// </summary>
+		public YieldRequest YieldRequest { get { return m_Object as YieldRequest; } }
+		/// <summary>
 		/// Gets the tail call data.
 		/// </summary>
 		public UserData UserData { get { return m_Object as UserData; } }
@@ -76,7 +84,7 @@ namespace MoonSharp.Interpreter
 		/// <summary>
 		/// Returns true if this instance is write protected.
 		/// </summary>
-		public bool ReadOnly { get { return m_ReadOnly; }}
+		public bool ReadOnly { get { return m_ReadOnly; } }
 
 
 
@@ -126,6 +134,21 @@ namespace MoonSharp.Interpreter
 			};
 		}
 
+		/// <summary>
+		/// Creates a new writable value initialized to the specified coroutine.
+		/// Internal use only, for external use, see ExecutionContext.CoroutineCreate
+		/// </summary>
+		/// <param name="coroutineHandle">The coroutine handle.</param>
+		/// <returns></returns>
+		internal static DynValue NewCoroutine(int coroutineHandle)
+		{
+			return new DynValue()
+			{
+				m_Object = coroutineHandle,
+				m_Type = DataType.Thread
+			};
+		}
+
 
 		/// <summary>
 		/// Creates a new writable value initialized to the specified closure (function).
@@ -225,6 +248,22 @@ namespace MoonSharp.Interpreter
 			};
 		}
 
+
+
+		/// <summary>
+		/// Creates a new request for a yield of the current coroutine.
+		/// </summary>
+		/// <param name="args">The yield argumenst.</param>
+		/// <returns></returns>
+		public static DynValue NewYieldReq(DynValue[] args)
+		{
+			return new DynValue()
+			{
+				m_Object = new YieldRequest() { ReturnValues = args },
+				m_Type = DataType.YieldRequest,
+			};
+		}
+
 		/// <summary>
 		/// Creates a new tuple initialized to the specified values.
 		/// </summary>
@@ -360,6 +399,8 @@ namespace MoonSharp.Interpreter
 					return string.Join("\t", Tuple.Select(t => t.ToPrintString()).ToArray());
 				case DataType.TailCallRequest:
 					return "(TailCallRequest -- INTERNAL!)";
+				case DataType.YieldRequest:
+					return "(YieldRequest -- INTERNAL!)";
 				case DataType.UserData:
 					return "(UserData)";
 				case DataType.Thread:
@@ -626,6 +667,39 @@ namespace MoonSharp.Interpreter
 			this.m_Number = num;
 		}
 
+		/// <summary>
+		/// Expands tuples to a list using functiona arguments logic
+		/// </summary>
+		/// <param name="args">The arguments to expand.</param>
+		/// <param name="target">The target list (if null, a new list is created and returned).</param>
+		/// <returns></returns>
+		public static IList<DynValue> ExpandArgumentsToList(IList<DynValue> args, List<DynValue> target = null)
+		{
+			target = target ?? new List<DynValue>();
+
+			for(int i = 0; i < args.Count; i++)
+			{
+				DynValue v = args[i];
+
+				if (v.Type == DataType.Tuple)
+				{
+					if (i == args.Count - 1)
+					{
+						ExpandArgumentsToList(v.Tuple, target);
+					}
+					else if (v.Tuple.Length > 0)
+					{
+						target.Add(v.Tuple[0]);
+					}
+				}
+				else
+				{
+					target.Add(v);
+				}
+			}
+
+			return target;
+		}
 
 		/// <summary>
 		/// Creates a new DynValue from a CLR object
@@ -645,6 +719,7 @@ namespace MoonSharp.Interpreter
 		{
 			return MoonSharp.Interpreter.Interop.ConversionHelper.MoonSharpValueToClrObject(this);
 		}
+
 	}
 
 

+ 13 - 0
src/MoonSharp.Interpreter/DataTypes/YieldRequest.cs

@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MoonSharp.Interpreter
+{
+	public class YieldRequest
+	{
+		public DynValue[] ReturnValues;
+		public int InstructionPtr;
+	}
+}

+ 14 - 0
src/MoonSharp.Interpreter/Errors/ScriptRuntimeException.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Text;
 
 using Antlr4.Runtime.Tree;
+using MoonSharp.Interpreter.Execution.VM;
 
 namespace MoonSharp.Interpreter
 {
@@ -133,5 +134,18 @@ namespace MoonSharp.Interpreter
 		{
 			return new ScriptRuntimeException("cannot access field {0} of userdata<{1}>", fieldname, typename);
 		}
+
+		public static ScriptRuntimeException CannotResumeNotSuspended(CoroutineState state)
+		{
+			if (state == CoroutineState.Dead)
+				return new ScriptRuntimeException("cannot resume dead coroutine");
+			else
+				return new ScriptRuntimeException("cannot resume non-suspended coroutine");
+		}
+
+		public static ScriptRuntimeException CannotYield()
+		{
+			return new ScriptRuntimeException("cannot yield to parent coroutine as it would cross a script-clr boundary");
+		}
 	}
 }

+ 29 - 0
src/MoonSharp.Interpreter/Execution/ExecutionContext.cs → src/MoonSharp.Interpreter/Execution/ScriptExecutionContext.cs

@@ -80,5 +80,34 @@ namespace MoonSharp.Interpreter.Execution
 			return true;
 		}
 
+		public DynValue CoroutineCreate(Closure closure)
+		{
+			return m_Processor.Coroutine_Create(closure);
+		}
+
+		public DynValue CoroutineResume(DynValue handle, DynValue[] args)
+		{
+			Processor P = m_Processor.Coroutine_Get(handle);
+			return P.Coroutine_Resume(args);
+		}
+
+		public DynValue CoroutineRunning()
+		{
+			return m_Processor.Coroutine_GetRunning();
+		}
+
+		public CoroutineState CoroutineGetState(DynValue handle)
+		{
+			return m_Processor.Coroutine_Get(handle).State;
+		}
+
+		public bool CoroutineIsRunning(DynValue handle)
+		{
+			DynValue running = CoroutineRunning();
+			return (running.CoroutineHandle == handle.CoroutineHandle) ;
+		}
+
+
+
 	}
 }

+ 17 - 0
src/MoonSharp.Interpreter/Execution/VM/CoroutineState.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter.DataStructs;
+
+namespace MoonSharp.Interpreter.Execution.VM
+{
+	public enum CoroutineState
+	{
+		Main,
+		NotStarted,
+		Suspended,
+		Running,
+		Dead
+	}
+}

+ 16 - 0
src/MoonSharp.Interpreter/Execution/VM/ExecutionState.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter.DataStructs;
+
+namespace MoonSharp.Interpreter.Execution.VM
+{
+	internal sealed class ExecutionState
+	{
+		public FastStack<DynValue> ValueStack = new FastStack<DynValue>(131072);
+		public FastStack<CallStackItem> ExecutionStack = new FastStack<CallStackItem>(131072);
+		public int InstructionPtr = 0;
+		public CoroutineState State = CoroutineState.NotStarted;
+	}
+}

+ 18 - 0
src/MoonSharp.Interpreter/Execution/VM/Processor/DebugContext.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter.Debugging;
+
+namespace MoonSharp.Interpreter.Execution.VM
+{
+	sealed partial class Processor
+	{
+		private class DebugContext
+		{
+			public IDebugger DebuggerAttached = null;
+			public DebuggerAction.ActionType DebuggerCurrentAction = DebuggerAction.ActionType.None;
+			public int DebuggerCurrentActionTarget = -1;
+		}
+	}
+}

+ 46 - 5
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor.cs

@@ -13,24 +13,65 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 		FastStack<DynValue> m_ValueStack = new FastStack<DynValue>(131072);
 		FastStack<CallStackItem> m_ExecutionStack = new FastStack<CallStackItem>(131072);
+
 		Table m_GlobalTable;
 
-		IDebugger m_DebuggerAttached = null;
-		DebuggerAction.ActionType m_DebuggerCurrentAction = DebuggerAction.ActionType.None;
-		int m_DebuggerCurrentActionTarget = -1;
+
 		Script m_Script;
+		Processor m_Parent = null;
+		List<Processor> m_Coroutines = null;
+		CoroutineState m_State;
+		bool m_CanYield = true;
+		int m_SavedInstructionPtr = -1;
+		DebugContext m_Debug;
 
 		public Processor(Script script, Table globalContext, ByteCode byteCode)
 		{
+			m_Debug = new DebugContext();
 			m_RootChunk = byteCode;
 			m_GlobalTable = globalContext;
 			m_Script = script;
+			m_Coroutines = new List<Processor>();
+			m_State = CoroutineState.Main;
+		}
+
+		private Processor(Processor parentProcessor)
+		{
+			m_Debug = parentProcessor.m_Debug;
+			m_RootChunk = parentProcessor.m_RootChunk;
+			m_GlobalTable = parentProcessor.m_GlobalTable;
+			m_Script = parentProcessor.m_Script;
+			m_Coroutines = parentProcessor.m_Coroutines;
+			m_Parent = parentProcessor;
+			m_State = CoroutineState.NotStarted;
 		}
 
+		
+
 
 		public DynValue Call(DynValue function, DynValue[] args)
 		{
-			m_ValueStack.Push(function);  // func val
+			m_CanYield = false;
+
+			try
+			{
+				int entrypoint = PushClrToScriptStackFrame(function, args);
+				return Processing_Loop(entrypoint);
+			}
+			finally
+			{
+				m_CanYield = true;
+			}
+		}
+
+		// pushes all what's required to perform a clr-to-script function call. function can be null if it's already
+		// at vstack top.
+		private int PushClrToScriptStackFrame(DynValue function, DynValue[] args)
+		{
+			if (function == null) 
+				function = m_ValueStack.Peek();
+			else
+				m_ValueStack.Push(function);  // func val
 
 			args = Internal_AdjustTuple(args);
 
@@ -47,7 +88,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 				ClosureScope = function.Function.ClosureContext,
 			});
 
-			return Processing_Loop(function.Function.EntryPointByteCodeLocation);
+			return function.Function.EntryPointByteCodeLocation;
 		}
 
 

+ 79 - 0
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Coroutines.cs

@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using MoonSharp.Interpreter.DataStructs;
+using MoonSharp.Interpreter.Debugging;
+
+namespace MoonSharp.Interpreter.Execution.VM
+{
+	// This part is practically written procedural style - it looks more like C than C#.
+	// This is intentional so to avoid this-calls and virtual-calls as much as possible.
+	// Same reason for the "sealed" declaration.
+	sealed partial class Processor
+	{
+		public DynValue Coroutine_Create(Closure closure)
+		{
+			// create a processor instance
+			Processor P = new Processor(this);
+			int coroutineHandle = this.m_Coroutines.Count;
+			this.m_Coroutines.Add(P);
+
+			// Put the closure as first value on the stack, for future reference
+			P.m_ValueStack.Push(DynValue.NewClosure(closure));
+
+			// Return the coroutine handle
+			return DynValue.NewCoroutine(coroutineHandle);
+		}
+
+		public Processor Coroutine_Get(DynValue handle)
+		{
+			return m_Coroutines[handle.CoroutineHandle];
+		}
+
+		public DynValue Coroutine_GetRunning()
+		{
+			for (int coroutineHandle = 0; coroutineHandle < m_Coroutines.Count; coroutineHandle++)
+			{
+				if (m_Coroutines[coroutineHandle] == this)
+					return DynValue.NewCoroutine(coroutineHandle);
+			}
+
+			throw new InternalErrorException("coroutine list exception in Coroutine_GetRunning - coroutine not found");
+		}
+
+		public CoroutineState State { get { return m_State; } }
+
+		public DynValue Coroutine_Resume(DynValue[] args)
+		{
+			int entrypoint = 0;
+
+			if (m_State != CoroutineState.NotStarted && m_State != CoroutineState.Suspended)
+				throw ScriptRuntimeException.CannotResumeNotSuspended(m_State);
+
+			if (m_State == CoroutineState.NotStarted)
+				entrypoint = PushClrToScriptStackFrame(null, args);
+			else
+				entrypoint = m_SavedInstructionPtr;
+
+			m_State = CoroutineState.Running;
+			DynValue retVal = Processing_Loop(entrypoint);
+
+			if (retVal.Type == DataType.YieldRequest)
+			{
+				m_State = CoroutineState.Suspended;
+				m_SavedInstructionPtr = retVal.YieldRequest.InstructionPtr;
+				return DynValue.NewTuple(retVal.YieldRequest.ReturnValues);
+			}
+			else
+			{
+				m_State = CoroutineState.Dead;
+				return retVal;
+			}
+		}
+
+
+
+	}
+
+}

+ 16 - 16
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_Debugger.cs

@@ -14,42 +14,42 @@ namespace MoonSharp.Interpreter.Execution.VM
 	{
 		internal void AttachDebugger(IDebugger debugger)
 		{
-			m_DebuggerAttached = debugger;
+			m_Debug.DebuggerAttached = debugger;
 		}
 
 		private void ListenDebugger(Instruction instr, int instructionPtr)
 		{
 			if (instr.Breakpoint)
 			{
-				m_DebuggerCurrentAction = DebuggerAction.ActionType.None;
-				m_DebuggerCurrentActionTarget = -1;
+				m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.None;
+				m_Debug.DebuggerCurrentActionTarget = -1;
 			}
 
-			if (m_DebuggerCurrentAction == DebuggerAction.ActionType.Run)
+			if (m_Debug.DebuggerCurrentAction == DebuggerAction.ActionType.Run)
 				return;
 
-			if (m_DebuggerCurrentAction == DebuggerAction.ActionType.StepOver && m_DebuggerCurrentActionTarget != instructionPtr)
+			if (m_Debug.DebuggerCurrentAction == DebuggerAction.ActionType.StepOver && m_Debug.DebuggerCurrentActionTarget != instructionPtr)
 				return;
 
 			RefreshDebugger();
 
 			while (true)
 			{
-				var action = m_DebuggerAttached.GetAction(instructionPtr);
+				var action = m_Debug.DebuggerAttached.GetAction(instructionPtr);
 
 				switch (action.Action)
 				{
 					case DebuggerAction.ActionType.StepIn:
-						m_DebuggerCurrentAction = DebuggerAction.ActionType.StepIn;
-						m_DebuggerCurrentActionTarget = -1;
+						m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.StepIn;
+						m_Debug.DebuggerCurrentActionTarget = -1;
 						return;
 					case DebuggerAction.ActionType.StepOver:
-						m_DebuggerCurrentAction = DebuggerAction.ActionType.StepOver;
-						m_DebuggerCurrentActionTarget = instructionPtr + 1;
+						m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.StepOver;
+						m_Debug.DebuggerCurrentActionTarget = instructionPtr + 1;
 						return;
 					case DebuggerAction.ActionType.Run:
-						m_DebuggerCurrentAction = DebuggerAction.ActionType.Run;
-						m_DebuggerCurrentActionTarget = -1;
+						m_Debug.DebuggerCurrentAction = DebuggerAction.ActionType.Run;
+						m_Debug.DebuggerCurrentActionTarget = -1;
 						return;
 					case DebuggerAction.ActionType.ToggleBreakpoint:
 						m_RootChunk.Code[action.InstructionPtr].Breakpoint = !m_RootChunk.Code[action.InstructionPtr].Breakpoint;
@@ -66,13 +66,13 @@ namespace MoonSharp.Interpreter.Execution.VM
 
 		private void RefreshDebugger()
 		{
-			List<string> watchList = m_DebuggerAttached.GetWatchItems();
+			List<string> watchList = m_Debug.DebuggerAttached.GetWatchItems();
 			List<WatchItem> callStack = Debugger_GetCallStack();
 			List<WatchItem> watches = Debugger_RefreshWatches(watchList);
 			List<WatchItem> vstack = Debugger_RefreshVStack();
-			m_DebuggerAttached.Update(WatchType.CallStack, callStack);
-			m_DebuggerAttached.Update(WatchType.Watches, watches);
-			m_DebuggerAttached.Update(WatchType.VStack, vstack);
+			m_Debug.DebuggerAttached.Update(WatchType.CallStack, callStack);
+			m_Debug.DebuggerAttached.Update(WatchType.Watches, watches);
+			m_Debug.DebuggerAttached.Update(WatchType.VStack, vstack);
 		}
 
 		private List<WatchItem> Debugger_RefreshVStack()

+ 47 - 29
src/MoonSharp.Interpreter/Execution/VM/Processor/Processor_InstructionLoop.cs

@@ -10,8 +10,13 @@ namespace MoonSharp.Interpreter.Execution.VM
 {
 	sealed partial class Processor
 	{
+		const int YIELD_SPECIAL_TRAP = -99;
+
 		private DynValue Processing_Loop(int instructionPtr)
 		{
+		// This is the main loop of the processor, has a weird control flow and needs to be as fast as possible.
+		// This sentence is just a convoluted way to say "don't complain about gotos".
+
 			repeat_execution:
 
 			try
@@ -20,7 +25,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 				{
 					Instruction i = m_RootChunk.Code[instructionPtr];
 
-					if (m_DebuggerAttached != null)
+					if (m_Debug.DebuggerAttached != null)
 					{
 						ListenDebugger(i, instructionPtr);
 					}
@@ -46,42 +51,55 @@ namespace MoonSharp.Interpreter.Execution.VM
 							break;
 						case OpCode.Add:
 							instructionPtr = ExecAdd(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Concat:
 							instructionPtr = ExecConcat(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Neg:
 							instructionPtr = ExecNeg(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Sub:
 							instructionPtr = ExecSub(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Mul:
 							instructionPtr = ExecMul(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Div:
 							instructionPtr = ExecDiv(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Mod:
 							instructionPtr = ExecMod(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Power:
 							instructionPtr = ExecPower(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Eq:
 							instructionPtr = ExecEq(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.LessEq:
 							instructionPtr = ExecLessEq(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Less:
 							instructionPtr = ExecLess(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Len:
 							instructionPtr = ExecLen(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Call:
 							instructionPtr = Internal_ExecCall(i.NumVal, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Scalar:
 							m_ValueStack.Push(m_ValueStack.Pop().ToScalar());
@@ -95,6 +113,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 						case OpCode.JfOrPop:
 						case OpCode.JtOrPop:
 							instructionPtr = ExecShortCircuitingOperator(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.JNil:
 							{
@@ -103,12 +122,15 @@ namespace MoonSharp.Interpreter.Execution.VM
 								if (v.Type == DataType.Nil)
 									instructionPtr = i.NumVal;
 							}
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Jf:
 							instructionPtr = JumpBool(i, false, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Jump:
 							instructionPtr = i.NumVal;
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.MkTuple:
 							ExecMkTuple(i);
@@ -134,6 +156,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 							break;
 						case OpCode.Ret:
 							instructionPtr = ExecRet(i);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							if (instructionPtr < 0)
 								goto return_to_native_code;
 							break;
@@ -145,6 +168,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 							break;
 						case OpCode.JFor:
 							instructionPtr = ExecJFor(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.NewTable:
 							m_ValueStack.Push(DynValue.NewTable(this.m_Script));
@@ -178,9 +202,11 @@ namespace MoonSharp.Interpreter.Execution.VM
 							break;
 						case OpCode.Index:
 							instructionPtr = ExecIndex(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.IndexSet:
 							instructionPtr = ExecIndexSet(i, instructionPtr);
+							if (instructionPtr == YIELD_SPECIAL_TRAP) goto yield_to_calling_coroutine;
 							break;
 						case OpCode.Invalid:
 							throw new NotImplementedException(string.Format("Invalid opcode : {0}", i.Name));
@@ -222,13 +248,17 @@ namespace MoonSharp.Interpreter.Execution.VM
 			}
 
 		return_to_native_code:
+			return m_ValueStack.Pop();
+
+		yield_to_calling_coroutine:
+
+			DynValue yieldRequest = m_ValueStack.Pop();
+
+			if (m_CanYield)
+				return yieldRequest;
+			else
+				throw ScriptRuntimeException.CannotYield();
 
-			//if (m_ValueStack.Count == 1)
-				return m_ValueStack.Pop();
-			//else if (m_ValueStack.Count == 0)
-			//	return DynValue.Nil;
-			//else
-			//	throw new InternalErrorException("Unexpected value stack count at program end : {0}", m_ValueStack.Count);
 		}
 
 
@@ -327,8 +357,8 @@ namespace MoonSharp.Interpreter.Execution.VM
 			double? v = m_ValueStack.Pop().CastToNumber();
 			if (v.HasValue)
 				m_ValueStack.Push(DynValue.NewNumber(v.Value));
-			else  
-				throw  ScriptRuntimeException.ConvertToNumberFailed(i.NumVal);
+			else
+				throw ScriptRuntimeException.ConvertToNumberFailed(i.NumVal);
 		}
 
 
@@ -517,24 +547,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 			}
 		}
 
-		private IList<DynValue> ExpandToList(IList<DynValue> args, List<DynValue> target = null)
-		{
-			target = target ?? new List<DynValue>();
-
-			foreach (DynValue v in args)
-			{
-				if (v.Type == DataType.Tuple)
-				{
-					ExpandToList(v.Tuple, target);
-				}
-				else
-				{
-					target.Add(v);
-				}
-			}
 
-			return target;
-		}
 
 
 		private int Internal_ExecCall(int argsCount, int instructionPtr, CallbackFunction handler = null, CallbackFunction continuation = null)
@@ -548,7 +561,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 				// we expand tuples before callbacks
 				if (args.Any(v => v.Type == DataType.Tuple))
 				{
-					args = ExpandToList(args);
+					args = DynValue.ExpandArgumentsToList(args);
 				}
 
 				var ret = fn.Callback.Invoke(new ScriptExecutionContext(this, fn.Callback), args);
@@ -567,7 +580,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 					Debug_EntryPoint = fn.Function.EntryPointByteCodeLocation,
 					ClosureScope = fn.Function.ClosureContext,
 					ErrorHandler = handler,
-					Continuation = continuation 
+					Continuation = continuation
 				});
 				return fn.Function.EntryPointByteCodeLocation;
 			}
@@ -656,6 +669,11 @@ namespace MoonSharp.Interpreter.Execution.VM
 				//instructionPtr -= 1;
 				return Internal_ExecCall(tcd.Args.Length, instructionPtr, tcd.ErrorHandler, tcd.Continuation);
 			}
+			else if (tail.Type == DataType.YieldRequest)
+			{
+				tail.YieldRequest.InstructionPtr = instructionPtr;
+				return YIELD_SPECIAL_TRAP;
+			}
 
 
 			return instructionPtr;
@@ -1000,7 +1018,7 @@ namespace MoonSharp.Interpreter.Execution.VM
 		private int ExecIndexSet(Instruction i, int instructionPtr)
 		{
 			int nestedMetaOps = 100; // sanity check, to avoid potential infinite loop here
-			
+
 			// stack: vals.. - base - index
 			DynValue idx = i.Value ?? m_ValueStack.Pop();
 			DynValue obj = m_ValueStack.Pop();

+ 2 - 1
src/MoonSharp.Interpreter/Modules/CoreModules.cs

@@ -17,11 +17,12 @@ namespace MoonSharp.Interpreter
 		Table = 0x20,
 		ErrorHandling = 0x80,
 		Math = 0x100,
+		Coroutine = 0x200,
 
 
 
 		Preset_HardSandbox = GlobalConsts | TableIterators | String | Table | Basic | Math,
-		Preset_SoftSandbox = Preset_HardSandbox | Metatables | ErrorHandling,
+		Preset_SoftSandbox = Preset_HardSandbox | Metatables | ErrorHandling | Coroutine,
 		Preset_Default = Preset_SoftSandbox | LoadMethods,
 		Preset_Complete = Preset_Default,
 

+ 1 - 0
src/MoonSharp.Interpreter/Modules/ModuleRegister.cs

@@ -23,6 +23,7 @@ namespace MoonSharp.Interpreter
 			if (modules.Has(CoreModules.Table)) RegisterModuleType<TableModule_Globals>(table);
 			if (modules.Has(CoreModules.ErrorHandling)) RegisterModuleType<ErrorHandling>(table);
 			if (modules.Has(CoreModules.Math)) RegisterModuleType<MathModule>(table);
+			if (modules.Has(CoreModules.Coroutine)) RegisterModuleType<CoroutineMethods>(table);
 
 			return table;
 		}

+ 7 - 1
src/MoonSharp.Interpreter/MoonSharp.Interpreter.csproj

@@ -76,6 +76,7 @@
     <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="CoreLib\CoroutineMethods.cs" />
     <Compile Include="CoreLib\BasicMethods.cs" />
     <Compile Include="CoreLib\ErrorHandling.cs" />
     <Compile Include="CoreLib\LoadMethods.cs" />
@@ -104,6 +105,7 @@
       <SubType>Code</SubType>
     </Compile>
     <Compile Include="DataTypes\WellKnownSymbols.cs" />
+    <Compile Include="DataTypes\YieldRequest.cs" />
     <Compile Include="Debugging\DebuggerAction.cs" />
     <Compile Include="Debugging\IDebugger.cs" />
     <Compile Include="Debugging\SourceCodeType.cs" />
@@ -127,11 +129,15 @@
     </Compile>
     <Compile Include="DataTypes\TablePair.cs" />
     <Compile Include="Execution\FileLoadRequestedEventArgs.cs" />
-    <Compile Include="Execution\ExecutionContext.cs">
+    <Compile Include="Execution\ScriptExecutionContext.cs">
       <SubType>Code</SubType>
     </Compile>
     <Compile Include="Debugging\SourceCode.cs" />
     <Compile Include="Execution\VM\Chunk.cs" />
+    <Compile Include="Execution\VM\CoroutineState.cs" />
+    <Compile Include="Execution\VM\ExecutionState.cs" />
+    <Compile Include="Execution\VM\Processor\DebugContext.cs" />
+    <Compile Include="Execution\VM\Processor\Processor_Coroutines.cs" />
     <Compile Include="Execution\VM\Processor\Processor_Errors.cs" />
     <Compile Include="Interface\IteratorHelper.cs" />
     <Compile Include="Interop\ConversionHelper.cs" />

+ 7 - 12
src/MoonSharp.Interpreter/Script.cs

@@ -21,14 +21,13 @@ namespace MoonSharp.Interpreter
 	/// </summary>
 	public class Script
 	{
-		Processor m_MainRoutine = null;
-		List<Processor> m_Coroutines = new List<Processor>();
+		Processor m_MainProcessor = null;
 		ByteCode m_ByteCode;
 		List<SourceCode> m_Sources = new List<SourceCode>();
 		Table m_GlobalTable;
 		IDebugger m_Debugger;
 		IScriptLoader m_ScriptLoader = DefaultScriptLoader;
-		Table[] m_TypeMetatables = new Table[(int)DataType.MaxMetaTypes];
+		Table[] m_TypeMetatables = new Table[(int)LuaTypeExtensions.MaxMetaTypes];
 
 		static Script()
 		{
@@ -53,7 +52,7 @@ namespace MoonSharp.Interpreter
 			DebugPrint = s => { Console.WriteLine(s); };
 			m_ByteCode = new ByteCode();
 			m_GlobalTable = new Table(this).RegisterCoreModules(coreModules);
-			m_MainRoutine = new Processor(this, m_GlobalTable, m_ByteCode);
+			m_MainProcessor = new Processor(this, m_GlobalTable, m_ByteCode);
 			ReseedRandomGenerator(DateTime.Now.Millisecond);
 		}
 
@@ -243,7 +242,7 @@ namespace MoonSharp.Interpreter
 		/// <returns></returns>
 		public DynValue Call(DynValue function)
 		{
-			return m_MainRoutine.Call(function, new DynValue[0]);
+			return m_MainProcessor.Call(function, new DynValue[0]);
 		}
 
 		/// <summary>
@@ -254,7 +253,7 @@ namespace MoonSharp.Interpreter
 		/// <returns></returns>
 		public DynValue Call(DynValue function, params DynValue[] args)
 		{
-			return m_MainRoutine.Call(function, args);
+			return m_MainProcessor.Call(function, args);
 		}
 
 		/// <summary>
@@ -265,7 +264,7 @@ namespace MoonSharp.Interpreter
 		/// <returns></returns>
 		public DynValue Call(DynValue function, params object[] args)
 		{
-			return m_MainRoutine.Call(function, args.Select(v => DynValue.FromObject(this, v)).ToArray());
+			return m_MainProcessor.Call(function, args.Select(v => DynValue.FromObject(this, v)).ToArray());
 		}
 
 
@@ -306,11 +305,7 @@ namespace MoonSharp.Interpreter
 		public void AttachDebugger(IDebugger debugger)
 		{
 			m_Debugger = debugger;
-			m_MainRoutine.AttachDebugger(debugger);
-
-			foreach (var C in m_Coroutines)
-				C.AttachDebugger(debugger);
-
+			m_MainProcessor.AttachDebugger(debugger);
 			m_Debugger.SetSourceCode(m_ByteCode, null);
 		}