跳转到内容

模組:PythonScript/Evaluator/Core/Coroutine

维基百科,自由的百科全书
-- Module:PythonScript/Evaluator/Core/Coroutine
-- 無堆疊協程狀態機 (Stackless Generator & The Grand Interceptor) 與函式參數綁定器
local Coroutine = {}

local PyClass = require("Module:PythonScript/Class")
local Runtime = require("Module:PythonScript/Runtime")
local Utils = require("Module:PythonScript/Evaluator/Utils")

local create_scope = Utils.create_scope
local set_var = Utils.set_var
local is_truthy = Utils.is_truthy
local make_iter_state = Utils.make_iter_state
local get_next_item = Utils.get_next_item
local assign_target = Utils.assign_target
local build_goto_labels = Utils.build_goto_labels

function Coroutine.create(eval_expr, exec_stmt, exec_block)
	local invoke = nil
	local bind_and_exec_func, exec_generator_step

	-- ==========================================
	-- 1. 無堆疊協程與生成器狀態機 (全新例外路由架構)
	-- ==========================================
	exec_generator_step = function(gen, send_val, throw_exc)
		if gen.__dict.is_finished then
			if throw_exc then error(throw_exc, 0) end
			Runtime.raise("StopIteration")
		end
		if not gen.__dict.started then
			if throw_exc then
				gen.__dict.is_finished = true
				error(throw_exc, 0)
			end
			if send_val ~= nil and send_val ~= Runtime.builtins.None then Runtime.raise("TypeError", "can't send non-None value to a just-started generator") end
			gen.__dict.started = true
		end
		gen.__dict.sent_value = send_val; local stack = gen.__dict.stack

		local func_name = gen.__name__ or "<generator>"
		local prev_func_name = Runtime.Traceback.current_func_name or "<module>"
		local prev_line = Runtime.Traceback.current_line
		table.insert(Runtime.Traceback.stack, {Runtime.Traceback.current_file_idx, prev_func_name, prev_line})
		Runtime.Traceback.current_func_name = func_name
		local prev_depth = Runtime.Traceback.eval_depth

		local is_running = true
		local ret_val = nil

		while is_running do
			local success, res = pcall(function()
				if throw_exc then
					local e = throw_exc
					throw_exc = nil
					local top_frame = stack[#stack]
					if top_frame and top_frame.yielding_from_state then
						local yf_state = top_frame.yielding_from_state
						local is_gen_exit = (type(e) == "table" and e.__class and PyClass.is_subclass(e.__class, Runtime.builtins.GeneratorExit))
						if is_gen_exit then
							top_frame.yielding_from_state = nil
							if yf_state.type == "iterator" and yf_state.obj then
								local ok_c, close_m = pcall(PyClass.get_attr, yf_state.obj, "close")
								if ok_c and close_m then
									if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
									invoke(close_m, {}, {})
								end
							end
							error(e, 0)
						elseif yf_state.type == "iterator" and yf_state.obj then
							local ok_t, throw_m = pcall(PyClass.get_attr, yf_state.obj, "throw")
							if ok_t and throw_m then
								if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
								local d_save = Runtime.Traceback.eval_depth
								local ok_sub, sub_res = pcall(invoke, throw_m, { e }, {})
								Runtime.Traceback.eval_depth = d_save
								if ok_sub then return sub_res end
								top_frame.yielding_from_state = nil
								if type(sub_res) == "table" and sub_res.__class and sub_res.__class.__name == "StopIteration" then
									local r_val = sub_res.__dict.value
									if r_val == nil then r_val = Runtime.builtins.None end
									if top_frame.waiting_assign then
										local target = (top_frame.stmt.type == "ExprStatement" and top_frame.stmt.expr.type == "Assign") and top_frame.stmt.expr.target or top_frame.stmt.target
										assign_target(target, r_val, top_frame.scope)
										top_frame.waiting_assign = false
									end
									top_frame.pc = top_frame.pc + 1
								else
									error(sub_res, 0)
								end
							else
								top_frame.yielding_from_state = nil
								error(e, 0)
							end
						else
							top_frame.yielding_from_state = nil
							error(e, 0)
						end
					else
						error(e, 0)
					end
				end

				while #stack > 0 do
					local frame = stack[#stack]; local skip_normal_execution = false
					if frame.yielding_from_state then
						skip_normal_execution = true; local iter_state = frame.yielding_from_state; local exhausted = false; local r_val = nil
						if iter_state.type == "iterator" then
							local d_save = Runtime.Traceback.eval_depth
							local success_iter, iter_res = pcall(function()
								if gen.__dict.sent_value ~= nil and gen.__dict.sent_value ~= Runtime.builtins.None then
									local success_send, send_func = pcall(PyClass.get_attr, iter_state.obj, "send")
									if success_send and send_func and send_func.__type == "bound_method" then return bind_and_exec_func(send_func.__func, {gen.__dict.sent_value}, {}, send_func.__self) end
								end
								if iter_state.next_func.__type == "bound_method" then return bind_and_exec_func(iter_state.next_func.__func, {}, {}, iter_state.next_func.__self) end
							end)
							Runtime.Traceback.eval_depth = d_save
							if not success_iter then
								if type(iter_res) == "table" and iter_res.__class and iter_res.__class.__name == "StopIteration" then
									exhausted = true
									r_val = iter_res.__dict.value
									if r_val == nil then r_val = Runtime.builtins.None end
								else error(iter_res, 0) end
							else return iter_res end
						else
							if gen.__dict.sent_value ~= nil and gen.__dict.sent_value ~= Runtime.builtins.None then Runtime.raise("TypeError", "can't send non-None value to a just-started generator or list iterator") end
							local has_next, val = get_next_item(iter_state)
							if has_next then return val else exhausted = true; r_val = Runtime.builtins.None end
						end
						if exhausted then
							frame.yielding_from_state = nil
							if frame.waiting_assign then
								local target = (frame.stmt.type == "ExprStatement" and frame.stmt.expr.type == "Assign") and frame.stmt.expr.target or frame.stmt.target
								assign_target(target, r_val, frame.scope)
								frame.waiting_assign = false
							end
							frame.pc = frame.pc + 1; gen.__dict.sent_value = nil; skip_normal_execution = false
						end
					end

					if not skip_normal_execution then
						if frame.waiting_assign then
							frame.waiting_assign = false
							local target = (frame.stmt.type == "ExprStatement" and frame.stmt.expr.type == "Assign") and frame.stmt.expr.target or frame.stmt.target
							assign_target(target, gen.__dict.sent_value, frame.scope)
							frame.pc = frame.pc + 1
						end
						if frame.pc > #frame.stmts then
							table.remove(stack)
							if frame.type == "while" then
								if is_truthy(eval_expr(frame.node.test, frame.scope)) then frame.pc = 1; table.insert(stack, frame)
								elseif frame.node.orelse and #frame.node.orelse > 0 then table.insert(stack, { type = "block", stmts = frame.node.orelse, pc = 1, scope = frame.scope }) end
							elseif frame.type == "for" then
								local has_next, val = get_next_item(frame.iter_state)
								if has_next then assign_target(frame.node.target, val, frame.scope); frame.pc = 1; table.insert(stack, frame)
								elseif frame.node.orelse and #frame.node.orelse > 0 then table.insert(stack, { type = "block", stmts = frame.node.orelse, pc = 1, scope = frame.scope }) end
							elseif frame.type == "try" then
								table.insert(stack, { type = "try_post", node = frame.node, scope = frame.scope, stmts = {}, pc = 1 })
								if frame.node.orelse and #frame.node.orelse > 0 then table.insert(stack, { type = "block", stmts = frame.node.orelse, pc = 1, scope = frame.scope }) end
							elseif frame.type == "try_post" or frame.type == "try_handler_post" then
								if frame.node.finalbody and #frame.node.finalbody > 0 then table.insert(stack, { type = "block", stmts = frame.node.finalbody, pc = 1, scope = frame.scope }) end
							elseif frame.type == "with" then
								if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
								for i = #frame.managers, 1, -1 do invoke(frame.managers[i].exit_func, {Runtime.builtins.None, Runtime.builtins.None, Runtime.builtins.None}, {}) end
							elseif frame.type == "unwind_resume" then
								if frame.flow_type == "exception" then error(frame.flow_val, 0)
								else error({ __is_control_flow = true, flow_type = frame.flow_type, value = frame.flow_val }, 0) end
							end
						else
							local stmt = frame.stmts[frame.pc]; frame.stmt = stmt
							local is_standalone, is_assigned, yield_node = false, false, nil

							if stmt.type == "ExprStatement" then
								if stmt.expr.type == "Yield" or stmt.expr.type == "YieldFrom" or stmt.expr.type == "Await" then
									is_standalone = true; yield_node = stmt.expr
								elseif stmt.expr.type == "Assign" and (stmt.expr.value.type == "Yield" or stmt.expr.value.type == "YieldFrom" or stmt.expr.value.type == "Await") then
									is_assigned = true; yield_node = stmt.expr.value
								end
							elseif stmt.type == "Assign" and (stmt.value.type == "Yield" or stmt.value.type == "YieldFrom" or stmt.value.type == "Await") then
								is_assigned = true; yield_node = stmt.value
							end

							if is_standalone or is_assigned then
								if yield_node.type == "YieldFrom" or yield_node.type == "Await" then
									local iterable = eval_expr(yield_node.value, frame.scope)
									frame.yielding_from_state = make_iter_state(iterable)
									if is_assigned then frame.waiting_assign = true end
									gen.__dict.sent_value = nil
								elseif yield_node.type == "Yield" then
									local y_val = Runtime.builtins.None
									if yield_node.value then y_val = eval_expr(yield_node.value, frame.scope) end
									if is_assigned then frame.waiting_assign = true else frame.pc = frame.pc + 1 end
									return y_val
								end
							else
								if stmt.type == "If" then
									frame.pc = frame.pc + 1
									if is_truthy(eval_expr(stmt.test, frame.scope)) then table.insert(stack, { type = "block", stmts = stmt.body, pc = 1, scope = frame.scope })
									elseif stmt.orelse and #stmt.orelse > 0 then table.insert(stack, { type = "block", stmts = stmt.orelse, pc = 1, scope = frame.scope }) end
								elseif stmt.type == "While" then
									frame.pc = frame.pc + 1
									if is_truthy(eval_expr(stmt.test, frame.scope)) then table.insert(stack, { type = "while", node = stmt, stmts = stmt.body, pc = 1, scope = frame.scope })
									elseif stmt.orelse and #stmt.orelse > 0 then table.insert(stack, { type = "block", stmts = stmt.orelse, pc = 1, scope = frame.scope }) end
								elseif stmt.type == "For" then
									frame.pc = frame.pc + 1; local iter_state = make_iter_state(eval_expr(stmt.iter, frame.scope)); local has_next, val = get_next_item(iter_state)
									if has_next then assign_target(stmt.target, val, frame.scope); table.insert(stack, { type = "for", node = stmt, stmts = stmt.body, pc = 1, scope = frame.scope, iter_state = iter_state })
									elseif stmt.orelse and #stmt.orelse > 0 then table.insert(stack, { type = "block", stmts = stmt.orelse, pc = 1, scope = frame.scope }) end
								elseif stmt.type == "Break" then error({ __is_control_flow = true, flow_type = "break" }, 0)
								elseif stmt.type == "Continue" then error({ __is_control_flow = true, flow_type = "continue" }, 0)
								elseif stmt.type == "Return" then
									local r_val = Runtime.builtins.None; if stmt.value then r_val = eval_expr(stmt.value, frame.scope) end
									error({ __is_control_flow = true, flow_type = "return", value = r_val }, 0)
								elseif stmt.type == "Label" then
									if not frame.scope.__goto_ctx then Runtime.raise("NameError", "name 'label' is not defined") end
									frame.pc = frame.pc + 1
								elseif stmt.type == "Goto" then
									local gctx = frame.scope.__goto_ctx
									if not gctx then Runtime.raise("NameError", "name 'goto' is not defined") end
									local t_info = gctx.labels[stmt.target]
									if not t_info then Runtime.raise("NameError", "name '" .. tostring(stmt.target) .. "' is not defined") end
									local found_frame = false
									while #stack > 0 do
										if stack[#stack].stmts == t_info.block then stack[#stack].pc = t_info.pc; found_frame = true; break end
										table.remove(stack)
									end
									if not found_frame then Runtime.raise("SyntaxError", "cannot jump into nested block to label '" .. tostring(stmt.target) .. "'") end
								elseif stmt.type == "Try" then
									frame.pc = frame.pc + 1
									table.insert(stack, { type = "try", node = stmt, stmts = stmt.body, pc = 1, scope = frame.scope })
								elseif stmt.type == "With" then
									frame.pc = frame.pc + 1
									local managers = {}
									if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
									for _, item in ipairs(stmt.items) do
										local ctx = eval_expr(item.context_expr, frame.scope)
										local enter_res = invoke(PyClass.get_attr(ctx, "__enter__"), {}, {})
										table.insert(managers, { exit_func = PyClass.get_attr(ctx, "__exit__"), ctx = ctx })
										if item.optional_vars then assign_target(item.optional_vars, enter_res, frame.scope) end
									end
									table.insert(stack, { type = "with", node = stmt, managers = managers, stmts = stmt.body, pc = 1, scope = frame.scope })
								else
									exec_stmt(stmt, frame.scope); frame.pc = frame.pc + 1
								end
							end
						end
					end
				end
				return { __is_generator_finish = true }
			end)

			if success then
				if type(res) == "table" and res.__is_generator_finish then
					gen.__dict.is_finished = true
					is_running = false
					Runtime.raise("StopIteration")
				else
					ret_val = res
					is_running = false
				end
			else
				-- ==========================================
				-- The Grand Interceptor (例外路由與跳轉)
				-- ==========================================
				local is_cf = type(res) == "table" and res.__is_control_flow
				local exc_obj = not is_cf and res or nil
				local flow_type = is_cf and res.flow_type or "exception"
				local flow_val = is_cf and res.value or exc_obj

				local handled = false
				if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
				while #stack > 0 do
					local frame = table.remove(stack)
					if frame.type == "with" then
						local suppress = false
						for i = #frame.managers, 1, -1 do
							local m = frame.managers[i]
							local etype, eval, etb = Runtime.builtins.None, Runtime.builtins.None, Runtime.builtins.None
							if flow_type == "exception" then
								eval = flow_val
								etype = (type(eval) == "table" and eval.__class) or Runtime.builtins.Exception
								etb = (type(eval) == "table" and eval.__dict and eval.__dict.__traceback_info__) or Runtime.builtins.None
							end
							local sup = invoke(m.exit_func, {etype, eval, etb}, {})
							if flow_type == "exception" and is_truthy(sup) then suppress = true end
						end
						if suppress then handled = true; break end
					elseif frame.type == "try" then
						if flow_type == "exception" then
							local matched_handler = nil
							for _, handler in ipairs(frame.node.handlers) do
								local is_match = false
								if not handler.type_expr then is_match = true else
									local target_types = eval_expr(handler.type_expr, frame.scope)
									local function check_match(target) return PyClass.is_instance(flow_val, target) end
									if type(target_types) == "table" and target_types.__type == "tuple" then
										for _, t in ipairs(target_types.elements) do if check_match(t) then is_match = true; break end end
									else is_match = check_match(target_types) end
								end
								if is_match then matched_handler = handler; break end
							end
							if matched_handler then
								if matched_handler.name then set_var(matched_handler.name, flow_val, frame.scope) end
								table.insert(stack, { type = "try_handler_post", node = frame.node, scope = frame.scope, stmts = {}, pc = 1 })
								table.insert(stack, { type = "block", stmts = matched_handler.body, pc = 1, scope = frame.scope })
								handled = true; break
							end
						end
						if frame.node.finalbody and #frame.node.finalbody > 0 then
							table.insert(stack, { type = "unwind_resume", flow_type = flow_type, flow_val = flow_val, stmts = {}, pc = 1 })
							table.insert(stack, { type = "block", stmts = frame.node.finalbody, pc = 1, scope = frame.scope })
							handled = true; break
						end
					elseif frame.type == "try_post" or frame.type == "try_handler_post" then
						if frame.node.finalbody and #frame.node.finalbody > 0 then
							table.insert(stack, { type = "unwind_resume", flow_type = flow_type, flow_val = flow_val, stmts = {}, pc = 1 })
							table.insert(stack, { type = "block", stmts = frame.node.finalbody, pc = 1, scope = frame.scope })
							handled = true; break
						end
					elseif frame.type == "while" or frame.type == "for" then
						if flow_type == "break" then handled = true; break
						elseif flow_type == "continue" then frame.pc = #frame.stmts + 1; table.insert(stack, frame); handled = true; break end
					end
				end
				if not handled then
					gen.__dict.is_finished = true
					if flow_type == "exception" then
						table.remove(Runtime.Traceback.stack)
						Runtime.Traceback.current_func_name = prev_func_name
						Runtime.Traceback.current_line = prev_line
						Runtime.Traceback.eval_depth = prev_depth
						error(flow_val, 0)
					elseif flow_type == "return" then
						local exc = PyClass.make_instance(Runtime.builtins.StopIteration)
						if flow_val ~= Runtime.builtins.None then PyClass.set_attr(exc, "value", flow_val) end
						error(exc, 0)
					else error("SyntaxError: 'break' or 'continue' outside loop", 0) end
				end
			end
		end

		Runtime.Traceback.eval_depth = prev_depth
		table.remove(Runtime.Traceback.stack)
		Runtime.Traceback.current_func_name = prev_func_name
		Runtime.Traceback.current_line = prev_line
		return ret_val
	end

	-- ==========================================
	-- 2. 函式參數綁定與對外全能介面
	-- ==========================================
	bind_and_exec_func = function(func_obj, passed_args, passed_kwargs, self_obj)
		if type(func_obj) == "table" and func_obj.__type == "bound_method" then
			return bind_and_exec_func(func_obj.__func, passed_args, passed_kwargs, func_obj.__self)
		end

		local safe_args = {}
		if self_obj then table.insert(safe_args, self_obj) end
		for _, v in ipairs(passed_args or {}) do table.insert(safe_args, v) end

		local safe_kwargs = {}
		for k, v in pairs(passed_kwargs or {}) do safe_kwargs[k] = v end

		if func_obj.__type == "builtin_method" or (func_obj.__type == "function" and func_obj.__call) then
			return func_obj.__call(safe_args, safe_kwargs)
		end

		local func_node = func_obj.__node; local func_scope = create_scope(func_obj.__scope)
		if func_node.name == "<genexpr>" then
			func_scope.is_comp_scope = true
		end
		if func_obj.__defining_class then
			func_scope.__super_cls = func_obj.__defining_class
			func_scope.__super_self = safe_args[1]
		end
		if func_obj.__with_goto__ then
			func_scope.__goto_ctx = {
				labels = build_goto_labels(func_node.body),
				jumps = 0
			}
		end

		local bound_names = {}; local p_idx = 1
		local posonly_count = func_node.posonly_count or 0
		for _, arg_name in ipairs(func_node.args or {}) do
			if p_idx <= #safe_args then
				set_var(arg_name, safe_args[p_idx], func_scope); bound_names[arg_name] = true; p_idx = p_idx + 1
			else break end
		end

		if func_node.vararg then
			local varargs = {}
			while p_idx <= #safe_args do table.insert(varargs, safe_args[p_idx]); p_idx = p_idx + 1 end
			set_var(func_node.vararg, { __type = "tuple", elements = varargs }, func_scope)
		elseif p_idx <= #safe_args then
			Runtime.raise("TypeError", "too many positional arguments")
		end

		for i, arg_name in ipairs(func_node.args or {}) do
			if i <= posonly_count then
				if not func_node.kwarg and safe_kwargs[arg_name] ~= nil then
					Runtime.raise("TypeError", "got some positional-only arguments passed as keyword arguments: '" .. arg_name .. "'")
				end
				if not bound_names[arg_name] then
					if func_obj.__defaults and func_obj.__defaults[arg_name] ~= nil then
						set_var(arg_name, func_obj.__defaults[arg_name], func_scope)
						bound_names[arg_name] = true
					else
						Runtime.raise("TypeError", "missing required positional-only argument: '" .. arg_name .. "'")
					end
				end
			else
				if not bound_names[arg_name] then
					if safe_kwargs[arg_name] ~= nil then
						set_var(arg_name, safe_kwargs[arg_name], func_scope)
						safe_kwargs[arg_name] = nil
						bound_names[arg_name] = true
					elseif func_obj.__defaults and func_obj.__defaults[arg_name] ~= nil then
						set_var(arg_name, func_obj.__defaults[arg_name], func_scope)
						bound_names[arg_name] = true
					else
						Runtime.raise("TypeError", "missing required argument '" .. arg_name .. "'")
					end
				elseif safe_kwargs[arg_name] ~= nil then
					Runtime.raise("TypeError", "multiple values for argument '" .. arg_name .. "'")
				end
			end
		end

		for _, kw_name in ipairs(func_node.kwonlyargs or {}) do
			if safe_kwargs[kw_name] ~= nil then
				set_var(kw_name, safe_kwargs[kw_name], func_scope)
				safe_kwargs[kw_name] = nil
				bound_names[kw_name] = true
			elseif func_obj.__defaults and func_obj.__defaults[kw_name] ~= nil then
				set_var(kw_name, func_obj.__defaults[kw_name], func_scope)
				bound_names[kw_name] = true
			else
				Runtime.raise("TypeError", "missing required keyword-only argument: '" .. kw_name .. "'")
			end
		end

		if func_node.kwarg then
			local kw_pairs = {}
			for k, v in pairs(safe_kwargs) do table.insert(kw_pairs, { key = k, value = v }) end
			set_var(func_node.kwarg, { __type = "dict", pairs = kw_pairs }, func_scope)
		else
			for k, _ in pairs(safe_kwargs) do Runtime.raise("TypeError", "got an unexpected keyword argument '" .. k .. "'") end
		end

		if func_node.is_generator or func_node.is_async then
			if not Runtime.builtins.GeneratorType then
				local GeneratorType_dict = {
					__iter__ = { __type = "builtin_method", __name = "__iter__", __call = function(args) return args[1] end },
					__next__ = { __type = "builtin_method", __name = "__next__", __call = function(args) return exec_generator_step(args[1], Runtime.builtins.None) end },
					send = { __type = "builtin_method", __name = "send", __call = function(args) return exec_generator_step(args[1], args[2] == nil and Runtime.builtins.None or args[2]) end },
					throw = { __type = "builtin_method", __name = "throw", __call = function(args)
						local gen, typ, val = args[1], args[2], args[3]
						local exc = val
						if exc == nil or exc == Runtime.builtins.None then
							if type(typ) == "table" and typ.__type == "class" then
								if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
								exc = invoke(typ, {}, {})
							else
								exc = typ
							end
						elseif type(exc) ~= "table" or exc.__type ~= "instance" then
							if type(typ) == "table" and typ.__type == "class" then
								if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
								exc = invoke(typ, { exc }, {})
							end
						end
						return exec_generator_step(gen, Runtime.builtins.None, exc)
					end },
					close = { __type = "builtin_method", __name = "close", __call = function(args)
						local gen = args[1]
						if gen.__dict.is_finished or not gen.__dict.started then
							gen.__dict.is_finished = true
							return Runtime.builtins.None
						end
						local ge = PyClass.make_instance(Runtime.builtins.GeneratorExit)
						local d_save = Runtime.Traceback.eval_depth
						local ok, res = pcall(exec_generator_step, gen, Runtime.builtins.None, ge)
						Runtime.Traceback.eval_depth = d_save
						if ok then
							gen.__dict.is_finished = true
							Runtime.raise("RuntimeError", "generator ignored GeneratorExit")
						else
							gen.__dict.is_finished = true
							if type(res) == "table" and res.__class and (
								PyClass.is_subclass(res.__class, Runtime.builtins.GeneratorExit) or
								PyClass.is_subclass(res.__class, Runtime.builtins.StopIteration)
							) then
								return Runtime.builtins.None
							end
							error(res, 0)
						end
					end }
				}
				Runtime.builtins.GeneratorType = PyClass.make_class("generator", {}, GeneratorType_dict)
				Runtime.builtins.CoroutineType = PyClass.make_class("coroutine", {}, GeneratorType_dict)
			end
			local gen_inst = PyClass.make_instance(func_node.is_async and Runtime.builtins.CoroutineType or Runtime.builtins.GeneratorType)
			gen_inst.__dict.stack = { { type = "block", stmts = func_node.body, pc = 1, scope = func_scope } }
			gen_inst.__dict.is_finished = false
			gen_inst.__dict.started = false
			return gen_inst
		end

		local func_name = func_obj.__qualname__ or func_obj.__name__ or func_obj.__name or "<lambda>"
		local prev_func_name = Runtime.Traceback.current_func_name or "<module>"
		local prev_line = Runtime.Traceback.current_line
		table.insert(Runtime.Traceback.stack, {Runtime.Traceback.current_file_idx, prev_func_name, prev_line})
		if #Runtime.Traceback.stack > 35 then
			table.remove(Runtime.Traceback.stack)
			Runtime.raise("RecursionError", "maximum recursion depth exceeded")
		end
		Runtime.Traceback.current_func_name = func_name
		Runtime.Traceback.current_line = func_node.line or prev_line
		local res = exec_block(func_node.body, func_scope)
		table.remove(Runtime.Traceback.stack)
		Runtime.Traceback.current_func_name = prev_func_name
		Runtime.Traceback.current_line = prev_line

		if res then
			if res.type == "return" then return res.value end
			if res.type == "goto" then
				Runtime.Traceback.current_line = res.line or Runtime.Traceback.current_line
				Runtime.raise("SyntaxError", "cannot jump into nested block to label '" .. tostring(res.target) .. "'")
			end
		end
		return Runtime.builtins.None
	end

	return bind_and_exec_func, exec_generator_step
end

return Coroutine