模組: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