跳转到内容

模組:PythonScript/Evaluator/Utils

维基百科,自由的百科全书
-- Module:PythonScript/Evaluator/Utils
-- Evaluator 內部輔助函式庫(作用域、比較器、大數/矩陣路由、迭代器、模式匹配、解包賦值與 f-string 引擎)
local Utils = {}

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

-- 確保 RecursionError 與 IndentationError 存在於 Runtime.builtins 中
if not Runtime.builtins.RecursionError then
    Runtime.builtins.RecursionError = PyClass.make_class("RecursionError", {Runtime.builtins.RuntimeError or Runtime.builtins.Exception}, {
        __str__ = { __type = "builtin_method", __call = function(args) return args[1].__dict.msg or "" end }
    })
end
if not Runtime.builtins.IndentationError then
    Runtime.builtins.IndentationError = PyClass.make_class("IndentationError", {Runtime.builtins.SyntaxError or Runtime.builtins.Exception}, {
        __str__ = { __type = "builtin_method", __call = function(args) return args[1].__dict.msg or "" end }
    })
end

-- 由 Module:PythonScript/Evaluator 於載入時注入核心回呼 (eval_expr, bind_and_exec_func, invoke)
local Core = {}
function Utils.bind_core(core_callbacks)
    Core = core_callbacks
end

-- ==========================================
-- 1. 控制流訊號與作用域管理
-- ==========================================
Utils.SIGNAL_BREAK = { type = "break" }
Utils.SIGNAL_CONTINUE = { type = "continue" }

function Utils.make_return(val)
    return { type = "return", value = val }
end

local function scan_goto_block(stmts, labels)
    if not stmts then return end
    for idx, stmt in ipairs(stmts) do
        if stmt.type == "Label" then
            if labels[stmt.target] then
                Runtime.Traceback.current_line = stmt.line or Runtime.Traceback.current_line
                Runtime.raise("SyntaxError", "duplicate label '" .. tostring(stmt.target) .. "'")
            end
            labels[stmt.target] = { block = stmts, pc = idx }
        elseif stmt.type == "If" or stmt.type == "While" or stmt.type == "For" then
            scan_goto_block(stmt.body, labels)
            scan_goto_block(stmt.orelse, labels)
        elseif stmt.type == "Try" then
            scan_goto_block(stmt.body, labels)
            for _, h in ipairs(stmt.handlers or {}) do scan_goto_block(h.body, labels) end
            scan_goto_block(stmt.orelse, labels)
            scan_goto_block(stmt.finalbody, labels)
        elseif stmt.type == "With" then
            scan_goto_block(stmt.body, labels)
        elseif stmt.type == "Match" then
            for _, c in ipairs(stmt.cases or {}) do scan_goto_block(c.body, labels) end
        end
        -- 注意:刻意不進入 FunctionDef 與 ClassDef,確保 label 嚴格限制在當前函數 Scope
    end
end

function Utils.build_goto_labels(stmts)
    local labels = {}
    scan_goto_block(stmts, labels)
    return labels
end

function Utils.create_scope(parent)
    return { vars = {}, parent = parent, globals = {}, nonlocals = {} }
end

function Utils.lookup_var(name, scope)
    local curr = scope
    local root = scope
    while curr do
        if curr.vars[name] ~= nil then return curr.vars[name] end
        if curr.custom_ns and type(curr.custom_ns) == "table" and curr.custom_ns.__type == "instance" then
            local prev_d = Runtime.Traceback.eval_depth
            local ok_gi, gi_m = pcall(PyClass.get_attr, curr.custom_ns, "__getitem__")
            Runtime.Traceback.eval_depth = prev_d
            if ok_gi and gi_m then
                local ok_v, val = pcall(Core.invoke, gi_m, { name }, {})
                Runtime.Traceback.eval_depth = prev_d
                if ok_v and val ~= nil then return val end
            end
        end
        
        -- 支援 from xxx import * 的延遲查找機制
        if curr.star_imports then
            for i = #curr.star_imports, 1, -1 do 
                local mod = curr.star_imports[i]
                local allowed = true
                
                if mod.__dict and type(mod.__dict["__all__"]) == "table" and mod.__dict["__all__"].elements then
                    allowed = false
                    for _, allowed_name in ipairs(mod.__dict["__all__"].elements) do
                        if allowed_name == name then allowed = true; break end
                    end
                elseif string.sub(name, 1, 1) == "_" then
                    allowed = false
                end
                
                if allowed then
                    local ok, val = pcall(PyClass.get_attr, mod, name)
                    if ok and val ~= nil then
                        curr.vars[name] = val 
                        return val
                    end
                end
            end
        end
        
        if not curr.parent then root = curr end
        curr = curr.parent
    end

    -- 檢查頂層作用域是否覆寫或禁用了 __builtins__ (例如 eval(expr, {"__builtins__": None}, safe_dict))
    local custom_builtins = root and root.vars and root.vars["__builtins__"]
    if custom_builtins ~= nil then
        if custom_builtins == Runtime.builtins.None or (type(custom_builtins) == "table" and custom_builtins.__class and custom_builtins.__class.__name == "NoneType") then
            Runtime.raise("NameError", "name '" .. name .. "' is not defined")
        elseif type(custom_builtins) == "table" then
            local b_dict = custom_builtins
            if b_dict.__type == "instance" and type(b_dict.__builtin_value) == "table" and b_dict.__builtin_value.__type == "dict" then
                b_dict = b_dict.__builtin_value
            end
            if b_dict.__type == "dict" then
                for _, p in ipairs(b_dict.pairs or {}) do
                    if p.key == name then return p.value end
                end
                Runtime.raise("NameError", "name '" .. name .. "' is not defined")
            elseif custom_builtins.__type == "instance" or custom_builtins.__type == "module" then
                local prev_d = Runtime.Traceback.eval_depth
                local ok_b, val = pcall(PyClass.get_attr, custom_builtins, name)
                Runtime.Traceback.eval_depth = prev_d
                if ok_b and val ~= nil then return val end
                Runtime.raise("NameError", "name '" .. name .. "' is not defined")
            end
        end
        Runtime.raise("NameError", "name '" .. name .. "' is not defined")
    end

    if Runtime.builtins[name] ~= nil then return Runtime.builtins[name] end
    Runtime.raise("NameError", "name '" .. name .. "' is not defined")
end

function Utils.set_var(name, value, scope)
    if scope.globals[name] then
        local root = scope
        while root.parent do root = root.parent end
        root.vars[name] = value
        return
    end
    if scope.nonlocals[name] then
        local curr = scope.parent
        while curr do
            if curr.vars[name] ~= nil then curr.vars[name] = value; return end
            curr = curr.parent
        end
        Runtime.raise("SyntaxError", "no binding for nonlocal '" .. name .. "' found")
    end
    scope.vars[name] = value
    if scope.custom_ns and type(scope.custom_ns) == "table" then
        local ns = scope.custom_ns
        if ns.__type == "dict" then
            local found = false
            for _, p in ipairs(ns.pairs) do
                if p.key == name then p.value = value; found = true; break end
            end
            if not found then table.insert(ns.pairs, { key = name, value = value }) end
        elseif ns.__type == "instance" then
            local prev_d = Runtime.Traceback.eval_depth
            local ok_si, si_m = pcall(PyClass.get_attr, ns, "__setitem__")
            Runtime.Traceback.eval_depth = prev_d
            if ok_si and si_m then
                Core.invoke(si_m, { name, value }, {})
            end
        end
    end
