跳转到内容

模組:PythonScript/Parser

维基百科,自由的百科全书
-- Module:PythonScript/Parser
local require_=require;local require=require_("Module:沙盒/a2569875/PythonScript/RequireWrapper")
-- 語法分析器
local Parser = {}

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

local PARSE_DEPTH_LIMIT = 200
local current_parse_depth = 0

local BP = {
    LOWEST = 0, ASSIGN = 10, COMMA = 12, WALRUS = 14, CONDITIONAL = 15,
    OR = 20, AND = 30, NOT = 40, COMPARE = 50,
    BIT_OR = 60, BIT_XOR = 70, BIT_AND = 80, SHIFT = 90,
    SUM = 100, PRODUCT = 110, UNARY = 120, POWER = 130, CALL = 140,
}

local LBP = {
    ["="] = BP.ASSIGN, ["+="] = BP.ASSIGN, ["-="] = BP.ASSIGN, ["*="] = BP.ASSIGN, ["/="] = BP.ASSIGN, ["//="] = BP.ASSIGN, ["@="] = BP.ASSIGN,
    ["%="] = BP.ASSIGN, ["**="] = BP.ASSIGN, ["&="] = BP.ASSIGN, ["|="] = BP.ASSIGN, ["^="] = BP.ASSIGN, ["<<="] = BP.ASSIGN, [">>="] = BP.ASSIGN, ["\\="] = BP.ASSIGN,
    [","] = BP.COMMA, [":="] = BP.WALRUS, ["if"] = BP.CONDITIONAL,
    ["or"] = BP.OR, ["and"] = BP.AND,
    ["=="] = BP.COMPARE, ["!="] = BP.COMPARE, ["<"] = BP.COMPARE, [">"] = BP.COMPARE, ["<="] = BP.COMPARE, [">="] = BP.COMPARE, ["is"] = BP.COMPARE, ["in"] = BP.COMPARE,
    ["is not"] = BP.COMPARE, ["not in"] = BP.COMPARE,
    ["|"] = BP.BIT_OR, ["^"] = BP.BIT_XOR, ["&"] = BP.BIT_AND,
    ["<<"] = BP.SHIFT, [">>"] = BP.SHIFT,
    ["+"] = BP.SUM, ["-"] = BP.SUM,
    ["*"] = BP.PRODUCT, ["/"] = BP.PRODUCT, ["//"] = BP.PRODUCT, ["%"] = BP.PRODUCT, ["@"] = BP.PRODUCT, ["\\"] = BP.PRODUCT,
    ["**"] = BP.POWER, ["("] = BP.CALL, ["["] = BP.CALL, ["."] = BP.CALL,
}

