模組:PythonScript/Evaluator
外观
-- Module:PythonScript/Evaluator
-- 核心 AST 樹狀直譯器、無堆疊協程狀態機與對外呼叫介面
-- 子模組:
-- -[[Module:PythonScript/Evaluator]]
-- -[[Module:PythonScript/Evaluator/Core]]
-- -[[Module:PythonScript/Evaluator/Core/Ops]]
-- -[[Module:PythonScript/Evaluator/Core/Stmt]]
-- -[[Module:PythonScript/Evaluator/Core/Coroutine]]
local Evaluator = {}
local PyClass = require("Module:PythonScript/Class")
local Runtime = require("Module:PythonScript/Runtime")
local Utils = require("Module:PythonScript/Evaluator/Utils")
-- 引入 Utils 內部輔助函式作為區域常數以維持最佳效能
local create_scope = Utils.create_scope
local resolve_index = Utils.resolve_index
local bigint_op = Utils.bigint_op
-- 核心函式前向宣告
local eval_expr, raw_eval_expr, exec_stmt, exec_block, bind_and_exec_func, exec_generator_step = require("Module:PythonScript/Evaluator/Core").getCoreFunction()
function Evaluator.default_instantiate_class(callee, passed_args, passed_kwargs)
if callee.__call then return callee.__call(passed_args, passed_kwargs) end
-- 1. 查找並呼叫 __new__(cls, *args, **kwargs)
local new_attr = nil
for _, base_cls in ipairs(callee.__mro or {callee}) do
if base_cls.__dict and base_cls.__dict["__new__"] ~= nil then
new_attr = base_cls.__dict["__new__"]
break
end
end
local instance
if new_attr then
local new_func = (type(new_attr) == "table" and (new_attr.__type == "staticmethod" or new_attr.__type == "classmethod")) and new_attr.__func or new_attr
local new_args = { callee }
for _, v in ipairs(passed_args) do table.insert(new_args, v) end
instance = bind_and_exec_func(new_func, new_args, passed_kwargs)
else
instance = PyClass.make_instance(callee)
end
-- 2. 若 __new__ 回傳的是當前類別 (或其子類別) 的實例 (含元類別建構出的 class),才繼續執行 __init__
if type(instance) == "table" and (instance.__type == "instance" or instance.__type == "class") and PyClass.is_instance(instance, callee) then
local init = nil
local inst_cls = instance.__class
for _, base_cls in ipairs((inst_cls and inst_cls.__mro) or callee.__mro or {}) do
if base_cls.__dict and base_cls.__dict["__init__"] ~= nil then
init = base_cls.__dict["__init__"]
break
end
end
local default_obj_init = Runtime.builtins.object and Runtime.builtins.object.__dict.__init__
local default_type_init = Runtime.builtins.type and Runtime.builtins.type.__dict.__init__
if init and init ~= default_obj_init and init ~= default_type_init then
local init_res
if type(init) == "table" then
if init.__type == "function" or init.__type == "builtin_method" then
init_res = bind_and_exec_func(init, passed_args, passed_kwargs, instance)
elseif init.__type == "classmethod" then
init_res = bind_and_exec_func(init.__func, passed_args, passed_kwargs, instance.__class)
elseif init.__type == "staticmethod" then
init_res = bind_and_exec_func(init.__func, passed_args, passed_kwargs)
end
end
if init_res ~= nil and init_res ~= Runtime.builtins.None then
Runtime.raise("TypeError", "__init__() should return None")
end
elseif instance.__type == "instance" and #passed_args > 0 and PyClass.is_subclass(callee, Runtime.builtins.BaseException) then
instance.__dict.args = { __type = "tuple", elements = passed_args }
instance.__dict.msg = Runtime.builtins.str.__call({passed_args[1]})
if callee.__name == "StopIteration" then instance.__dict.value = passed_args[1] end
if callee.__name == "SystemExit" then instance.code = passed_args[1] end
end
end
return instance
end
function Evaluator.invoke(callee, args, kwargs, self_obj)
local passed_args = args or {}
local passed_kwargs = kwargs or {}
local r
if type(callee) == "function" then
r = callee(passed_args, passed_kwargs); return r == nil and Runtime.builtins.None or r
elseif type(callee) == "table" then
if callee.__type == "class" then
-- 若該類別擁有自訂元類別且定義了 __call__,優先交由元類別的 __call__(cls, *args, **kwargs) 攔截
local meta = callee.__class
if type(meta) == "table" and meta.__type == "class" and meta ~= Runtime.builtins.type then
for _, m_base in ipairs(meta.__mro or {}) do
if m_base == Runtime.builtins.type or m_base == Runtime.builtins.object then break end
local meta_call = m_base.__dict and m_base.__dict["__call__"]
if meta_call ~= nil then
r = bind_and_exec_func(meta_call, passed_args, passed_kwargs, callee)
return r == nil and Runtime.builtins.None or r
end
end
end
return Evaluator.default_instantiate_class(callee, passed_args, passed_kwargs)
elseif callee.__type == "instance" then
local prev_depth = Runtime.Traceback.eval_depth
local success, attr = pcall(PyClass.get_attr, callee, "__call__")
Runtime.Traceback.eval_depth = prev_depth
if success and attr then return Evaluator.invoke(attr, passed_args, passed_kwargs) end
elseif callee.__type == "bound_method" or callee.__type == "function" or callee.__type == "builtin_method" then
r = bind_and_exec_func(callee, passed_args, passed_kwargs, self_obj)
return r == nil and Runtime.builtins.None or r
elseif callee.__type == "complex" and callee.__call then
r = callee.__call(passed_args, passed_kwargs)
return r == nil and Runtime.builtins.None or r
end
end
local err_name = type(callee) == "table" and (callee.__class and callee.__class.__name or callee.__type) or type(callee)
Runtime.raise("TypeError", "'" .. err_name .. "' object is not callable")
end
function Evaluator.execute(ast, global_env)
local global_scope = create_scope(nil)
if global_env then for k, v in pairs(global_env) do global_scope.vars[k] = v end end
if ast.type == "Module" then
global_scope.__return_signal = exec_block(ast.body, global_scope)
else
Runtime.raise("RuntimeError", "Evaluator expected a Module AST")
end
return global_scope
end
-- 輔助:將直譯器內部 scope.vars 雙向同步回 Python dict 物件
local function sync_scope_to_dict(scope_vars, dict_obj)
if type(dict_obj) ~= "table" or dict_obj.__type ~= "dict" then return end
for i = #dict_obj.pairs, 1, -1 do
local p = dict_obj.pairs[i]
if type(p.key) == "string" and p.key ~= "__builtins__" and scope_vars[p.key] == nil then
table.remove(dict_obj.pairs, i)
if dict_obj.__scope_ref and dict_obj.__scope_ref ~= scope_vars then
dict_obj.__scope_ref[p.key] = nil
end
end
end
for k, v in pairs(scope_vars) do
local found = false
for _, p in ipairs(dict_obj.pairs) do
if p.key == k then p.value = v; found = true; break end
end
if not found then table.insert(dict_obj.pairs, { key = k, value = v }) end
if dict_obj.__scope_ref and dict_obj.__scope_ref ~= scope_vars then
dict_obj.__scope_ref[k] = v
end
end
if not dict_obj.__scope_ref then
dict_obj.__scope_ref = scope_vars
end
end
function Evaluator.eval_code(code_obj, globals_arg, locals_arg, caller_kind)
local function is_none(v)
return v == nil or (type(v) == "table" and v.__class and v.__class.__name == "NoneType")
end
local has_g = not is_none(globals_arg)
local has_l = not is_none(locals_arg)
if has_g and type(globals_arg) == "table" and globals_arg.__type == "instance" and type(globals_arg.__builtin_value) == "table" and globals_arg.__builtin_value.__type == "dict" then
globals_arg = globals_arg.__builtin_value
end
if has_g and (type(globals_arg) ~= "table" or globals_arg.__type ~= "dict") then
Runtime.raise("TypeError", caller_kind .. "() globals must be a dict")
end
if has_l and (type(locals_arg) ~= "table" or (locals_arg.__type ~= "dict" and locals_arg.__type ~= "instance")) then
Runtime.raise("TypeError", caller_kind .. "() locals must be a mapping")
end
local curr_scope = Runtime.Traceback.current_scope
local g_scope, exec_scope
if not has_g and not has_l then
exec_scope = curr_scope or create_scope(nil)
else
if has_g then
g_scope = create_scope(nil)
for _, p in ipairs(globals_arg.pairs) do
if type(p.key) == "string" then g_scope.vars[p.key] = p.value end
end
globals_arg.__scope_ref = g_scope.vars
else
local root = curr_scope
while root and root.parent do root = root.parent end
g_scope = root or create_scope(nil)
end
if not has_l or locals_arg == globals_arg then
exec_scope = g_scope
else
exec_scope = create_scope(g_scope)
local l_dict = (locals_arg.__type == "instance" and type(locals_arg.__builtin_value) == "table" and locals_arg.__builtin_value.__type == "dict") and locals_arg.__builtin_value or locals_arg
if l_dict.__type == "dict" then
for _, p in ipairs(l_dict.pairs) do
if type(p.key) == "string" then exec_scope.vars[p.key] = p.value end
end
l_dict.__scope_ref = exec_scope.vars
else
exec_scope.custom_ns = locals_arg
end
end
end
local ast = code_obj.__dict._ast
local file_idx = code_obj.__dict._file_idx or Runtime.Traceback.current_file_idx
local prev_file_idx = Runtime.Traceback.current_file_idx
local prev_func_name = Runtime.Traceback.current_func_name or "<module>"
local prev_line = Runtime.Traceback.current_line
local prev_scope = Runtime.Traceback.current_scope
local prev_depth = Runtime.Traceback.eval_depth
local prev_stack_len = #Runtime.Traceback.stack
table.insert(Runtime.Traceback.stack, { prev_file_idx, prev_func_name, prev_line })
Runtime.Traceback.current_file_idx = file_idx
Runtime.Traceback.current_func_name = "<module>"
Runtime.Traceback.current_line = 1
Runtime.Traceback.current_scope = exec_scope
local ok, res = pcall(function()
if ast.type == "Expression" then
return eval_expr(ast.body, exec_scope)
else
exec_block(ast.body, exec_scope)
return Runtime.builtins.None
end
end)
if has_g then sync_scope_to_dict(g_scope.vars, globals_arg) end
if has_l and locals_arg ~= globals_arg then sync_scope_to_dict(exec_scope.vars, locals_arg) end
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
Runtime.Traceback.current_line = prev_line
Runtime.Traceback.current_scope = prev_scope
Runtime.Traceback.eval_depth = prev_depth
if not ok then error(res, 0) end
if caller_kind == "exec" then return Runtime.builtins.None end
return res == nil and Runtime.builtins.None or res
end
-- 將核心回呼注入 Utils,完成無循環依賴的雙向綁定
Utils.bind_core({
eval_expr = eval_expr,
bind_and_exec_func = bind_and_exec_func,
invoke = Evaluator.invoke
})
-- 保持對外匯出介面 100% 相容
Evaluator.resolve_index = resolve_index
Evaluator.bigint_op = bigint_op
return Evaluator