end

function Utils.del_var(name, scope)
    if scope.globals[name] then
        local root = scope
        while root.parent do root = root.parent end
        if root.vars[name] == nil then Runtime.raise("NameError", "name '" .. name .. "' is not defined") end
        root.vars[name] = nil
        return
    end
    if scope.nonlocals[name] then
        local curr = scope.parent
        while curr do
            if curr.vars[name] ~= nil then curr.vars[name] = nil; return end
            curr = curr.parent
        end
        Runtime.raise("SyntaxError", "no binding for nonlocal '" .. name .. "' found")
    end
    if scope.vars[name] == nil then
        Runtime.raise("NameError", "name '" .. name .. "' is not defined")
    end
    scope.vars[name] = nil
    if scope.custom_ns and type(scope.custom_ns) == "table" then
        local ns = scope.custom_ns
        if ns.__type == "dict" then
            for i, p in ipairs(ns.pairs) do
                if p.key == name then table.remove(ns.pairs, i); break end
            end
        elseif ns.__type == "instance" then
            local prev_d = Runtime.Traceback.eval_depth
            local ok_di, di_m = pcall(PyClass.get_attr, ns, "__delitem__")
            Runtime.Traceback.eval_depth = prev_d
            if ok_di and di_m then pcall(Core.invoke, di_m, { name }, {}) end
        end
    end
end

function Utils.is_truthy(val)
    return Runtime.builtins.bool.__call({val})
end

-- ==========================================
-- 2. 索引與切片正規化
-- ==========================================
local function unwrap_int_index(idx)
    if type(idx) == "table" and idx.__type == "instance" and idx.__builtin_value ~= nil then
        idx = idx.__builtin_value
    end
    if type(idx) == "boolean" then return idx and 1 or 0 end
    if type(idx) == "table" and idx.__type == "int" then
        local MathCore = require("Module:PythonScript/Runtime/Cmath")
        return idx.value or tonumber(MathCore.BigInt.to_string(idx.sign, idx.limbs))
    end
    return idx
end
Utils.unwrap_int_index = unwrap_int_index

local function resolve_index(obj, idx)
    idx = unwrap_int_index(idx)
    if type(idx) ~= "number" or math.floor(idx) ~= idx then Runtime.raise("TypeError", "indices must be integers") end
    local length = 0
    if type(obj) == "table" and obj.elements then length = #(obj.elements)
    elseif type(obj) == "table" and obj._data then length = #(obj._data)
    elseif type(obj) == "string" then length = mw.ustring.len(obj) end
    if idx < 0 then idx = length + idx end
    if idx < 0 or idx >= length then Runtime.raise("IndexError", "index out of range") end
    return idx + 1
end
Utils.resolve_index = resolve_index

local function resolve_slice(length, lower, upper, step)
    lower = lower ~= nil and unwrap_int_index(lower) or nil
    upper = upper ~= nil and unwrap_int_index(upper) or nil
    step = step ~= nil and unwrap_int_index(step) or nil
    if type(lower) == "table" and lower.__class and lower.__class.__name == "NoneType" then lower = nil end
    if type(upper) == "table" and upper.__class and upper.__class.__name == "NoneType" then upper = nil end
    if type(step) == "table" and step.__class and step.__class.__name == "NoneType" then step = nil end

    local s_step = step or 1
    if s_step == 0 then Runtime.raise("ValueError", "slice step cannot be zero") end
    local s_start = lower
    if s_start == nil then
        s_start = (s_step > 0) and 0 or (length - 1)
    else
        if s_start < 0 then s_start = s_start + length end
        if s_step > 0 then
            if s_start < 0 then s_start = 0 end
            if s_start > length then s_start = length end
        else
            if s_start < -1 then s_start = -1 end
            if s_start >= length then s_start = length - 1 end
        end
    end
    local s_stop = upper
    if s_stop == nil then
        s_stop = (s_step > 0) and length or -1
    else
        if s_stop < 0 then s_stop = s_stop + length end
        if s_step > 0 then
            if s_stop < 0 then s_stop = 0 end
            if s_stop > length then s_stop = length end
        else
            if s_stop < -1 then s_stop = -1 end
            if s_stop >= length then s_stop = length - 1 end
        end
    end
    return s_start, s_stop, s_step
end
Utils.resolve_slice = resolve_slice

-- ==========================================
-- 3. 相等與大小比較引擎 (含循環參照防護)
-- ==========================================
Utils.COMPARE_OPS = {
    ["=="] = true, ["!="] = true, ["<"] = true, [">"] = true,
    ["<="] = true, [">="] = true, ["is"] = true, ["is not"] = true,
    ["in"] = true, ["not in"] = true
}

local py_cmp, py_eq