function Parser.parse(tokens, parse_mode)
    local pos = 1
    local current = tokens[pos]
    local token_count = #tokens
    
    local current_func_stack = {}
    local goto_enabled = false
    
    local function advance()
        if pos < token_count then pos = pos + 1 end
        current = tokens[pos]
    end

    local function match_type(type)
        if current.type == type then advance(); return true end
        return false
    end

    local function match_val(val)
        if current.value == val then advance(); return true end
        return false
    end

    local function consume_type(type, err_msg)
        if current.type == type then
            local t = current; advance(); return t
        end
        Runtime.Traceback.current_line = current.line
        Runtime.raise("SyntaxError", err_msg or ("Expected " .. type))
    end

    local function consume_val(val, err_msg)
        if current.value == val then
            local t = current; advance(); return t
        end
        Runtime.Traceback.current_line = current.line
        Runtime.raise("SyntaxError", err_msg or ("Expected '" .. val .. "'"))
    end

    local function skip_newlines()
        while current.type == "NEWLINE" do advance() end
    end

    local parse_expression, parse_nud, parse_led

    parse_expression = function(rbp)
		current_parse_depth = current_parse_depth + 1
	    if current_parse_depth > PARSE_DEPTH_LIMIT then
	        Runtime.raise("SyntaxError", "maximum recursion depth exceeded during parsing")
	    end
		
        rbp = rbp or BP.LOWEST
        skip_newlines()
        local left = parse_nud()
        while current.type ~= "EOF" do
            local next_bp = LBP[current.value] or BP.LOWEST
            
            if current.value == "is" and tokens[pos+1] and tokens[pos+1].value == "not" then
                next_bp = BP.COMPARE
            elseif current.value == "not" and tokens[pos+1] and tokens[pos+1].value == "in" then
                next_bp = BP.COMPARE
            end
            
            if rbp >= next_bp then break end
            left = parse_led(left)
        end
		current_parse_depth = current_parse_depth - 1
        return left
    end

    local function collect_comp_targets(node, target_set)
        if not node then return end
        if node.type == "Identifier" then
            target_set[node.name] = true
        elseif node.type == "Tuple" or node.type == "List" then
            for _, el in ipairs(node.elements or {}) do collect_comp_targets(el, target_set) end
        elseif node.type == "Starred" then
            collect_comp_targets(node.value, target_set)
        elseif node.type == "NamedExpr" then
            Runtime.Traceback.current_line = node.line or current.line
            Runtime.raise("SyntaxError", "cannot assign to named expression")
        end
    end

    local function check_comp_walrus(node, target_set, in_iter)
        if type(node) ~= "table" then return end
        if node.type == "NamedExpr" then
            if in_iter then
                Runtime.Traceback.current_line = node.line or current.line
                Runtime.raise("SyntaxError", "assignment expression cannot be used in a comprehension iterable expression")
            end
            if node.target and target_set[node.target.name] then
                Runtime.Traceback.current_line = node.line or current.line
                Runtime.raise("SyntaxError", "assignment expression cannot rebind comprehension iteration variable '" .. tostring(node.target.name) .. "'")
            end
        end
        for k, v in pairs(node) do
            if k ~= "type" and k ~= "line" and type(v) == "table" then
                check_comp_walrus(v, target_set, in_iter)
            end
        end
    end

    local function parse_comprehension_generators(elt1, elt2)
        local generators = {}
        local target_set = {}
        while true do
            local old_in_bp = LBP["in"]; local old_if_bp = LBP["if"]
            LBP["in"] = -1 
            local targets = { parse_expression(BP.COMMA) }
            while match_val(",") do table.insert(targets, parse_expression(BP.COMMA)) end
            LBP["in"] = old_in_bp
            local target = #targets == 1 and targets[1] or { type = "Tuple", elements = targets, line = current.line }
            collect_comp_targets(target, target_set)
            
            consume_val("in", "Expected 'in' in comprehension")
            LBP["if"] = -1; local iter = parse_expression(BP.CONDITIONAL)
            
            local ifs = {}
            while match_val("if") do
                LBP["if"] = old_if_bp
                table.insert(ifs, parse_expression(BP.WALRUS - 1))
                LBP["if"] = -1
            end
            LBP["if"] = old_if_bp
            
            table.insert(generators, { target = target, iter = iter, ifs = ifs })
            if not match_val("for") then break end
        end
        for _, gen in ipairs(generators) do
            check_comp_walrus(gen.iter, target_set, true)
            for _, if_expr in ipairs(gen.ifs) do check_comp_walrus(if_expr, target_set, false) end
        end
        if elt1 then check_comp_walrus(elt1, target_set, false) end
        if elt2 then check_comp_walrus(elt2, target_set, false) end
        return generators
    end

    local function parse_parameters(end_val, allow_annotations)
        local args, kwonlyargs = {}, {}
        local defaults, annotations = {}, {}
        local posonly_count = 0
        local vararg, kwarg = nil, nil
        local seen_names = {}
        local seen_slash, seen_star, need_kwonly = false, false, false
        local seen_pos_default = false

        local function reg_name(name, line_no)
            if seen_names[name] then
                Runtime.Traceback.current_line = line_no or current.line
                Runtime.raise("SyntaxError", "duplicate argument '" .. name .. "' in function definition")
            end
            seen_names[name] = true
        end

        if current.value ~= end_val then
            while true do
                if kwarg ~= nil then
                    Runtime.Traceback.current_line = current.line
                    Runtime.raise("SyntaxError", "arguments cannot follow var-keyword argument")
                end
                if match_val("/") then
                    if seen_slash then
                        Runtime.Traceback.current_line = current.line
                        Runtime.raise("SyntaxError", "/ may appear only once")
                    end
                    if seen_star then
                        Runtime.Traceback.current_line = current.line
                        Runtime.raise("SyntaxError", "/ must be ahead of *")
                    end
                    if #args == 0 then
                        Runtime.Traceback.current_line = current.line
                        Runtime.raise("SyntaxError", "at least one argument must precede /")
                    end
                    seen_slash = true
                    posonly_count = #args
                elseif match_val("*") then
                    if seen_star then
                        Runtime.Traceback.current_line = current.line
                        Runtime.raise("SyntaxError", "* argument may appear only once")
                    end
                    seen_star = true
                    if current.type == "IDENTIFIER" then
                        local tok = consume_type("IDENTIFIER")
                        vararg = tok.value
                        reg_name(vararg, tok.line)
                        if allow_annotations and match_val(":") then
                            annotations[vararg] = parse_expression(BP.COMMA)
                        end
                        if match_val("=") then
                            Runtime.Traceback.current_line = current.line
                            Runtime.raise("SyntaxError", "var-positional argument cannot have default value")
                        end
                    else
                        need_kwonly = true
                    end
                elseif match_val("**") then
                    if need_kwonly then
                        Runtime.Traceback.current_line = current.line
                        Runtime.raise("SyntaxError", "named arguments must follow bare *")
                    end
                    local tok = consume_type("IDENTIFIER", "Expected parameter name after '**'")
                    kwarg = tok.value
                    reg_name(kwarg, tok.line)
                    if allow_annotations and match_val(":") then
                        annotations[kwarg] = parse_expression(BP.COMMA)
                    end
                    if match_val("=") then
                        Runtime.Traceback.current_line = current.line
                        Runtime.raise("SyntaxError", "var-keyword argument cannot have default value")
                    end
                else
                    local tok = consume_type("IDENTIFIER", "Expected parameter name")
                    local arg_name = tok.value
                    reg_name(arg_name, tok.line)
                    if allow_annotations and match_val(":") then
                        annotations[arg_name] = parse_expression(BP.COMMA)
                    end
                    local has_default = false
                    if match_val("=") then
                        defaults[arg_name] = parse_expression(BP.COMMA)
                        has_default = true
                    end
                    if seen_star then
                        need_kwonly = false
                        table.insert(kwonlyargs, arg_name)
                    else
                        if has_default then
                            seen_pos_default = true
                        elseif seen_pos_default then
                            Runtime.Traceback.current_line = tok.line
                            Runtime.raise("SyntaxError", "non-default argument follows default argument")
                        end
                        table.insert(args, arg_name)
                    end
                end
                if not match_val(",") then break end
                if current.value == end_val then break end
            end
        end
        if need_kwonly then
            Runtime.Traceback.current_line = current.line
            Runtime.raise("SyntaxError", "named arguments must follow bare *")
        end
        return {
            args = args,
            posonly_count = posonly_count,
            kwonlyargs = kwonlyargs,
            defaults = defaults,
            annotations = annotations,
            vararg = vararg,
            kwarg = kwarg
        }
    end

    parse_nud = function()
        local t = current
        advance()

        if t.type == "NUMBER" then return { type = "Number", value = t.value, is_complex = t.is_complex, line = t.line }
        elseif t.type == "IDENTIFIER" then return { type = "Identifier", name = t.value, line = t.line }
        elseif t.type == "KEYWORD" then
            if t.value == "True" then return { type = "Boolean", value = true, line = t.line }
            elseif t.value == "False" then return { type = "Boolean", value = false, line = t.line }
            elseif t.value == "None" then return { type = "None", line = t.line }
            elseif t.value == "not" then return { type = "UnaryOp", op = "not", right = parse_expression(BP.NOT), line = t.line }
            elseif t.value == "await" then
                if #current_func_stack > 0 and current_func_stack[#current_func_stack].is_async then
                    return { type = "Await", value = parse_expression(BP.UNARY), line = t.line }
                else
                    Runtime.Traceback.current_line = t.line
                    Runtime.raise("SyntaxError", "'await' outside async function")
                end
            elseif t.value == "yield" then
                if #current_func_stack > 0 then
                    current_func_stack[#current_func_stack].is_generator = true
                else
                    if parse_mode ~= "invoke" then
                        Runtime.Traceback.current_line = t.line
                        Runtime.raise("SyntaxError", "'yield' outside function")
                    end
                end
                if current.type == "KEYWORD" and current.value == "from" then
                    advance()
                    return { type = "YieldFrom", value = parse_expression(), line = t.line }
                end
                local val = nil
                if current.type ~= "NEWLINE" and current.type ~= "EOF" and current.value ~= ")" and current.value ~= "]" and current.value ~= "}" and current.value ~= "," then
                    val = parse_expression()
                end
                return { type = "Yield", value = val, line = t.line }

            elseif t.value == "lambda" then
                local params = parse_parameters(":", false)
                consume_val(":", "Expected ':' after lambda arguments")
                table.insert(current_func_stack, { is_generator = false, is_async = false })
                local body = parse_expression(BP.COMMA)
                local func_info = table.remove(current_func_stack)
                return {
                    type = "Lambda",
                    args = params.args,
                    posonly_count = params.posonly_count,
                    kwonlyargs = params.kwonlyargs,
                    defaults = params.defaults,
                    vararg = params.vararg,
                    kwarg = params.kwarg,
                    body = body,
                    is_generator = func_info.is_generator,
                    line = t.line
                }
            end
            
        elseif t.type == "STRING" then
            local str_node = { type = "String", value = t.value, is_fstring = t.is_fstring, is_raw = t.is_raw, is_bytes = t.is_bytes, is_wiki = t.is_wiki, line = t.line }
            while current.type == "STRING" do
                str_node.value = str_node.value .. current.value
                str_node.is_fstring = str_node.is_fstring or current.is_fstring
                str_node.is_bytes = str_node.is_bytes or current.is_bytes
				str_node.is_wiki = str_node.is_wiki or current.is_wiki
                advance()
            end
            return str_node
            
        elseif t.value == "(" then
            if current.value == ")" then advance(); return { type = "Tuple", elements = {}, line = t.line } end
            local expr = parse_expression()
            if match_val("for") then
                local generators = parse_comprehension_generators(expr)
                consume_val(")", "Expected ')' to close generator expression")
                return { type = "GeneratorExp", elt = expr, generators = generators, line = t.line }
            end
            consume_val(")", "Expected ')' to close parentheses")
            if type(expr) == "table" then expr.parenthesized = true end
            return expr
            
        elseif t.value == "[" then
            if match_val("]") then return { type = "List", elements = {}, line = t.line } end
            local first_expr = parse_expression(BP.COMMA)
            if match_val("for") then
                local generators = parse_comprehension_generators(first_expr)
                consume_val("]", "Expected ']' for list comprehension")
                return { type = "ListComp", elt = first_expr, generators = generators, line = t.line }
            else
                local elements = { first_expr }
                while match_val(",") do
                    if current.value == "]" then break end
                    table.insert(elements, parse_expression(BP.COMMA))
                end
                consume_val("]", "Expected ']' for list")
                return { type = "List", elements = elements, line = t.line }
            end
            
        elseif t.value == "{" then
            if match_val("}") then return { type = "Dict", pairs = {}, line = t.line } end
            if match_val("**") then
                local rest_expr = parse_expression(BP.COMMA)
                local kv_pairs = { { key = nil, value = rest_expr, line = current.line } }
                while match_val(",") do
                    if current.value == "}" then break end
                    if match_val("**") then
                        table.insert(kv_pairs, { key = nil, value = parse_expression(BP.COMMA), line = current.line })
                    else
                        local k = parse_expression(BP.COMMA); consume_val(":")
                        local v = parse_expression(BP.COMMA)
                        table.insert(kv_pairs, { key = k, value = v })
                    end
                end
                consume_val("}", "Expected '}' for dict")
                return { type = "Dict", pairs = kv_pairs, line = t.line }
            end
            local first_expr = parse_expression(BP.COMMA)
            if match_val(":") then
                local val_expr = parse_expression(BP.COMMA)
                if match_val("for") then
                    local generators = parse_comprehension_generators(first_expr, val_expr)
                    consume_val("}", "Expected '}' for dict comprehension")
                    return { type = "DictComp", key = first_expr, value = val_expr, generators = generators, line = t.line }
                else
                    local kv_pairs = { { key = first_expr, value = val_expr } }
                    while match_val(",") do
                        if current.value == "}" then break end
                        if match_val("**") then
                            table.insert(kv_pairs, { key = nil, value = parse_expression(BP.COMMA), line = current.line })
                        else
                            local k = parse_expression(BP.COMMA); consume_val(":")
                            local v = parse_expression(BP.COMMA)
                            table.insert(kv_pairs, { key = k, value = v })
                        end
                    end
                    consume_val("}", "Expected '}' for dict")
                    return { type = "Dict", pairs = kv_pairs, line = t.line }
                end
            else
                if match_val("for") then
                    local generators = parse_comprehension_generators(first_expr)
                    consume_val("}", "Expected '}' for set comprehension")
                    return { type = "SetComp", elt = first_expr, generators = generators, line = t.line }
                else
                    local elements = { first_expr }
                    while match_val(",") do
                        if current.value == "}" then break end
                        table.insert(elements, parse_expression(BP.COMMA))
                    end
                    consume_val("}", "Expected '}' for set")
                    return { type = "Set", elements = elements, line = t.line }
                end
            end
        elseif t.value == "+" or t.value == "-" or t.value == "~" then return { type = "UnaryOp", op = t.value, right = parse_expression(BP.UNARY), line = t.line }
        elseif t.value == "*" then return { type = "Starred", value = parse_expression(BP.UNARY), line = t.line }
        end
        
        Runtime.Traceback.current_line = t.line
        Runtime.raise("SyntaxError", "Unexpected token '" .. t.value .. "'")
    end

    parse_led = function(left)
        local t = current
        advance()

        if t.value == "is" and current.value == "not" then
            advance()
            t = { type = "KEYWORD", value = "is not", line = t.line }
        elseif t.value == "not" and current.value == "in" then
            advance()
            t = { type = "KEYWORD", value = "not in", line = t.line }
        end
        
        local bp = LBP[t.value] or BP.LOWEST

        if t.value == "," then
            local elements = { left }
            if current.type ~= "NEWLINE" and current.type ~= "EOF" and current.value ~= "=" and current.value ~= ")" and current.value ~= "]" and current.value ~= "}" then
                table.insert(elements, parse_expression(BP.COMMA))
                while current.value == "," do
                    advance()
                    if current.type == "NEWLINE" or current.type == "EOF" or current.value == "=" or current.value == ")" or current.value == "]" or current.value == "}" then break end
                    table.insert(elements, parse_expression(BP.COMMA))
                end
            end
            return { type = "Tuple", elements = elements, line = t.line }

        elseif t.value == "(" then
            local args = {}; local kwargs = {}
            local seen_keyword = false
            local seen_kw_unpack = false
            local seen_kw_keys = {}
            if current.value ~= ")" then
                while true do
                    if match_val("*") then
                        if seen_kw_unpack then
                            Runtime.Traceback.current_line = current.line
                            Runtime.raise("SyntaxError", "iterable argument unpacking follows keyword argument unpacking")
                        end
                        table.insert(args, { type = "Starred", value = parse_expression(BP.COMMA) })
                    elseif match_val("**") then
                        seen_kw_unpack = true
                        table.insert(kwargs, { key = nil, value = parse_expression(BP.COMMA) })
                    elseif current.type == "IDENTIFIER" and tokens[pos+1] and tokens[pos+1].value == "=" then
                        local key_tok = consume_type("IDENTIFIER")
                        local key = key_tok.value
                        consume_val("=")
                        if seen_kw_keys[key] then
                            Runtime.Traceback.current_line = key_tok.line
                            Runtime.raise("SyntaxError", "keyword argument repeated: " .. key)
                        end
                        seen_kw_keys[key] = true
                        seen_keyword = true
                        table.insert(kwargs, { key = key, value = parse_expression(BP.COMMA) })
                    else
                        local arg_line = current.line
                        local arg_expr = parse_expression(BP.COMMA)
                        if current.value == "=" then
                            Runtime.Traceback.current_line = arg_line
                            Runtime.raise("SyntaxError", "expression cannot contain assignment, perhaps you meant \"==\"?")
                        end
                        if seen_kw_unpack then
                            Runtime.Traceback.current_line = arg_line
                            Runtime.raise("SyntaxError", "positional argument follows keyword argument unpacking")
                        elseif seen_keyword then
                            Runtime.Traceback.current_line = arg_line
                            Runtime.raise("SyntaxError", "positional argument follows keyword argument")
                        end
                        if match_val("for") then
                            local generators = parse_comprehension_generators(arg_expr)
                            table.insert(args, { type = "GeneratorExp", elt = arg_expr, generators = generators, line = t.line })
                        else
                            table.insert(args, arg_expr)
                        end
                    end
                    if not match_val(",") then break end
                    if current.value == ")" then break end
                end
            end
            consume_val(")", "Expected ')' after function arguments")
            return { type = "Call", callee = left, args = args, kwargs = kwargs, line = t.line }
            
        elseif t.value == "[" then
			local function parse_subscript_item()
				local is_slice = false; local lower, upper, step = nil, nil, nil
				if match_val(":") then
					is_slice = true
					if current.value ~= "]" and current.value ~= ":" and current.value ~= "," then upper = parse_expression(BP.COMMA) end
					if match_val(":") then if current.value ~= "]" and current.value ~= "," then step = parse_expression(BP.COMMA) end end
				else
					lower = parse_expression(BP.COMMA)
					if match_val(":") then
						is_slice = true
						if current.value ~= "]" and current.value ~= ":" and current.value ~= "," then upper = parse_expression(BP.COMMA) end
						if match_val(":") then if current.value ~= "]" and current.value ~= "," then step = parse_expression(BP.COMMA) end end
					end
				end
				if is_slice then return { type = "Slice", lower = lower, upper = upper, step = step, line = t.line }
				else return lower end
			end

			local first_item = parse_subscript_item()
			local index_node
			if match_val(",") then
				local elts = { first_item }
				while current.value ~= "]" and current.type ~= "EOF" do
					table.insert(elts, parse_subscript_item())
					if not match_val(",") then break end
				end
				index_node = { type = "Tuple", elements = elts, line = t.line }
			else
				index_node = first_item
			end

			consume_val("]", "Expected ']' after index or slice")
			return { type = "Index", target = left, index = index_node, line = t.line }
            
        elseif t.value == "if" then
            local test = parse_expression(BP.CONDITIONAL)
            consume_val("else", "Expected 'else' in conditional expression")
            local orelse = parse_expression(bp - 1)
            return { type = "IfExp", body = left, test = test, orelse = orelse, line = t.line }
            
        elseif t.value == "." then
            local attr = consume_type("IDENTIFIER", "Expected attribute name").value
            return { type = "Attribute", target = left, attr = attr, line = t.line }

        elseif t.value == "as" then
            local alias = consume_type("IDENTIFIER", "Expected identifier after 'as'").value
            if alias == "_" then
                Runtime.Traceback.current_line = t.line
                Runtime.raise("SyntaxError", "cannot use '_' as a target of 'as' in match pattern")
            end
            return { type = "MatchAs", pattern = left, alias = alias, line = t.line }

        elseif t.value == ":=" then
            if left.type ~= "Identifier" or left.parenthesized then
                Runtime.Traceback.current_line = t.line
                Runtime.raise("SyntaxError", "cannot use assignment expressions with " .. string.lower(left.type))
            end
            return { type = "NamedExpr", target = left, value = parse_expression(bp - 1), line = t.line }

        elseif bp == BP.ASSIGN then
            if left.type == "NamedExpr" then
                Runtime.Traceback.current_line = t.line
                Runtime.raise("SyntaxError", "cannot assign to named expression")
            end
            local val_expr = parse_expression(bp - 1)
            if val_expr.type == "NamedExpr" and not val_expr.parenthesized then
                Runtime.Traceback.current_line = val_expr.line or t.line
                Runtime.raise("SyntaxError", "unparenthesized named expression cannot be used right of '" .. t.value .. "'")
            end
            return { type = "Assign", op = t.value, target = left, value = val_expr, line = t.line }
            
        elseif t.value == "**" then return { type = "BinaryOp", op = t.value, left = left, right = parse_expression(bp - 1), line = t.line }
        else return { type = "BinaryOp", op = t.value, left = left, right = parse_expression(bp), line = t.line }
        end
    end

    local parse_statement, parse_block, parse_suite

    parse_block = function()
        consume_type("INDENT", "Expected an indented block")
        local stmts = {}
        while current.type ~= "DEDENT" and current.type ~= "EOF" do table.insert(stmts, parse_statement()) end
        consume_type("DEDENT", "Expected dedent")
        return stmts
    end

    parse_suite = function()
        if current.type == "NEWLINE" then
            advance()
            return parse_block()
        else
            return { parse_statement() }
        end
    end

    local function parse_import_name()
        local name_parts = {}
        while current.value == "." do
            table.insert(name_parts, ".")
            advance()
        end
        
        if current.type == "IDENTIFIER" then
            table.insert(name_parts, current.value)
            advance()
        elseif #name_parts == 0 then
            Runtime.Traceback.current_line = current.line
            Runtime.raise("SyntaxError", "Expected module name")
        end

        while current.value == "." or current.value == ":" or current.value == "/" or current.value == "-" do
            table.insert(name_parts, current.value)
            advance()
            if current.type == "IDENTIFIER" or current.type == "NUMBER" then
                table.insert(name_parts, tostring(current.value))
                advance()
            end
        end
        return table.concat(name_parts)
    end

    parse_statement = function()
        skip_newlines()
        
        local decorator_list = {}
        while current.value == "@" do
            advance()
            table.insert(decorator_list, parse_expression())
            consume_type("NEWLINE", "Expected newline after decorator")
            skip_newlines()
        end

        local is_async = false
        if current.type == "KEYWORD" and current.value == "async" then
            is_async = true
            advance()
        end
        
        local t = current
        local stmt = nil

        if t.type == "KEYWORD" then
            if t.value == "if" then
                advance()
                local test = parse_expression(); consume_val(":")
                local body = parse_suite(); local orelse = {}
                while current.value == "elif" do
                    advance()
                    local elif_test = parse_expression(); consume_val(":")
                    table.insert(orelse, { type = "If", test = elif_test, body = parse_suite(), orelse = {} })
                end
                if match_val("else") then
                    consume_val(":")
                    local else_body = parse_suite()
                    if #orelse > 0 then orelse[#orelse].orelse = else_body else orelse = else_body end
                end
                stmt = { type = "If", test = test, body = body, orelse = orelse, line = t.line }

            elseif t.value == "while" then
                advance()
                local test = parse_expression(); consume_val(":")
                local body = parse_suite(); local orelse = {}
                if match_val("else") then consume_val(":"); orelse = parse_suite() end
                stmt = { type = "While", test = test, body = body, orelse = orelse, line = t.line }

            elseif t.value == "for" then
                advance()
                local old_in_bp = LBP["in"]; LBP["in"] = -1 
                local targets = { parse_expression(BP.COMMA) }
                while match_val(",") do table.insert(targets, parse_expression(BP.COMMA)) end
                LBP["in"] = old_in_bp
                local target = #targets == 1 and targets[1] or { type = "Tuple", elements = targets, line = t.line }
                consume_val("in", "Expected 'in' after for loop target")
                local iter = parse_expression(); consume_val(":")
                local body = parse_suite(); local orelse = {}
                if match_val("else") then consume_val(":"); orelse = parse_suite() end
                stmt = { type = "For", target = target, iter = iter, body = body, orelse = orelse, line = t.line }
                
            elseif t.value == "try" then
                advance(); consume_val(":")
                local body = parse_suite(); local handlers = {}
                while current.value == "except" do
                    advance(); local type_expr = nil; local name = nil
                    if current.value ~= ":" then
                        type_expr = parse_expression()
                        if match_val("as") then name = consume_type("IDENTIFIER").value end
                    end
                    consume_val(":")
                    table.insert(handlers, { type = "ExceptHandler", type_expr = type_expr, name = name, body = parse_suite() })
                end
                local orelse = {}
                if match_val("else") then consume_val(":"); orelse = parse_suite() end
                local finalbody = {}
                if match_val("finally") then consume_val(":"); finalbody = parse_suite() end
                stmt = { type = "Try", body = body, handlers = handlers, orelse = orelse, finalbody = finalbody, line = t.line }

            elseif t.value == "def" then
                advance()
                local name = consume_type("IDENTIFIER", "Expected function name").value
                consume_val("(")
                local params = parse_parameters(")", true)
                consume_val(")")
                local returns = nil
                if match_val("->") or (current.value == "-" and tokens[pos+1] and tokens[pos+1].value == ">") then
                    if current.value == "-" then advance(); advance() end
                    returns = parse_expression(BP.COMMA)
                end
                consume_val(":")
                table.insert(current_func_stack, { is_generator = false, is_async = is_async })
                local body = parse_suite()
                local func_info = table.remove(current_func_stack)
                stmt = {
                    type = "FunctionDef",
                    name = name,
                    args = params.args,
                    posonly_count = params.posonly_count,
                    kwonlyargs = params.kwonlyargs,
                    defaults = params.defaults,
                    annotations = params.annotations,
                    returns = returns,
                    vararg = params.vararg,
                    kwarg = params.kwarg,
                    body = body,
                    decorator_list = decorator_list,
                    is_generator = func_info.is_generator,
                    is_async = func_info.is_async,
                    line = t.line
                }
                
            elseif t.value == "class" then
                advance()
                local name = consume_type("IDENTIFIER", "Expected class name").value
                local bases = {}
                local kwargs = {}
                if match_val("(") then
                    local seen_keyword = false
                    local seen_kw_unpack = false
                    local seen_kw_keys = {}
                    if current.value ~= ")" then
                        while true do
                            if match_val("*") then
                                if seen_kw_unpack then
                                    Runtime.Traceback.current_line = current.line
                                    Runtime.raise("SyntaxError", "iterable argument unpacking follows keyword argument unpacking")
                                end
                                table.insert(bases, { type = "Starred", value = parse_expression(BP.COMMA) })
                            elseif match_val("**") then
                                seen_kw_unpack = true
                                table.insert(kwargs, { key = nil, value = parse_expression(BP.COMMA) })
                            elseif current.type == "IDENTIFIER" and tokens[pos+1] and tokens[pos+1].value == "=" then
                                local key_tok = consume_type("IDENTIFIER")
                                local key = key_tok.value
                                consume_val("=")
                                if seen_kw_keys[key] then
                                    Runtime.Traceback.current_line = key_tok.line
                                    Runtime.raise("SyntaxError", "keyword argument repeated: " .. key)
                                end
                                seen_kw_keys[key] = true
                                seen_keyword = true
                                table.insert(kwargs, { key = key, value = parse_expression(BP.COMMA) })
                            else
                                if seen_kw_unpack then
                                    Runtime.Traceback.current_line = current.line
                                    Runtime.raise("SyntaxError", "positional argument follows keyword argument unpacking")
                                elseif seen_keyword then
                                    Runtime.Traceback.current_line = current.line
                                    Runtime.raise("SyntaxError", "positional argument follows keyword argument")
                                end
                                table.insert(bases, parse_expression(BP.COMMA))
                            end
                            if not match_val(",") then break end
                            if current.value == ")" then break end
                        end
                    end
                    consume_val(")")
                end
                consume_val(":")
                stmt = { type = "ClassDef", name = name, bases = bases, kwargs = kwargs, body = parse_suite(), decorator_list = decorator_list, line = t.line }

            -- (import, from, global, nonlocal, assert, del, raise 保持原樣,直接跳到 with)
            elseif t.value == "import" then
                advance()
                local names = {}
                while true do
                    local name = parse_import_name(); local alias = nil
                    if match_val("as") then alias = consume_type("IDENTIFIER").value end
                    if name == "goto" then goto_enabled = true end
                    table.insert(names, { name = name, alias = alias })
                    if not match_val(",") then break end
                end
                stmt = { type = "Import", names = names, line = t.line }

            elseif t.value == "from" then
                advance()
                local module = parse_import_name(); consume_val("import")
                if module == "goto" then goto_enabled = true end
                local names = {}
                if current.value ~= "NEWLINE" and current.type ~= "NEWLINE" and current.type ~= "EOF" then
                    if current.value == "*" then
                        table.insert(names, { name = "*" })
                        advance()
                    else
                        local has_parens = match_val("(") -- 💡 偵測是否使用括號
                        
                        -- 支援 from module import () 的空括號防呆
                        if has_parens and current.value == ")" then
                            -- 空的 import 列表
                        else
                            while true do
                                local item = consume_type("IDENTIFIER").value; local alias = nil
                                if match_val("as") then alias = consume_type("IDENTIFIER").value end
                                table.insert(names, { name = item, alias = alias })
                                
                                if not match_val(",") then break end
                                -- 💡 若有括號且遇到結尾逗號,提早結束迴圈
                                if has_parens and current.value == ")" then break end
                            end
                        end
                        
                        if has_parens then
                            consume_val(")", "Expected ')' to close import list") -- 💡 消耗掉右括號
                        end
                    end
                end
                stmt = { type = "FromImport", module = module, names = names, line = t.line }

            elseif t.value == "global" or t.value == "nonlocal" then
                local kw_type = (t.value == "global") and "Global" or "Nonlocal"
                advance()
                local names = { consume_type("IDENTIFIER", "Expected identifier").value }
                while match_val(",") do table.insert(names, consume_type("IDENTIFIER").value) end
                stmt = { type = kw_type, names = names, line = t.line }
                
            elseif t.value == "assert" then
                advance()
                local test = parse_expression(); local msg = nil
                if match_val(",") then msg = parse_expression() end
                stmt = { type = "Assert", test = test, msg = msg, line = t.line }
                
            elseif t.value == "del" then
                advance()
                local targets = { parse_expression(BP.COMMA) }
                while match_val(",") do table.insert(targets, parse_expression(BP.COMMA)) end
                stmt = { type = "Delete", targets = targets, line = t.line }
                
            elseif t.value == "raise" then
                advance()
                local exc = nil; local cause = nil
                if current.type ~= "NEWLINE" and current.type ~= "EOF" then
                    exc = parse_expression()
                    if match_val("from") then cause = parse_expression() end
                end
                stmt = { type = "Raise", exc = exc, cause = cause, line = t.line }
                
            elseif t.value == "with" then
                advance()
                local items = {}
                while true do
                    local context_expr = parse_expression(BP.COMMA); local optional_vars = nil
                    if match_val("as") then optional_vars = parse_expression(BP.COMMA) end
                    table.insert(items, { context_expr = context_expr, optional_vars = optional_vars })
                    if not match_val(",") then break end
                end
                consume_val(":")
                stmt = { type = "With", items = items, body = parse_suite(), line = t.line }
                
            elseif t.value == "return" then
                advance()
                if #current_func_stack == 0 and parse_mode ~= "invoke" then
                    Runtime.Traceback.current_line = t.line
                    Runtime.raise("SyntaxError", "'return' outside function")
                end
                local val = nil
                if current.type ~= "NEWLINE" and current.type ~= "EOF" then val = parse_expression() end
                stmt = { type = "Return", value = val, line = t.line }
                
            elseif t.value == "break" then advance(); stmt = { type = "Break", line = t.line }
            elseif t.value == "continue" then advance(); stmt = { type = "Continue", line = t.line }
            elseif t.value == "pass" then advance(); stmt = { type = "Pass", line = t.line }
            end
        
        elseif t.type == "IDENTIFIER" and t.value == "match" and tokens[pos+1] and tokens[pos+1].value ~= "=" and tokens[pos+1].value ~= "(" and tokens[pos+1].value ~= "." then
            local function validate_match_pattern(pat)
                if type(pat) ~= "table" then return end
                if pat.type == "Dict" then
                    local seen_star = false
                    for i, p in ipairs(pat.pairs or {}) do
                        if p.key == nil then
                            if seen_star or i < #pat.pairs then
                                Runtime.Traceback.current_line = p.line or pat.line or current.line
                                Runtime.raise("SyntaxError", "double star pattern must be the last entry in a mapping pattern")
                            end
                            if type(p.value) ~= "table" or p.value.type ~= "Identifier" or p.value.name == "_" then
                                Runtime.Traceback.current_line = p.line or pat.line or current.line
                                Runtime.raise("SyntaxError", "cannot use '_' or non-identifier as a target of '**' in mapping pattern")
                            end
                            seen_star = true
                        else
                            validate_match_pattern(p.value)
                        end
                    end
                elseif pat.type == "Tuple" or pat.type == "List" then
                    for _, el in ipairs(pat.elements or {}) do validate_match_pattern(el) end
                elseif pat.type == "Call" then
                    for _, a in ipairs(pat.args or {}) do validate_match_pattern(a) end
                    for _, kw in ipairs(pat.kwargs or {}) do validate_match_pattern(kw.value) end
                elseif pat.type == "BinaryOp" and pat.op == "|" then
                    validate_match_pattern(pat.left)
                    validate_match_pattern(pat.right)
                elseif pat.type == "MatchAs" then
                    validate_match_pattern(pat.pattern)
                end
            end

            advance()
            local subject = parse_expression()
            consume_val(":")
            consume_type("NEWLINE")
            consume_type("INDENT")
            local cases = {}
            while current.type == "IDENTIFIER" and current.value == "case" do
                advance()
                local old_if_bp = LBP["if"]
                local old_as_bp = LBP["as"]
                LBP["if"] = -1
                LBP["as"] = 18
                local pattern = parse_expression()
                LBP["if"] = old_if_bp
                LBP["as"] = old_as_bp
                validate_match_pattern(pattern)
                local guard = nil
                if match_val("if") then guard = parse_expression() end
                consume_val(":")
                local body = parse_suite()
                table.insert(cases, { pattern = pattern, guard = guard, body = body })
            end
            consume_type("DEDENT")
            stmt = { type = "Match", subject = subject, cases = cases, line = t.line }
            
        elseif t.type == "IDENTIFIER" and (t.value == "goto" or t.value == "label") and tokens[pos+1] and tokens[pos+1].type == "IDENTIFIER" then
            if not goto_enabled then
                Runtime.Traceback.current_line = t.line
                Runtime.raise("SyntaxError", "invalid syntax (goto/label requires 'import goto')")
            end
            local keyword = t.value
            advance() -- 消耗 goto/label
            local target_name = consume_type("IDENTIFIER", "Expected label name after " .. keyword).value
            stmt = { type = (keyword == "goto" and "Goto" or "Label"), target = target_name, line = t.line }
        end

        if not stmt then
            local expr = parse_expression()
            if current.value == ":" then
                if expr.type == "NamedExpr" then
                    Runtime.Traceback.current_line = expr.line or current.line
                    Runtime.raise("SyntaxError", "cannot assign to named expression")
                end
                advance() -- 消耗 ':'
                -- 【關鍵修復】:傳入 BP.ASSIGN,強制 Parser 在遇到 '=' 時停止解析,避免吃掉預設值
                local annotation = parse_expression(BP.ASSIGN) 
                local value = nil
                if current.value == "=" then
                    advance() -- 消耗 '='
                    value = parse_expression()
                end
                stmt = { type = "AnnAssign", target = expr, annotation = annotation, value = value, line = t.line }
            else
                if expr.type == "NamedExpr" and not expr.parenthesized then
                    Runtime.Traceback.current_line = expr.line or current.line
                    Runtime.raise("SyntaxError", "cannot use assignment expression here (use ':=' inside parentheses or '=' for assignment)")
                end
                stmt = { type = "ExprStatement", expr = expr, line = t.line }
            end
        end
        if current.type == "NEWLINE" then advance() end
        return stmt
    end

    if parse_mode == "eval" then
        skip_newlines()
        if current.type == "EOF" then
            Runtime.Traceback.current_line = current.line
            Runtime.raise("SyntaxError", "unexpected EOF while parsing")
        end
        local expr = parse_expression()
        if expr.type == "Assign" or expr.type == "Yield" or expr.type == "YieldFrom" then
            Runtime.Traceback.current_line = expr.line or current.line
            Runtime.raise("SyntaxError", "invalid syntax")
        end
        skip_newlines()
        if current.type ~= "EOF" then
            Runtime.Traceback.current_line = current.line
            Runtime.raise("SyntaxError", "invalid syntax")
        end
        return { type = "Expression", body = expr }
    end

    local ast = { type = "Module", body = {} }
    while current.type ~= "EOF" do
        skip_newlines()
        if current.type == "EOF" then break end
        table.insert(ast.body, parse_statement())
    end

    return ast
end

return Parser