跳转到内容

模組:PythonScript/Evaluator/Core/Stmt

维基百科,自由的百科全书
-- Module:PythonScript/Evaluator/Core/Stmt
-- AST 敘述與區塊執行器 (負責 If, While, For, Try, Match, With, FunctionDef, ClassDef, Import, Goto 等)
local Stmt = {}

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

local SIGNAL_BREAK = Utils.SIGNAL_BREAK
local SIGNAL_CONTINUE = Utils.SIGNAL_CONTINUE
local make_return = Utils.make_return
local create_scope = Utils.create_scope
local set_var = Utils.set_var
local del_var = Utils.del_var
local is_truthy = Utils.is_truthy
local resolve_index = Utils.resolve_index
local resolve_slice = Utils.resolve_slice
local py_eq = Utils.py_eq
local match_pattern = Utils.match_pattern
local make_iter_state = Utils.make_iter_state
local get_next_item = Utils.get_next_item
local assign_target = Utils.assign_target

local bind_and_exec_func = nil
function Stmt.bind_func_executor(baef)
	bind_and_exec_func = baef
end

function Stmt.create(eval_expr)
	local invoke = nil
	local exec_stmt, exec_block

	exec_block = function(stmts, scope)
		local pc = 1
		while pc <= #stmts do
			local stmt = stmts[pc]
			local sig = exec_stmt(stmt, scope)
			if sig then
				if sig.type == "goto" and sig.target_block == stmts then
					pc = sig.target_pc
				else
					return sig
				end
			else
				pc = pc + 1
			end
		end
		return nil
	end

	exec_stmt = function(node, scope)
		if node.line then Runtime.Traceback.current_line = node.line end
		if node.type == "ExprStatement" then
			if node.expr.type == "Yield" then
				local val = Runtime.builtins.None
				if node.expr.value then val = eval_expr(node.expr.value, scope) end
				return { type = "return", value = val, is_yield = true }
			elseif node.expr.type == "YieldFrom" then
				local val = eval_expr(node.expr.value, scope)
				return { type = "return", value = val, is_yield_from = true }
			end
			eval_expr(node.expr, scope)
		elseif node.type == "AnnAssign" then
			local ann_val = eval_expr(node.annotation, scope)
			if node.target.type == "Identifier" then
				local ann_dict = scope.vars["__annotations__"]
				if not ann_dict then
					ann_dict = { __type = "dict", pairs = {} }
					scope.vars["__annotations__"] = ann_dict
				end
				local found = false
				for _, p in ipairs(ann_dict.pairs) do
					if p.key == node.target.name then
						p.value = ann_val; found = true; break
					end
				end
				if not found then
					table.insert(ann_dict.pairs, { key = node.target.name, value = ann_val })
				end
			end
			if node.value then
				local val = eval_expr(node.value, scope)
				assign_target(node.target, val, scope)
			end
		elseif node.type == "If" then
			if is_truthy(eval_expr(node.test, scope)) then return exec_block(node.body, scope)
			elseif node.orelse then return exec_block(node.orelse, scope) end
		elseif node.type == "While" then
			local break_hit = false
			while is_truthy(eval_expr(node.test, scope)) do
				local sig = exec_block(node.body, scope)
				if sig then
					if sig.type == "break" then break_hit = true; break end
					if sig.type == "return" or sig.type == "goto" then return sig end
				end
			end
			if not break_hit and node.orelse then return exec_block(node.orelse, scope) end
		elseif node.type == "For" then
			local iter_state = make_iter_state(eval_expr(node.iter, scope)); local break_hit = false
			while true do
				local has_next, v = get_next_item(iter_state); if not has_next then break end
				assign_target(node.target, v, scope); local sig = exec_block(node.body, scope)
				if sig then
					if sig.type == "break" then break_hit = true; break end
					if sig.type == "return" or sig.type == "goto" then return sig end
				end
			end
			if not break_hit and node.orelse then return exec_block(node.orelse, scope) end
		elseif node.type == "Try" then
			local prev_stack_len = #Runtime.Traceback.stack
			local prev_depth = Runtime.Traceback.eval_depth
			local prev_file_idx = Runtime.Traceback.current_file_idx
			local prev_func_name = Runtime.Traceback.current_func_name
			local pending_sig, uncaught_err = nil, nil

			local success, err_or_sig = pcall(function() return exec_block(node.body, scope) end)
			Runtime.Traceback.eval_depth = prev_depth

			if success then
				if err_or_sig then
					pending_sig = err_or_sig
				elseif node.orelse and #node.orelse > 0 then
					local else_ok, else_res = pcall(function() return exec_block(node.orelse, scope) end)
					Runtime.Traceback.eval_depth = prev_depth
					if else_ok then pending_sig = else_res else uncaught_err = else_res end
				end
			else
				while #Runtime.Traceback.stack > prev_stack_len do table.remove(Runtime.Traceback.stack) end
				Runtime.Traceback.current_file_idx = prev_file_idx
				Runtime.Traceback.current_func_name = prev_func_name
				if type(err_or_sig) == "string" and err_or_sig:lower():find("stack overflow", 1, true) then
					local rec_inst = PyClass.make_instance(Runtime.builtins.RecursionError)
					PyClass.set_attr(rec_inst, "msg", "maximum recursion depth exceeded")
					PyClass.set_attr(rec_inst, "args", { __type = "tuple", elements = { "maximum recursion depth exceeded" } })
					err_or_sig = rec_inst
				end
				if type(err_or_sig) ~= "table" or not err_or_sig.__class then
					uncaught_err = err_or_sig
				else
					local handled = false
					for _, handler in ipairs(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, scope)
							local function check_match(target) return PyClass.is_instance(err_or_sig, 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
							handled = true
							if handler.name then set_var(handler.name, err_or_sig, scope) end
							local prev_active = Runtime.active_exception
							Runtime.active_exception = err_or_sig
							local h_ok, h_res = pcall(function() return exec_block(handler.body, scope) end)
							Runtime.Traceback.eval_depth = prev_depth
							Runtime.active_exception = prev_active
							if h_ok then pending_sig = h_res else uncaught_err = h_res end
							break
						end
					end
					if not handled then uncaught_err = err_or_sig end
				end
			end

			if node.finalbody and #node.finalbody > 0 then
				local fin_sig = exec_block(node.finalbody, scope)
				if fin_sig then return fin_sig end
			end

			if uncaught_err ~= nil then error(uncaught_err, 0) end
			return pending_sig
		elseif node.type == "Match" then
			local subject_val = eval_expr(node.subject, scope)
			for _, case_node in ipairs(node.cases) do
				local case_scope = create_scope(scope)
				if match_pattern(case_node.pattern, subject_val, case_scope, scope) then
					local guard_pass = true
					if case_node.guard then guard_pass = is_truthy(eval_expr(case_node.guard, case_scope)) end
					if guard_pass then
						for k, v in pairs(case_scope.vars) do scope.vars[k] = v end
						local sig = exec_block(case_node.body, scope); if sig then return sig end; break
					end
				end
			end
		elseif node.type == "Global" then for _, name in ipairs(node.names) do scope.globals[name] = true end
		elseif node.type == "Nonlocal" then for _, name in ipairs(node.names) do scope.nonlocals[name] = true end
		elseif node.type == "Assert" then
			if not is_truthy(eval_expr(node.test, scope)) then
				local msg_str = ""; if node.msg then msg_str = Runtime.builtins.str.__call({eval_expr(node.msg, scope)}) end
				Runtime.raise("AssertionError", msg_str)
			end
		elseif node.type == "Delete" then
			for _, target in ipairs(node.targets) do
				if target.type == "Identifier" then del_var(target.name, scope)
				elseif target.type == "Index" then
					local t_obj = eval_expr(target.target, scope)
					if target.index.type == "Slice" then
						if type(t_obj) == "table" and t_obj.__type == "instance" and t_obj.__builtin_value ~= nil then
							t_obj = t_obj.__builtin_value
						end
						if type(t_obj) == "table" and t_obj.__type == "bytearray" then
							local lower = target.index.lower and eval_expr(target.index.lower, scope) or nil
							local upper = target.index.upper and eval_expr(target.index.upper, scope) or nil
							local step = target.index.step and eval_expr(target.index.step, scope) or nil
							local p_start, p_stop, p_step = resolve_slice(#t_obj._data, lower, upper, step)
							if p_step == 1 then
								t_obj._data = t_obj._data:sub(1, p_start) .. t_obj._data:sub(p_stop + 1)
							else
								local del_map = {}
								if p_step > 0 then for i = p_start, p_stop - 1, p_step do del_map[i + 1] = true end
								else for i = p_start, p_stop + 1, p_step do del_map[i + 1] = true end end
								local kept = {}
								for i = 1, #t_obj._data do if not del_map[i] then table.insert(kept, t_obj._data:sub(i, i)) end end
								t_obj._data = table.concat(kept)
							end
						elseif type(t_obj) == "table" and t_obj.__type == "list" then
							local lower = target.index.lower and eval_expr(target.index.lower, scope) or nil
							local upper = target.index.upper and eval_expr(target.index.upper, scope) or nil
							local step = target.index.step and eval_expr(target.index.step, scope) or nil
							local p_start, p_stop, p_step = resolve_slice(#t_obj.elements, lower, upper, step)
							local to_delete = {}
							if p_step > 0 then for i = p_start, p_stop - 1, p_step do table.insert(to_delete, i + 1) end
							else for i = p_start, p_stop + 1, p_step do table.insert(to_delete, i + 1) end end
							table.sort(to_delete, function(a, b) return a > b end)
							for _, idx in ipairs(to_delete) do table.remove(t_obj.elements, idx) end
						else
							Runtime.raise("TypeError", "object doesn't support slice deletion")
						end
					else
						local idx = eval_expr(target.index, scope)
						local handled_delitem = false
						if type(t_obj) == "table" and t_obj.__type == "instance" then
							local prev_depth = Runtime.Traceback.eval_depth
							local success, attr = pcall(PyClass.get_attr, t_obj, "__delitem__")
							Runtime.Traceback.eval_depth = prev_depth
							if success and attr and attr.__type == "bound_method" then
								bind_and_exec_func(attr.__func, {idx}, {}, attr.__self)
								handled_delitem = true
							elseif t_obj.__builtin_value ~= nil then
								t_obj = t_obj.__builtin_value
							end
						end
						if not handled_delitem then
							if type(t_obj) == "table" and t_obj.__type == "dict" then
								local found = false
								for i, p in ipairs(t_obj.pairs) do
									if py_eq(p.key, idx) then
										table.remove(t_obj.pairs, i)
										if t_obj.__scope_ref and type(idx) == "string" then t_obj.__scope_ref[idx] = nil end
										found = true; break
									end
								end
								if not found then Runtime.raise("KeyError", tostring(idx)) end
							else
								local lua_idx = resolve_index(t_obj, idx)
								if type(t_obj) == "table" and t_obj.__type == "list" then table.remove(t_obj.elements, lua_idx)
								elseif type(t_obj) == "table" and t_obj.__type == "bytearray" then t_obj._data = t_obj._data:sub(1, lua_idx - 1) .. t_obj._data:sub(lua_idx + 1)
								else Runtime.raise("TypeError", "object doesn't support item deletion") end
							end
						end
					end
				elseif target.type == "Attribute" then PyClass.set_attr(eval_expr(target.target, scope), target.attr, nil) end
			end
		elseif node.type == "Raise" then
			local exc = nil
			if node.exc then
				exc = eval_expr(node.exc, scope)
				if type(exc) == "table" and exc.__type == "class" then exc = PyClass.make_instance(exc) end
			else
				exc = Runtime.active_exception
				if not exc then Runtime.raise("RuntimeError", "No active exception to reraise") end
			end
			if node.cause then
				local cause = eval_expr(node.cause, scope)
				PyClass.set_attr(exc, "__cause__", cause)
				PyClass.set_attr(exc, "__suppress_context__", true)
			end
			Runtime.raise(exc)
		elseif node.type == "With" then
			local managers = {}
			for _, item in ipairs(node.items) do
				local ctx = eval_expr(item.context_expr, scope)
				local enter_func = PyClass.get_attr(ctx, "__enter__")
				local exit_func = PyClass.get_attr(ctx, "__exit__")
				if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
				local enter_res = invoke(enter_func, {}, {})
				table.insert(managers, { exit_func = exit_func, ctx = ctx })
				if item.optional_vars then assign_target(item.optional_vars, enter_res, scope) end
			end
			local prev_stack_len = #Runtime.Traceback.stack
			local prev_depth = Runtime.Traceback.eval_depth
			local prev_file_idx = Runtime.Traceback.current_file_idx
			local prev_func_name = Runtime.Traceback.current_func_name
			local success, sig = pcall(function() return exec_block(node.body, scope) end)
			Runtime.Traceback.eval_depth = prev_depth
			if not success then
				while #Runtime.Traceback.stack > prev_stack_len do table.remove(Runtime.Traceback.stack) end
				Runtime.Traceback.current_file_idx = prev_file_idx
				Runtime.Traceback.current_func_name = prev_func_name
				if type(sig) == "string" and sig:lower():find("stack overflow", 1, true) then
					local rec_inst = PyClass.make_instance(Runtime.builtins.RecursionError)
					PyClass.set_attr(rec_inst, "msg", "maximum recursion depth exceeded")
					PyClass.set_attr(rec_inst, "args", { __type = "tuple", elements = { "maximum recursion depth exceeded" } })
					sig = rec_inst
				end
			end

			local None = Runtime.builtins.None
			if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
			for i = #managers, 1, -1 do
				local m = managers[i]
				local exc_type, exc_val, tb = None, None, None
				if not success then
					exc_val = sig
					exc_type = (type(exc_val) == "table" and exc_val.__class) or None
					tb = (type(exc_val) == "table" and exc_val.__dict and exc_val.__dict.__traceback_info__) or None
				end
				local suppress = invoke(m.exit_func, {exc_type, exc_val, tb}, {})
				if not success and is_truthy(suppress) then success = true; sig = nil end
			end
			if not success then error(sig, 0) end
			return sig
		elseif node.type == "FunctionDef" then
			local defaults_val = {}; if node.defaults then for arg_name, expr_node in pairs(node.defaults) do defaults_val[arg_name] = eval_expr(expr_node, scope) end end
			local ann_pairs = {}
			if node.annotations then
				for _, arg_name in ipairs(node.args or {}) do
					if node.annotations[arg_name] then
						table.insert(ann_pairs, { key = arg_name, value = eval_expr(node.annotations[arg_name], scope) })
					end
				end
				if node.vararg and node.annotations[node.vararg] then
					table.insert(ann_pairs, { key = node.vararg, value = eval_expr(node.annotations[node.vararg], scope) })
				end
				for _, kw_name in ipairs(node.kwonlyargs or {}) do
					if node.annotations[kw_name] then
						table.insert(ann_pairs, { key = kw_name, value = eval_expr(node.annotations[kw_name], scope) })
					end
				end
				if node.kwarg and node.annotations[node.kwarg] then
					table.insert(ann_pairs, { key = node.kwarg, value = eval_expr(node.annotations[node.kwarg], scope) })
				end
			end
			if node.returns then
				table.insert(ann_pairs, { key = "return", value = eval_expr(node.returns, scope) })
			end
			local curr = scope; while curr.parent do curr = curr.parent end; local mod_name = curr.vars["__name__"] or "__main__"
			local qname = node.name; if scope.is_class_scope then qname = scope.class_name .. "." .. node.name end
			local bind_scope = scope
			if scope.is_class_scope then bind_scope = scope.parent end
			local func_obj = {
				__type = "function", __name = node.name, __qualname__ = qname,
				__node = node, __scope = bind_scope, __defaults = defaults_val,
				__module__ = mod_name,
				__dict = { __annotations__ = { __type = "dict", pairs = ann_pairs } }
			}
			local final_obj = func_obj
			if node.decorator_list and #node.decorator_list > 0 then
				for i = #node.decorator_list, 1, -1 do
					local deco = eval_expr(node.decorator_list[i], scope)
					final_obj = eval_expr({ type = "Call", callee = { type = "DirectValue", value = deco }, args = { { type = "DirectValue", value = final_obj } }, kwargs = {} }, scope)
				end
			end
			set_var(node.name, final_obj, scope)
		elseif node.type == "ClassDef" then
			local bases = {}
			for _, b_node in ipairs(node.bases or {}) do
				if b_node.type == "Starred" then
					local iter_state = make_iter_state(eval_expr(b_node.value, scope))
					while true do
						local has_next, v = get_next_item(iter_state); if not has_next then break end
						table.insert(bases, v)
					end
				else
					table.insert(bases, eval_expr(b_node, scope))
				end
			end

			local passed_kwargs = {}
			for _, kw_node in ipairs(node.kwargs or {}) do
				if kw_node.key then
					local v = eval_expr(kw_node.value, scope)
					if passed_kwargs[kw_node.key] ~= nil then
						Runtime.raise("TypeError", "got multiple values for keyword argument '" .. tostring(kw_node.key) .. "'")
					end
					passed_kwargs[kw_node.key] = v
				else
					local dict_obj = eval_expr(kw_node.value, scope)
					if type(dict_obj) == "table" and dict_obj.__type == "dict" then
						for _, pair in ipairs(dict_obj.pairs) do
							if passed_kwargs[pair.key] ~= nil then
								Runtime.raise("TypeError", "got multiple values for keyword argument '" .. tostring(pair.key) .. "'")
							end
							passed_kwargs[pair.key] = pair.value
						end
					else Runtime.raise("TypeError", "argument after ** must be a mapping") end
				end
			end

			local explicit_meta = passed_kwargs["metaclass"]
			passed_kwargs["metaclass"] = nil
			local meta = PyClass.resolve_metaclass(explicit_meta, bases)
			local prep_ns = PyClass.prepare_namespace(meta, node.name, bases, passed_kwargs)

			local curr = scope; while curr.parent do curr = curr.parent end
			local mod_name = curr.vars["__name__"] or "__main__"

			local class_scope = create_scope(scope)
			class_scope.is_class_scope = true
			class_scope.class_name = node.name
			class_scope.custom_ns = prep_ns

			if type(prep_ns) == "table" and prep_ns.__type == "dict" then
				for _, p in ipairs(prep_ns.pairs or {}) do
					if type(p.key) == "string" then class_scope.vars[p.key] = p.value end
				end
			end
			set_var("__module__", mod_name, class_scope)
			set_var("__qualname__", node.name, class_scope)

			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, node.line or prev_line})
			Runtime.Traceback.current_func_name = node.name
			exec_block(node.body, class_scope)
			table.remove(Runtime.Traceback.stack)
			Runtime.Traceback.current_func_name = prev_func_name

			local class_obj
			if meta == Runtime.builtins.type then
				class_obj = PyClass.make_class(node.name, bases, class_scope.vars)
				PyClass.init_class_hooks(class_obj, meta, passed_kwargs)
			else
				if type(invoke) ~= type(function()end) then invoke = require("Module:PythonScript/Evaluator").invoke end
				local bases_tup = { __type = "tuple", elements = bases }
				local ns_arg = prep_ns
				if type(ns_arg) == "table" and ns_arg.__type == "dict" then
					ns_arg.__scope_ref = class_scope.vars
				elseif type(ns_arg) == "table" then
					ns_arg._class_vars = class_scope.vars
				end
				class_obj = invoke(meta, { node.name, bases_tup, ns_arg }, passed_kwargs)
			end

			local final_obj = class_obj
			if node.decorator_list and #node.decorator_list > 0 then
				for i = #node.decorator_list, 1, -1 do
					local deco = eval_expr(node.decorator_list[i], scope)
					final_obj = eval_expr({ type = "Call", callee = { type = "DirectValue", value = deco }, args = { { type = "DirectValue", value = final_obj } } }, scope)
				end
			end
			set_var(node.name, final_obj, scope)
		elseif node.type == "Import" then
			local ImportMod = require("Module:PythonScript/Import")
			local root_scope = scope.parent or scope
			local function resolve_sub_import(mod_name)
				local ok, mod = pcall(ImportMod.execute_import, mod_name, root_scope)
				if ok then return mod end
				local root_pkg, rest = mod_name:match("^([^%.]+)%.(.+)$")
				if root_pkg and rest then
					local ok_r, pkg = pcall(ImportMod.execute_import, root_pkg, root_scope)
					if ok_r and pkg then
						local cur = pkg
						for part in rest:gmatch("[^%.]+") do
							local ok_a, nxt = pcall(PyClass.get_attr, cur, part)
							if not ok_a or nxt == nil then error(mod, 0) end
							cur = nxt
						end
						return cur
					end
				end
				error(mod, 0)
			end
			local items = node.names or { { name = node.name, alias = node.alias } }
			for _, item in ipairs(items) do
				local imported_obj = resolve_sub_import(item.name)
				local target_name = item.alias or item.name:match("([^/%.%:-]+)$") or item.name
				set_var(target_name, imported_obj, scope)
				if item.name == "goto" and scope.vars["with_goto"] == nil then
					local ok_wg, wg = pcall(PyClass.get_attr, imported_obj, "with_goto")
					if ok_wg and wg then set_var("with_goto", wg, scope) end
				end
			end
		elseif node.type == "FromImport" then
			local ImportMod = require("Module:PythonScript/Import")
			local root_scope = scope; while root_scope.parent do root_scope = root_scope.parent end
			local function resolve_sub_import(mod_name)
				local d_save = Runtime.Traceback.eval_depth
				local ok, mod = pcall(ImportMod.execute_import, mod_name, root_scope)
				Runtime.Traceback.eval_depth = d_save
				if ok then return true, mod end
				local root_pkg, rest = mod_name:match("^([^%.]+)%.(.+)$")
				if root_pkg and rest then
					local ok_r, pkg = pcall(ImportMod.execute_import, root_pkg, root_scope)
					Runtime.Traceback.eval_depth = d_save
					if ok_r and pkg then
						local cur = pkg
						local all_ok = true
						for part in rest:gmatch("[^%.]+") do
							local ok_a, nxt = pcall(PyClass.get_attr, cur, part)
							Runtime.Traceback.eval_depth = d_save
							if ok_a and nxt ~= nil then cur = nxt else all_ok = false; break end
						end
						if all_ok then return true, cur end
					end
				end
				return false, mod
			end
			local ok_pkg, imported_obj = resolve_sub_import(node.module)

			for _, item in ipairs(node.names) do
				if item.name == "*" then
					if not ok_pkg then error(imported_obj, 0) end
					scope.star_imports = scope.star_imports or {}
					table.insert(scope.star_imports, imported_obj)
				else
					local val
					local found = false
					if ok_pkg then
						local prev_depth = Runtime.Traceback.eval_depth
						local success, res = pcall(PyClass.get_attr, imported_obj, item.name)
						Runtime.Traceback.eval_depth = prev_depth
						if success then val = res; found = true end
					end
					if not found then
						local sub_module_name = (node.module:match("^%.+$") and (node.module .. item.name)) or (node.module .. "." .. item.name)
						local success2, sub_mod = resolve_sub_import(sub_module_name)
						if success2 then
							val = sub_mod
						elseif not ok_pkg then
							error(imported_obj, 0)
						else
							Runtime.raise("ImportError", "cannot import name '" .. item.name .. "' from '" .. node.module .. "'")
						end
					end
					local target_name = item.alias or item.name; set_var(target_name, val, scope)
				end
			end
		elseif node.type == "Return" then
			local val = Runtime.builtins.None
			if node.value then val = eval_expr(node.value, scope) end
			return make_return(val)
		elseif node.type == "Break" then return SIGNAL_BREAK
		elseif node.type == "Continue" then return SIGNAL_CONTINUE
		elseif node.type == "Pass" then
		elseif node.type == "Label" then
			if not scope.__goto_ctx then
				Runtime.raise("NameError", "name 'label' is not defined")
			end
		elseif node.type == "Goto" then
			local gctx = scope.__goto_ctx
			if not gctx then
				Runtime.raise("NameError", "name 'goto' is not defined")
			end
			local target_info = gctx.labels[node.target]
			if not target_info then
				Runtime.raise("NameError", "name '" .. tostring(node.target) .. "' is not defined")
			end
			gctx.jumps = gctx.jumps + 1
			if gctx.jumps > 100000 then
				Runtime.raise("RuntimeError", "maximum goto jump limit (100000) exceeded")
			end
			return {
				type = "goto",
				target = node.target,
				target_block = target_info.block,
				target_pc = target_info.pc,
				line = node.line
			}
		end
	end

	return exec_stmt, exec_block
end

return Stmt