py_cmp = function(a, b)
    if type(a) == "boolean" then a = a and 1 or 0 end
    if type(b) == "boolean" then b = b and 1 or 0 end

    local is_big_a = type(a) == "table" and a.__type == "int"
    local is_big_b = type(b) == "table" and b.__type == "int"
    if is_big_a or is_big_b then
        local is_float_a = type(a) == "number" and math.floor(a) ~= a
        local is_float_b = type(b) == "number" and math.floor(b) ~= b
        if is_float_a or is_float_b then
            local MathCore = require("Module:PythonScript/Runtime/Cmath")
            local num_a = is_big_a and tonumber(MathCore.BigInt.to_string(a.sign, a.limbs)) or a
            local num_b = is_big_b and tonumber(MathCore.BigInt.to_string(b.sign, b.limbs)) or b
            if num_a < num_b then return -1 elseif num_a > num_b then return 1 else return 0 end
        end

        if (type(a) == "number" or is_big_a) and (type(b) == "number" or is_big_b) then
            local MathCore = require("Module:PythonScript/Runtime/Cmath")
            local sign_a = is_big_a and a.sign or (a < 0 and -1 or 1)
            local sign_b = is_big_b and b.sign or (b < 0 and -1 or 1)
            if a == 0 then sign_a = 1 end; if b == 0 then sign_b = 1 end
            if sign_a ~= sign_b then return sign_a < sign_b and -1 or 1 end
            local c = MathCore.BigInt.cmp_limbs(MathCore.BigInt.to_limbs(a), MathCore.BigInt.to_limbs(b))
            return sign_a == 1 and c or -c
        end
    end
    if type(a) == "table" and type(b) == "table" and a.__type == b.__type and (a.__type == "list" or a.__type == "tuple") then
        local len_a, len_b = #a.elements, #b.elements
        for i = 1, math.min(len_a, len_b) do
            if not py_eq(a.elements[i], b.elements[i]) then
                return py_cmp(a.elements[i], b.elements[i])
            end
        end
        if len_a < len_b then return -1 elseif len_a > len_b then return 1 else return 0 end
    end
    if type(a) == "number" and type(b) == "number" then
        if a < b then return -1 elseif a > b then return 1 else return 0 end
    end
    if type(a) == "string" and type(b) == "string" then
        if a < b then return -1 elseif a > b then return 1 else return 0 end
    end
    if type(a) == "table" and type(b) == "table" then
        local is_b1 = a.__type == "bytes" or a.__type == "bytearray"
        local is_b2 = b.__type == "bytes" or b.__type == "bytearray"
        if is_b1 and is_b2 then
            if a._data < b._data then return -1 elseif a._data > b._data then return 1 else return 0 end
        end
    end
    -- 支援 instance 與任意型別 (含原生 number) 的比較魔術方法
    if type(a) == "table" and a.__type == "instance" then
        local prev_depth = Runtime.Traceback.eval_depth
        local success, lt = pcall(PyClass.get_attr, a, "__lt__")
        Runtime.Traceback.eval_depth = prev_depth
        if success and lt and lt.__type == "bound_method" then
            local res = Core.bind_and_exec_func(lt.__func, {b}, {}, lt.__self)
            if res ~= nil and res ~= Runtime.builtins.None and res ~= Runtime.builtins.NotImplemented then
                if Utils.is_truthy(res) then return -1 end
                if py_eq(a, b) then return 0 end
                return 1
            end
        end
    end
    if type(b) == "table" and b.__type == "instance" then
        local prev_depth = Runtime.Traceback.eval_depth
        local success, gt = pcall(PyClass.get_attr, b, "__gt__")
        Runtime.Traceback.eval_depth = prev_depth
        if success and gt and gt.__type == "bound_method" then
            local res = Core.bind_and_exec_func(gt.__func, {a}, {}, gt.__self)
            if res ~= nil and res ~= Runtime.builtins.None and res ~= Runtime.builtins.NotImplemented then
                if Utils.is_truthy(res) then return -1 end
                if py_eq(a, b) then return 0 end
                return 1
            end
        end
    end
    local has_bv_a = type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil
    local has_bv_b = type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil
    if has_bv_a or has_bv_b then
        return py_cmp(has_bv_a and a.__builtin_value or a, has_bv_b and b.__builtin_value or b)
    end
    Runtime.raise("TypeError", "unorderable types")
end
Utils.py_cmp = py_cmp

py_eq = function(a, b, seen)
    if a == b then return true end

    if type(a) == "boolean" and type(b) == "number" then return (a and 1 or 0) == b end
    if type(a) == "number" and type(b) == "boolean" then return a == (b and 1 or 0) end

    if type(a) ~= type(b) then
        -- 若其中一方為自定義實例 (如 Fraction(2, 1) == 2),先嘗試觸發 __eq__
        if type(a) == "table" and a.__type == "instance" then
            local prev_depth = Runtime.Traceback.eval_depth
            local success, eq_method = pcall(PyClass.get_attr, a, "__eq__")
            Runtime.Traceback.eval_depth = prev_depth
            if success and eq_method and eq_method.__type == "bound_method" then
                local res = Core.bind_and_exec_func(eq_method.__func, {b}, {}, eq_method.__self)
                if res ~= nil and res ~= Runtime.builtins.None and res ~= Runtime.builtins.NotImplemented then return Utils.is_truthy(res) end
            end
        end
        if type(b) == "table" and b.__type == "instance" then
            local prev_depth = Runtime.Traceback.eval_depth
            local success, eq_method = pcall(PyClass.get_attr, b, "__eq__")
            Runtime.Traceback.eval_depth = prev_depth
            if success and eq_method and eq_method.__type == "bound_method" then
                local res = Core.bind_and_exec_func(eq_method.__func, {a}, {}, eq_method.__self)
                if res ~= nil and res ~= Runtime.builtins.None and res ~= Runtime.builtins.NotImplemented then return Utils.is_truthy(res) end
            end
        end
		if (type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil) or (type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil) then
            local ua = (type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil) and a.__builtin_value or a
            local ub = (type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil) and b.__builtin_value or b
            return py_eq(ua, ub, seen)
        end
        local is_big_a = type(a) == "table" and a.__type == "int"
        local is_big_b = type(b) == "table" and b.__type == "int"
        if is_big_a or is_big_b then
            local na = type(a) == "boolean" and (a and 1 or 0) or a
            local nb = type(b) == "boolean" and (b and 1 or 0) or b
            if (type(na) == "number" or is_big_a) and (type(nb) == "number" or is_big_b) then
                return py_cmp(na, nb) == 0
            end
        end
        if (type(a) == "table" and a.__type == "complex") or (type(b) == "table" and b.__type == "complex") then
            return require("Module:PythonScript/Runtime/Cmath").Cmath.eq(a, b)
        end
        return false
    end

    if type(a) == "table" then
        local is_b1 = a.__type == "bytes" or a.__type == "bytearray" or a.__type == "memoryview"
        local is_b2 = b.__type == "bytes" or b.__type == "bytearray" or b.__type == "memoryview"
        if is_b1 or is_b2 then
            if a.__type == "memoryview" and a._released then return false end
            if b.__type == "memoryview" and b._released then return false end
            return (is_b1 and is_b2) and (a._data == b._data) or false
        end

        if a.__type == "int" and b.__type == "int" then
            return py_cmp(a, b) == 0
        end

        if a.__type == "complex" and b.__type == "complex" then
            return require("Module:PythonScript/Runtime/Cmath").Cmath.eq(a, b)
        end

        seen = seen or {}
        if seen[a] and seen[a][b] then return true end
        seen[a] = seen[a] or {}
        seen[a][b] = true

        if (a.__type == "list" and b.__type == "list") or (a.__type == "tuple" and b.__type == "tuple") then
            if #a.elements ~= #b.elements then return false end
            for i = 1, #a.elements do
                if not py_eq(a.elements[i], b.elements[i], seen) then return false end
            end
            return true
        end

        local is_s1 = a.__type == "set" or a.__type == "frozenset"
        local is_s2 = b.__type == "set" or b.__type == "frozenset"
        if is_s1 and is_s2 then
            if #a.elements ~= #b.elements then return false end
            for _, ea in ipairs(a.elements) do
                local found = false
                for _, eb in ipairs(b.elements) do
                    if py_eq(ea, eb, seen) then found = true; break end
                end
                if not found then return false end
            end
            return true
        end

        if a.__type == "dict" and b.__type == "dict" then
            if #a.pairs ~= #b.pairs then return false end
            for _, pa in ipairs(a.pairs) do
                local found = false
                for _, pb in ipairs(b.pairs) do
                    if py_eq(pa.key, pb.key, seen) and py_eq(pa.value, pb.value, seen) then
                        found = true; break
                    end
                end
                if not found then return false end
            end
            return true
        end

        if a.__type == "instance" then
            local prev_depth = Runtime.Traceback.eval_depth
            local success, eq_method = pcall(PyClass.get_attr, a, "__eq__")
            Runtime.Traceback.eval_depth = prev_depth
            if success and eq_method and eq_method.__type == "bound_method" then
                local res = Core.bind_and_exec_func(eq_method.__func, {b}, {}, eq_method.__self)
                if res ~= Runtime.builtins.NotImplemented then return Utils.is_truthy(res) end
            end
        end
        if b.__type == "instance" then
            local prev_depth = Runtime.Traceback.eval_depth
            local success, eq_method = pcall(PyClass.get_attr, b, "__eq__")
            Runtime.Traceback.eval_depth = prev_depth
            if success and eq_method and eq_method.__type == "bound_method" then
                local res = Core.bind_and_exec_func(eq_method.__func, {a}, {}, eq_method.__self)
                if res ~= Runtime.builtins.NotImplemented then return Utils.is_truthy(res) end
            end
        end
        if (a.__type == "instance" and a.__builtin_value ~= nil) or (b.__type == "instance" and b.__builtin_value ~= nil) then
            return py_eq(a.__builtin_value ~= nil and a.__builtin_value or a, b.__builtin_value ~= nil and b.__builtin_value or b, seen)
        end

        return a == b
    end

    return a == b
end
Utils.py_eq = py_eq

function Utils.py_is(a, b)
    return a == b
end

-- ==========================================
-- 4. 矩陣與大數引擎路由器
-- ==========================================
local function bigint_op(a, b, op)
    if type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil then a = a.__builtin_value end
    if type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil then b = b.__builtin_value end
    local is_float_a = type(a) == "number" and (math.floor(a) ~= a or math.abs(a) > 9007199254740990)
    local is_float_b = type(b) == "number" and (math.floor(b) ~= b or math.abs(b) > 9007199254740990)
    
    if is_float_a or is_float_b then
        local MathCore = require("Module:PythonScript/Runtime/Cmath")
        local num_a = (type(a) == "table" and a.__type == "int") and tonumber(MathCore.BigInt.to_string(a.sign, a.limbs)) or a
        local num_b = (type(b) == "table" and b.__type == "int") and tonumber(MathCore.BigInt.to_string(b.sign, b.limbs)) or b
        if op == "+" then return num_a + num_b
        elseif op == "-" then return num_a - num_b
        elseif op == "*" then return num_a * num_b
        elseif op == "/" then return num_a / num_b
        elseif op == "//" then return math.floor(num_a / num_b)
        elseif op == "%" then return num_a % num_b
        elseif op == "**" then return num_a ^ num_b
        end
    end

    local MathCore = require("Module:PythonScript/Runtime/Cmath")
    local BigInt = MathCore.BigInt
    local limbs_a, limbs_b = BigInt.to_limbs(a), BigInt.to_limbs(b)
    local sign_a = (type(a) == "table" and a.sign) or (a < 0 and -1 or 1)
    local sign_b = (type(b) == "table" and b.sign) or (b < 0 and -1 or 1)
    if a == 0 then sign_a = 1 end
    if b == 0 then sign_b = 1 end
    
    local res_sign, res_limbs
    if op == "+" then
        if sign_a == sign_b then res_sign = sign_a; res_limbs = BigInt.add_limbs(limbs_a, limbs_b)
        else
            local cmp = BigInt.cmp_limbs(limbs_a, limbs_b)
            if cmp == 0 then return 0 end
            if cmp > 0 then res_sign = sign_a; res_limbs = BigInt.sub_limbs(limbs_a, limbs_b)
            else res_sign = sign_b; res_limbs = BigInt.sub_limbs(limbs_b, limbs_a) end
        end
    elseif op == "-" then
        if sign_a ~= sign_b then res_sign = sign_a; res_limbs = BigInt.add_limbs(limbs_a, limbs_b)
        else
            local cmp = BigInt.cmp_limbs(limbs_a, limbs_b)
            if cmp == 0 then return 0 end
            if cmp > 0 then res_sign = sign_a; res_limbs = BigInt.sub_limbs(limbs_a, limbs_b)
            else res_sign = -sign_a; res_limbs = BigInt.sub_limbs(limbs_b, limbs_a) end
        end
    elseif op == "*" then
        res_sign = sign_a * sign_b; res_limbs = BigInt.mul_limbs(limbs_a, limbs_b)
    elseif op == "//" or op == "%" then
        if #limbs_b == 1 and limbs_b[1] == 0 then Runtime.raise("ZeroDivisionError", "integer division or modulo by zero") end
        local q, r = BigInt.divmod_limbs(limbs_a, limbs_b)
        local r_is_zero = (#r == 1 and r[1] == 0)
        local q_sign = sign_a * sign_b; local r_sign = sign_a
        if not r_is_zero and sign_a ~= sign_b then
            q = BigInt.add_limbs(q, {1}); r = BigInt.sub_limbs(limbs_b, r); r_sign = sign_b
        end
        if op == "//" then res_sign = q_sign; res_limbs = q else res_sign = r_sign; res_limbs = r end
    elseif op == "**" then
        local exp = tonumber(BigInt.to_string(sign_b, limbs_b))
        if exp < 0 then return tonumber(BigInt.to_string(sign_a, limbs_a)) ^ exp end
        if exp == 0 then return 1 end
        local base_limbs = limbs_a
        res_limbs = {1}; res_sign = (sign_a == -1 and exp % 2 == 1) and -1 or 1
        while exp > 0 do
            if exp % 2 == 1 then res_limbs = BigInt.mul_limbs(res_limbs, base_limbs) end
            if exp > 1 then base_limbs = BigInt.mul_limbs(base_limbs, base_limbs) end
            exp = math.floor(exp / 2)
        end
    elseif op == "/" then
        if #limbs_b == 1 and limbs_b[1] == 0 then Runtime.raise("ZeroDivisionError", "division by zero") end
        return tonumber(BigInt.to_string(sign_a, limbs_a)) / tonumber(BigInt.to_string(sign_b, limbs_b))
    elseif op == "<<" or op == ">>" or op == "&" or op == "|" or op == "^" then
        Runtime.raise("NotImplementedError", "bitwise operations on BigInt are not fully supported in this environment")
    end
    
    if #res_limbs == 1 and res_limbs[1] == 0 then return 0 end
    local num_str = BigInt.to_string(res_sign, res_limbs)
    local num = tonumber(num_str)
    if num and math.abs(num) <= BigInt.SAFE_LIMIT then return num end
    return { __type = "int", is_big = true, value = num, sign = res_sign, limbs = res_limbs }
end
Utils.bigint_op = bigint_op

local function get_matrix_engine()
    return require("Module:PythonScript/Library").Matrix
end
Utils.get_matrix_engine = get_matrix_engine

function Utils.elementwise_op(a, b, op)
    return get_matrix_engine().elementwise_op(a, b, op)
end

function Utils.matmul(A, B)
    return get_matrix_engine().matmul(A, B)
end

local UnionTypeClass = nil

local function is_single_unionable(x)
    if type(x) ~= "table" then return false end
    if x.__type == "class" then return true end
    if x.__class and x.__class.__name == "NoneType" then return true end
    if x.__dict and (x.__dict._is_union or x.__dict._is_typing) then return true end
    return false
end

function Utils.is_unionable_type(a, b)
    return is_single_unionable(a) and is_single_unionable(b)
end

local function format_type_item(t)
    if type(t) == "table" then
        if t == Runtime.builtins.NoneType or (t.__class and t.__class.__name == "NoneType") then
            return "None"
        elseif t.__type == "class" then
            local mod = t.__dict and t.__dict["__module__"]
            if mod and mod ~= "builtins" and mod ~= "__main__" then
                return mod .. "." .. t.__name
            end
            return t.__name
        end
    end
    return Runtime.builtins.repr({t})
end

function Utils.make_union_type(items)
    if not UnionTypeClass then
        local u_dict = {
            __repr__ = { __type = "builtin_method", __call = function(a)
                local args_tup = a[1].__dict.__args__
                local parts = {}
                for i, t in ipairs(args_tup.elements) do
                    parts[i] = format_type_item(t)
                end
                return table.concat(parts, " | ")
            end },
            __or__ = { __type = "builtin_method", __call = function(a)
                if not is_single_unionable(a[2]) then return Runtime.builtins.NotImplemented end
                return Utils.make_union_type({ a[1], a[2] })
            end },
            __ror__ = { __type = "builtin_method", __call = function(a)
                if not is_single_unionable(a[2]) then return Runtime.builtins.NotImplemented end
                return Utils.make_union_type({ a[2], a[1] })
            end },
            __eq__ = { __type = "builtin_method", __call = function(a)
                local self, other = a[1], a[2]
                if type(other) ~= "table" or not other.__dict or not other.__dict._is_union then
                    return false
                end
                local e1 = self.__dict.__args__.elements
                local e2 = other.__dict.__args__.elements
                if #e1 ~= #e2 then return false end
                for _, x in ipairs(e1) do
                    local found = false
                    for _, y in ipairs(e2) do
                        if py_eq(x, y) then found = true; break end
                    end
                    if not found then return false end
                end
                return true
            end },
            __hash__ = { __type = "builtin_method", __call = function(a)
                return Runtime.builtins.hash({ a[1].__dict.__args__ })
            end }
        }
        u_dict.__str__ = u_dict.__repr__
        UnionTypeClass = PyClass.make_class("UnionType", {}, u_dict)
        UnionTypeClass.__dict["__module__"] = "types"
    end

    local flat = {}
    local function add_item(item)
        if type(item) == "table" and item.__class and item.__class.__name == "NoneType" then
            item = Runtime.builtins.NoneType
        end
        if type(item) == "table" and item.__dict and item.__dict._is_union and item.__dict.__args__ then
            for _, sub in ipairs(item.__dict.__args__.elements) do add_item(sub) end
            return
        end
        for _, existing in ipairs(flat) do
            if py_eq(existing, item) then return end
        end
        table.insert(flat, item)
    end

    for _, it in ipairs(items) do add_item(it) end
    if #flat == 1 then return flat[1] end

    local inst = PyClass.make_instance(UnionTypeClass)
    inst.__dict._is_union = true
    inst.__dict._is_typing = true
    inst.__dict.__args__ = { __type = "tuple", elements = flat }
    inst.__dict.__parameters__ = { __type = "tuple", elements = {} }
    return inst
end

-- ==========================================
-- 5. f-string 解析與格式化引擎
-- ==========================================
local function format_fstring_value(val, conv, fmt_spec)
    if conv == "r" then val = Runtime.builtins.repr({val})
    elseif conv == "s" then val = Runtime.builtins.str.__call({val})
    elseif conv == "a" then val = Runtime.builtins.ascii({val}) end

    return Runtime.builtins.format({val, fmt_spec or ""})
end

local function split_fstring_field(field_str)
    local b_level = 0
    local in_str = false
    local quote = nil
    local conv_pos, colon_pos = nil, nil
    local n = #field_str
    local i = 1
    while i <= n do
        local c = field_str:sub(i, i)
        if not in_str then
            if c == "(" or c == "[" or c == "{" then b_level = b_level + 1
            elseif c == ")" or c == "]" or c == "}" then b_level = math.max(0, b_level - 1)
            elseif c == "'" or c == '"' then in_str = true; quote = c
            elseif b_level == 0 then
                if c == "!" and field_str:sub(i+1, i+1) ~= "=" and not colon_pos then
                    local next_c = field_str:sub(i+1, i+1)
                    if next_c == "r" or next_c == "s" or next_c == "a" then
                        conv_pos = i
                    end
                elseif c == ":" and not colon_pos then
                    colon_pos = i
                    break
                end
            end
        else
            if c == "\\" then i = i + 1
            elseif c == quote then in_str = false end
        end
        i = i + 1
    end

    local expr_part = field_str
    local conv = nil
    local fmt_spec = nil
    if colon_pos then
        fmt_spec = field_str:sub(colon_pos + 1)
        expr_part = field_str:sub(1, colon_pos - 1)
    end
    if conv_pos and (not colon_pos or conv_pos < colon_pos) then
        conv = expr_part:sub(conv_pos + 1)
        expr_part = expr_part:sub(1, conv_pos - 1)
    end
    return expr_part, conv, fmt_spec
end

function Utils.eval_fstring(content, scope)
    local current_f_line = Runtime.Traceback.current_line
    local prev_depth = Runtime.Traceback.eval_depth
    local success_f, final_content = pcall(function()
        local res_parts = {}
        local i = 1
        local n = #content
        while i <= n do
            local c = content:sub(i, i)
            if c == "{" then
                if i < n and content:sub(i+1, i+1) == "{" then
                    table.insert(res_parts, "{")
                    i = i + 2
                else
                    local expr_start = i + 1
                    local bracket_level = 1
                    local j = expr_start
                    local in_string = false
                    local str_quote = nil
                    
                    while j <= n do
                        local char = content:sub(j, j)
                        if not in_string then
                            if char == "{" then bracket_level = bracket_level + 1
                            elseif char == "}" then
                                bracket_level = bracket_level - 1
                                if bracket_level == 0 then break end
                            elseif char == "'" or char == '"' then
                                in_string = true; str_quote = char
                            end
                        else
                            if char == "\\" then j = j + 1
                            elseif char == str_quote then in_string = false end
                        end
                        j = j + 1
                    end
                    
                    if bracket_level > 0 then Runtime.raise("SyntaxError", "f-string: unclosed '{'") end
                    local raw_field = content:sub(expr_start, j - 1)
                    
                    if raw_field == "" then
                        table.insert(res_parts, "{}")
                    else
                        local expr_str, conv, fmt_spec = split_fstring_field(raw_field)
                        if fmt_spec and fmt_spec:find("{", 1, true) then
                            fmt_spec = Utils.eval_fstring(fmt_spec, scope)
                        end
                        local Lexer = require("Module:PythonScript/Lexer")
                        local Parser = require("Module:PythonScript/Parser")
                        local d_save = Runtime.Traceback.eval_depth
                        local clean_expr = expr_str:match("^%s*(.-)%s*$")
                        local success, inner_tokens = pcall(Lexer.tokenize, clean_expr)
                        Runtime.Traceback.eval_depth = d_save
                        if success then
                            for _, t in ipairs(inner_tokens) do t.line = current_f_line end
                            local success2, inner_ast = pcall(Parser.parse, inner_tokens)
                            Runtime.Traceback.eval_depth = d_save
                            if success2 and inner_ast.body[1] and inner_ast.body[1].type == "ExprStatement" then
                                local evaluated_val = Core.eval_expr(inner_ast.body[1].expr, scope)
                                table.insert(res_parts, format_fstring_value(evaluated_val, conv, fmt_spec))
                            else
                                table.insert(res_parts, "{" .. raw_field .. "}")
                            end
                        else
                            table.insert(res_parts, "{" .. raw_field .. "}")
                        end
                    end
                    i = j + 1
                end
            elseif c == "}" then
                if i < n and content:sub(i+1, i+1) == "}" then
                    table.insert(res_parts, "}")
                    i = i + 2
                else
                    Runtime.raise("SyntaxError", "f-string: single '}' is not allowed")
                end
            else
                table.insert(res_parts, c)
                i = i + 1
            end
        end
        return table.concat(res_parts)
    end)
    Runtime.Traceback.eval_depth = prev_depth
    if not success_f then error(final_content, 0) end
    return final_content
end

-- ==========================================
-- 6. 模式匹配、迭代器狀態與解包賦值
-- ==========================================
local match_pattern, make_iter_state, get_next_item, eval_comprehension, assign_target

match_pattern = function(pat, val, scope, exec_scope)
    if pat.type == "Identifier" then
        if pat.name == "_" then return true end
        scope.vars[pat.name] = val; return true
    elseif pat.type == "MatchAs" then
        if match_pattern(pat.pattern, val, scope, exec_scope) then scope.vars[pat.alias] = val; return true end
        return false
    elseif pat.type == "Number" or pat.type == "String" or pat.type == "Boolean" or pat.type == "None" or pat.type == "UnaryOp" then
        return py_eq(Core.eval_expr(pat, exec_scope), val)
    elseif pat.type == "Attribute" then
        return py_eq(Core.eval_expr(pat, exec_scope), val)
    elseif pat.type == "Tuple" or pat.type == "List" then
        if type(val) == "table" and val.__type == "instance" and val.__builtin_value ~= nil then 
            val = val.__builtin_value 
        end
        
        if type(val) ~= "table" or (val.__type ~= "list" and val.__type ~= "tuple") then return false end
        local starred_idx = nil
        for i, p_elem in ipairs(pat.elements) do
            if p_elem.type == "Starred" then starred_idx = i; break end
        end
        if starred_idx then
            local min_len = #pat.elements - 1
            if #val.elements < min_len then return false end
            local star_len = #val.elements - min_len
            local cur = 1
            for i, p_elem in ipairs(pat.elements) do
                if i == starred_idx then
                    local star_elts = {}
                    for j = 1, star_len do table.insert(star_elts, val.elements[cur]); cur = cur + 1 end
                    if p_elem.value.type == "Identifier" and p_elem.value.name ~= "_" then
                        scope.vars[p_elem.value.name] = { __type = "list", elements = star_elts }
                    end
                else
                    if not match_pattern(p_elem, val.elements[cur], scope, exec_scope) then return false end
                    cur = cur + 1
                end
            end
            return true
        else
            if #pat.elements ~= #val.elements then return false end
            for i, p_elem in ipairs(pat.elements) do
                if not match_pattern(p_elem, val.elements[i], scope, exec_scope) then return false end
            end
            return true
        end
    elseif pat.type == "Dict" then
        local val_pairs = nil
        if type(val) == "table" then
            if val.__type == "dict" then
                val_pairs = val.pairs
            elseif val.__type == "instance" then
                if type(val.__builtin_value) == "table" and val.__builtin_value.__type == "dict" then
                    val_pairs = val.__builtin_value.pairs
                elseif val.__dict and type(val.__dict.data) == "table" and val.__dict.data.__type == "dict" then
                    val_pairs = val.__dict.data.pairs
                end
            end
        end
        if not val_pairs then return false end

        local matched_indices = {}
        local rest_var = nil
        for _, p_pair in ipairs(pat.pairs) do
            if p_pair.key == nil then
                rest_var = p_pair.value.name
            else
                local k_val = Core.eval_expr(p_pair.key, exec_scope)
                local found_idx, found_v = nil, nil
                for v_i, v_pair in ipairs(val_pairs) do
                    if py_eq(v_pair.key, k_val) then
                        found_idx = v_i
                        found_v = v_pair.value
                        break
                    end
                end
                if found_idx == nil or not match_pattern(p_pair.value, found_v, scope, exec_scope) then
                    return false
                end
                matched_indices[found_idx] = true
            end
        end

        if rest_var then
            local rest_pairs = {}
            for v_i, v_pair in ipairs(val_pairs) do
                if not matched_indices[v_i] then
                    table.insert(rest_pairs, { key = v_pair.key, value = v_pair.value })
                end
            end
            scope.vars[rest_var] = { __type = "dict", pairs = rest_pairs }
        end
        return true
    elseif pat.type == "BinaryOp" and pat.op == "|" then
        local snap = {}; for k, v in pairs(scope.vars) do snap[k] = v end
        if match_pattern(pat.left, val, scope, exec_scope) then return true end
        scope.vars = {}; for k, v in pairs(snap) do scope.vars[k] = v end
        if match_pattern(pat.right, val, scope, exec_scope) then return true end
        return false
    elseif pat.type == "Call" then
        local class_obj = Core.eval_expr(pat.callee, exec_scope)
        if type(class_obj) ~= "table" or class_obj.__type ~= "class" then
            Runtime.raise("TypeError", "called match pattern must be a type")
        end
        if not Runtime.builtins.isinstance({val, class_obj}) then return false end

        local pos_args = pat.args or {}
        local seen_attrs = {}

        if #pos_args > 0 then
            local B = Runtime.builtins
            local SELF_MATCH_BUILTINS = {
                [B.bool] = true, [B.bytearray] = true, [B.bytes] = true, [B.dict] = true,
                [B.float] = true, [B.frozenset] = true, [B.int] = true, [B.list] = true,
                [B.set] = true, [B.str] = true, [B.tuple] = true
            }
            local prev_d = Runtime.Traceback.eval_depth
            local has_ma, match_args = pcall(PyClass.get_attr, class_obj, "__match_args__")
            Runtime.Traceback.eval_depth = prev_d

            if has_ma and match_args ~= nil then
                if type(match_args) ~= "table" or match_args.__type ~= "tuple" then
                    Runtime.raise("TypeError", tostring(class_obj.__name) .. ".__match_args__ must be a tuple")
                end
                local ma_elts = match_args.elements or {}
                if #pos_args > #ma_elts then
                    Runtime.raise("TypeError", string.format("%s() accepts %d positional sub-pattern%s (%d given)",
                        tostring(class_obj.__name), #ma_elts, #ma_elts == 1 and "" or "s", #pos_args))
                end
                for i, pos_pat in ipairs(pos_args) do
                    local attr_name = ma_elts[i]
                    if type(attr_name) ~= "string" then
                        Runtime.raise("TypeError", "__match_args__ elements must be strings")
                    end
                    seen_attrs[attr_name] = true
                    prev_d = Runtime.Traceback.eval_depth
                    local ok_attr, attr_val = pcall(PyClass.get_attr, val, attr_name)
                    Runtime.Traceback.eval_depth = prev_d
                    if not ok_attr or not match_pattern(pos_pat, attr_val, scope, exec_scope) then
                        return false
                    end
                end
            else
                local is_self_match = SELF_MATCH_BUILTINS[class_obj] == true
                if not is_self_match then
                    for b_cls, _ in pairs(SELF_MATCH_BUILTINS) do
                        if PyClass.is_subclass(class_obj, b_cls) then is_self_match = true; break end
                    end
                end
                if is_self_match then
                    if #pos_args > 1 then
                        Runtime.raise("TypeError", string.format("%s() accepts 1 positional sub-pattern (%d given)", tostring(class_obj.__name), #pos_args))
                    end
                    if not match_pattern(pos_args[1], val, scope, exec_scope) then return false end
                else
                    Runtime.raise("TypeError", string.format("%s() accepts 0 positional sub-patterns (%d given)", tostring(class_obj.__name), #pos_args))
                end
            end
        end

        for _, kw in ipairs(pat.kwargs or {}) do
            if seen_attrs[kw.key] then
                Runtime.raise("TypeError", string.format("%s() got multiple sub-patterns for attribute '%s'", tostring(class_obj.__name), tostring(kw.key)))
            end
            seen_attrs[kw.key] = true
            local prev_d = Runtime.Traceback.eval_depth
            local ok_attr, attr_val = pcall(PyClass.get_attr, val, kw.key)
            Runtime.Traceback.eval_depth = prev_d
            if not ok_attr or not match_pattern(kw.value, attr_val, scope, exec_scope) then
                return false
            end
        end
        return true
    end
    return false
end
Utils.match_pattern = match_pattern

make_iter_state = function(obj)
    local prev_depth = Runtime.Traceback.eval_depth
    local success, iter_func = pcall(PyClass.get_attr, obj, "__iter__")
    Runtime.Traceback.eval_depth = prev_depth
    local py_iter = obj
    if success and iter_func and type(iter_func) == "table" and iter_func.__type == "bound_method" then
        py_iter = Core.bind_and_exec_func(iter_func.__func, {}, {}, iter_func.__self)
    end
    prev_depth = Runtime.Traceback.eval_depth
    local success2, next_func = pcall(PyClass.get_attr, py_iter, "__next__")
    Runtime.Traceback.eval_depth = prev_depth
    if success2 and next_func then return { type = "iterator", obj = py_iter, next_func = next_func } end
    if type(obj) == "table" and obj.__type == "instance" and obj.__builtin_value ~= nil then
        obj = obj.__builtin_value
    end
    local iter_list = {}
    if type(obj) == "table" then
        if obj.__type == "range" then
            local curr, stop, step = obj.start, obj.stop, obj.step
            if step > 0 then while curr < stop do table.insert(iter_list, curr); curr = curr + step end
            else while curr > stop do table.insert(iter_list, curr); curr = curr + step end end
        elseif obj.__type == "list" or obj.__type == "tuple" or obj.__type == "set" or obj.__type == "frozenset" then
            iter_list = obj.elements
        elseif obj.__type == "dict" then
            for _, p in ipairs(obj.pairs) do table.insert(iter_list, p.key) end
        elseif obj.__type == "bytes" or obj.__type == "bytearray" or obj.__type == "memoryview" then
            if obj.__type == "memoryview" and obj._released then
                Runtime.raise("ValueError", "operation forbidden on released memoryview object")
            end
            for i = 1, #obj._data do table.insert(iter_list, string.byte(obj._data, i)) end
        else
            Runtime.raise("TypeError", "'" .. (obj.__class and obj.__class.__name or obj.__type or "object") .. "' object is not iterable")
        end
    elseif type(obj) == "string" then
        for i = 1, mw.ustring.len(obj) do table.insert(iter_list, mw.ustring.sub(obj, i, i)) end
    else
        Runtime.raise("TypeError", "'" .. type(obj) .. "' object is not iterable")
    end
    return { type = "list", elements = iter_list, idx = 0 }
end
Utils.make_iter_state = make_iter_state

get_next_item = function(state)
    if state.type == "iterator" then
        local prev_depth = Runtime.Traceback.eval_depth
        local success, res = pcall(function()
            if state.next_func.__type == "bound_method" then
                return Core.bind_and_exec_func(state.next_func.__func, {}, {}, state.next_func.__self)
            end
        end)
        Runtime.Traceback.eval_depth = prev_depth
        if not success then
            if type(res) == "table" and res.__class and res.__class.__name == "StopIteration" then return false, nil end
            error(res, 0)
        end
        return true, res
    else
        state.idx = state.idx + 1
        if state.idx <= #state.elements then return true, state.elements[state.idx] end
        return false, nil
    end
end
Utils.get_next_item = get_next_item

eval_comprehension = function(generators, gen_idx, scope, callback)
    if gen_idx > #generators then callback(); return end
    local gen = generators[gen_idx]
    local iter_state = make_iter_state(Core.eval_expr(gen.iter, scope))
    while true do
        local has_next, v = get_next_item(iter_state)
        if not has_next then break end
        assign_target(gen.target, v, scope)
        local condition_pass = true
        for _, if_node in ipairs(gen.ifs) do
            if not Utils.is_truthy(Core.eval_expr(if_node, scope)) then condition_pass = false; break end
        end
        if condition_pass then eval_comprehension(generators, gen_idx + 1, scope, callback) end
    end
end
Utils.eval_comprehension = eval_comprehension

assign_target = function(target_node, value, scope)
    if value == nil then value = Runtime.builtins.None end
	-- 支援單獨星號打包賦值:*u = (a+bj+ck+djk).imag 或 *a = foo()
    if target_node.type == "Starred" then
        local elts = Runtime.builtins.list.__call({ value }).elements
        local is_ut = (type(value) == "table" and value._is_unpacked_tuple == true)
        local packed = { __type = is_ut and "tuple" or "list", elements = elts }
        assign_target(target_node.value, packed, scope)
        return
    end
    if target_node.type == "Identifier" then
        Utils.set_var(target_node.name, value, scope)
    elseif target_node.type == "Index" then
        if target_node.index.type == "Slice" then
            local target = Core.eval_expr(target_node.target, scope)
            if type(target) == "table" and target.__type == "instance" and target.__builtin_value ~= nil then
                target = target.__builtin_value
            end
            if type(target) == "table" and (target.__type == "bytearray" or target.__type == "memoryview") then
                if target.__type == "memoryview" then
                    if target._released then Runtime.raise("ValueError", "operation forbidden on released memoryview object") end
                    if target._readonly then Runtime.raise("TypeError", "cannot modify read-only memory") end
                end
                local lower = target_node.index.lower and Core.eval_expr(target_node.index.lower, scope) or nil
                local upper = target_node.index.upper and Core.eval_expr(target_node.index.upper, scope) or nil
                local step = target_node.index.step and Core.eval_expr(target_node.index.step, scope) or nil
                if step and step ~= 1 then Runtime.raise("NotImplementedError", "extended slice assignment on bytes-like object is not supported") end
                local repl = Runtime.builtins.bytes.__call({value})._data
                local p_start, p_stop, _ = resolve_slice(#target._data, lower, upper, 1)
                if target.__type == "memoryview" and #repl ~= (p_stop - p_start) then
                    Runtime.raise("ValueError", "memoryview assignment: lvalue and rvalue have different structures")
                end
                target._data = target._data:sub(1, p_start) .. repl .. target._data:sub(p_stop + 1)
                if target.__type == "memoryview" and target._obj then target._obj._data = target._data end
                return
            end
            if type(target) ~= "table" or target.__type ~= "list" then Runtime.raise("TypeError", "object does not support slice assignment") end
            local lower = target_node.index.lower and Core.eval_expr(target_node.index.lower, scope) or nil
            local upper = target_node.index.upper and Core.eval_expr(target_node.index.upper, scope) or nil
            local step = target_node.index.step and Core.eval_expr(target_node.index.step, scope) or nil
            local val_list = Runtime.builtins.list.__call({value}).elements
            local length = #target.elements
            local p_start, p_stop, p_step = resolve_slice(length, lower, upper, step)
            if p_step ~= 1 then
                local slice_len = 0
                if p_step > 0 then for i = p_start, p_stop - 1, p_step do slice_len = slice_len + 1 end
                else for i = p_start, p_stop + 1, p_step do slice_len = slice_len + 1 end end
                if slice_len ~= #val_list then Runtime.raise("ValueError", "attempt to assign sequence of size " .. #val_list .. " to extended slice of size " .. slice_len) end
                local v_idx = 1
                if p_step > 0 then for i = p_start, p_stop - 1, p_step do target.elements[i + 1] = val_list[v_idx]; v_idx = v_idx + 1 end
                else for i = p_start, p_stop + 1, p_step do target.elements[i + 1] = val_list[v_idx]; v_idx = v_idx + 1 end end
            else
                local head, tail = {}, {}
                for i = 1, p_start do table.insert(head, target.elements[i]) end
                for i = p_stop + 1, length do table.insert(tail, target.elements[i]) end
                local new_elements = {}
                for _, v in ipairs(head) do table.insert(new_elements, v) end
                for _, v in ipairs(val_list) do table.insert(new_elements, v) end
                for _, v in ipairs(tail) do table.insert(new_elements, v) end
                target.elements = new_elements
            end
        else
            local target = Core.eval_expr(target_node.target, scope)
            local idx = Core.eval_expr(target_node.index, scope)
            if type(target) == "table" and target.__type == "instance" then
                local prev_depth = Runtime.Traceback.eval_depth
                local success, attr = pcall(PyClass.get_attr, target, "__setitem__")
                Runtime.Traceback.eval_depth = prev_depth
                if success and attr and attr.__type == "bound_method" then
                    Core.bind_and_exec_func(attr.__func, {idx, value}, {}, attr.__self)
                    return
                elseif target.__builtin_value ~= nil then
                    target = target.__builtin_value
                end
            end
            if type(target) == "table" and target.__type == "dict" then
                local found = false
                for _, p in ipairs(target.pairs) do if py_eq(p.key, idx) then p.value = value; found = true; break end end
                if not found then table.insert(target.pairs, {key = idx, value = value}) end
                if target.__scope_ref and type(idx) == "string" then target.__scope_ref[idx] = value end
            else
                local lua_idx = resolve_index(target, idx)
                if type(target) == "table" and target.__type == "list" then
                    target.elements[lua_idx] = value
                elseif type(target) == "table" and (target.__type == "bytearray" or target.__type == "memoryview") then
                    if target.__type == "memoryview" then
                        if target._released then Runtime.raise("ValueError", "operation forbidden on released memoryview object") end
                        if target._readonly then Runtime.raise("TypeError", "cannot modify read-only memory") end
                    end
                    local bval = unwrap_int_index(value)
                    if type(bval) ~= "number" or math.floor(bval) ~= bval then Runtime.raise("TypeError", "an integer is required") end
                    if bval < 0 or bval > 255 then Runtime.raise("ValueError", "byte must be in range(0, 256)") end
                    target._data = target._data:sub(1, lua_idx - 1) .. string.char(bval) .. target._data:sub(lua_idx + 1)
                    if target.__type == "memoryview" and target._obj then target._obj._data = target._data end
                else
                    Runtime.raise("TypeError", "object does not support item assignment")
                end
            end
        end
    elseif target_node.type == "Attribute" then
        local target = Core.eval_expr(target_node.target, scope)
        PyClass.set_attr(target, target_node.attr, value)
    elseif target_node.type == "Tuple" or target_node.type == "List" then
        local iter_state = make_iter_state(value); local unpacked = {}
        while true do local has_next, v = get_next_item(iter_state); if not has_next then break end; table.insert(unpacked, v) end
        
        local starred_idx = nil
        for i, elem_node in ipairs(target_node.elements) do
            if elem_node.type == "Starred" then
                if starred_idx then Runtime.raise("SyntaxError", "two starred expressions in assignment") end
                starred_idx = i
            end
        end
        
        if starred_idx then
            local min_len = #target_node.elements - 1
            if #unpacked < min_len then
                Runtime.raise("ValueError", "not enough values to unpack (expected at least " .. min_len .. ", got " .. #unpacked .. ")")
            end
            
            local star_len = #unpacked - min_len
            local current_idx = 1
            for i, elem_node in ipairs(target_node.elements) do
                if i == starred_idx then
                    local star_list = {}
                    for j = 1, star_len do
                        table.insert(star_list, unpacked[current_idx])
                        current_idx = current_idx + 1
                    end
					local starred_container_type = (type(value) == "table" and value._is_unpacked_tuple == true) and "tuple" or "list"
                    assign_target(elem_node.value, { __type = starred_container_type, elements = star_list }, scope)
                else
                    assign_target(elem_node, unpacked[current_idx], scope)
                    current_idx = current_idx + 1
                end
            end
        else
            if #unpacked > #target_node.elements then Runtime.raise("ValueError", "too many values to unpack (expected " .. #target_node.elements .. ")") end
            if #unpacked < #target_node.elements then Runtime.raise("ValueError", "not enough values to unpack (expected " .. #target_node.elements .. ", got " .. #unpacked .. ")") end
            for i, elem_node in ipairs(target_node.elements) do assign_target(elem_node, unpacked[i], scope) end
        end
    else
        Runtime.raise("SyntaxError", "invalid syntax for assignment")
    end
end
Utils.assign_target = assign_target

return Utils