# Generated by parsegen_code.py from parsegen JSON tables. Do not edit.
from __future__ import annotations

from dataclasses import dataclass
from typing import Optional, Protocol

from runtime.token import Token, new_token
from runtime.ast import AST, ASTNode, new_ast_node, new_ast_node_terminal
from runtime.lexer import AbstractLexer
from runtime.token import TOKEN_TYPE_EOF, TOKEN_TYPE_ERROR


class ActionKind:
    SHIFT = 0
    REDUCE = 1
    ACCEPT = 2


@dataclass
class Action:
    kind: int
    target: int


@dataclass
class Production:
    lhs: str
    rhs_count: int
    has_hint: bool = False
    has_passthrough: bool = False
    has_parent_literal: bool = False
    has_with_appended_children: bool = False
    has_with_prepended_children: bool = False
    has_with_adopted_grandchildren: bool = False
    parent_index: int = 0
    passthrough_index: int = 0
    parent_literal: str = ""
    child_indices: list[int] = None
    with_appended_children: list[int] = None
    with_prepended_children: list[int] = None
    with_adopted_grandchildren: list[int] = None
    node_type: str = ""

    def __post_init__(self) -> None:
        if self.child_indices is None:
            self.child_indices = []
        if self.with_appended_children is None:
            self.with_appended_children = []
        if self.with_prepended_children is None:
            self.with_prepended_children = []
        if self.with_adopted_grandchildren is None:
            self.with_adopted_grandchildren = []


# No-AST sentinel for ast_mode "noast"
_pgpg_JSONParser_NO_AST_SENTINEL = ASTNode(token=None, type="", children=[])

ACTIONS: dict[int, dict[str, Action]] = {
    0: {
        "false": Action(kind=ActionKind.SHIFT, target=5),
        "lbracket": Action(kind=ActionKind.SHIFT, target=6),
        "lcurly": Action(kind=ActionKind.SHIFT, target=7),
        "null": Action(kind=ActionKind.SHIFT, target=8),
        "number": Action(kind=ActionKind.SHIFT, target=9),
        "string": Action(kind=ActionKind.SHIFT, target=10),
        "true": Action(kind=ActionKind.SHIFT, target=11),
    },
    1: {
        "EOF": Action(kind=ActionKind.REDUCE, target=3),
    },
    2: {
        "EOF": Action(kind=ActionKind.ACCEPT, target=0),
    },
    3: {
        "EOF": Action(kind=ActionKind.REDUCE, target=2),
    },
    4: {
        "EOF": Action(kind=ActionKind.REDUCE, target=1),
    },
    5: {
        "EOF": Action(kind=ActionKind.REDUCE, target=7),
    },
    6: {
        "false": Action(kind=ActionKind.SHIFT, target=16),
        "lbracket": Action(kind=ActionKind.SHIFT, target=17),
        "lcurly": Action(kind=ActionKind.SHIFT, target=18),
        "null": Action(kind=ActionKind.SHIFT, target=19),
        "number": Action(kind=ActionKind.SHIFT, target=20),
        "rbracket": Action(kind=ActionKind.SHIFT, target=21),
        "string": Action(kind=ActionKind.SHIFT, target=22),
        "true": Action(kind=ActionKind.SHIFT, target=23),
    },
    7: {
        "rcurly": Action(kind=ActionKind.SHIFT, target=26),
        "string": Action(kind=ActionKind.SHIFT, target=27),
    },
    8: {
        "EOF": Action(kind=ActionKind.REDUCE, target=8),
    },
    9: {
        "EOF": Action(kind=ActionKind.REDUCE, target=5),
    },
    10: {
        "EOF": Action(kind=ActionKind.REDUCE, target=4),
    },
    11: {
        "EOF": Action(kind=ActionKind.REDUCE, target=6),
    },
    12: {
        "comma": Action(kind=ActionKind.REDUCE, target=3),
        "rbracket": Action(kind=ActionKind.REDUCE, target=3),
    },
    13: {
        "comma": Action(kind=ActionKind.SHIFT, target=28),
        "rbracket": Action(kind=ActionKind.SHIFT, target=29),
    },
    14: {
        "comma": Action(kind=ActionKind.REDUCE, target=2),
        "rbracket": Action(kind=ActionKind.REDUCE, target=2),
    },
    15: {
        "comma": Action(kind=ActionKind.REDUCE, target=16),
        "rbracket": Action(kind=ActionKind.REDUCE, target=16),
    },
    16: {
        "comma": Action(kind=ActionKind.REDUCE, target=7),
        "rbracket": Action(kind=ActionKind.REDUCE, target=7),
    },
    17: {
        "false": Action(kind=ActionKind.SHIFT, target=16),
        "lbracket": Action(kind=ActionKind.SHIFT, target=17),
        "lcurly": Action(kind=ActionKind.SHIFT, target=18),
        "null": Action(kind=ActionKind.SHIFT, target=19),
        "number": Action(kind=ActionKind.SHIFT, target=20),
        "rbracket": Action(kind=ActionKind.SHIFT, target=31),
        "string": Action(kind=ActionKind.SHIFT, target=22),
        "true": Action(kind=ActionKind.SHIFT, target=23),
    },
    18: {
        "rcurly": Action(kind=ActionKind.SHIFT, target=33),
        "string": Action(kind=ActionKind.SHIFT, target=27),
    },
    19: {
        "comma": Action(kind=ActionKind.REDUCE, target=8),
        "rbracket": Action(kind=ActionKind.REDUCE, target=8),
    },
    20: {
        "comma": Action(kind=ActionKind.REDUCE, target=5),
        "rbracket": Action(kind=ActionKind.REDUCE, target=5),
    },
    21: {
        "EOF": Action(kind=ActionKind.REDUCE, target=14),
    },
    22: {
        "comma": Action(kind=ActionKind.REDUCE, target=4),
        "rbracket": Action(kind=ActionKind.REDUCE, target=4),
    },
    23: {
        "comma": Action(kind=ActionKind.REDUCE, target=6),
        "rbracket": Action(kind=ActionKind.REDUCE, target=6),
    },
    24: {
        "comma": Action(kind=ActionKind.REDUCE, target=11),
        "rcurly": Action(kind=ActionKind.REDUCE, target=11),
    },
    25: {
        "comma": Action(kind=ActionKind.SHIFT, target=34),
        "rcurly": Action(kind=ActionKind.SHIFT, target=35),
    },
    26: {
        "EOF": Action(kind=ActionKind.REDUCE, target=9),
    },
    27: {
        "colon": Action(kind=ActionKind.SHIFT, target=36),
    },
    28: {
        "false": Action(kind=ActionKind.SHIFT, target=16),
        "lbracket": Action(kind=ActionKind.SHIFT, target=17),
        "lcurly": Action(kind=ActionKind.SHIFT, target=18),
        "null": Action(kind=ActionKind.SHIFT, target=19),
        "number": Action(kind=ActionKind.SHIFT, target=20),
        "string": Action(kind=ActionKind.SHIFT, target=22),
        "true": Action(kind=ActionKind.SHIFT, target=23),
    },
    29: {
        "EOF": Action(kind=ActionKind.REDUCE, target=15),
    },
    30: {
        "comma": Action(kind=ActionKind.SHIFT, target=28),
        "rbracket": Action(kind=ActionKind.SHIFT, target=38),
    },
    31: {
        "comma": Action(kind=ActionKind.REDUCE, target=14),
        "rbracket": Action(kind=ActionKind.REDUCE, target=14),
    },
    32: {
        "comma": Action(kind=ActionKind.SHIFT, target=34),
        "rcurly": Action(kind=ActionKind.SHIFT, target=39),
    },
    33: {
        "comma": Action(kind=ActionKind.REDUCE, target=9),
        "rbracket": Action(kind=ActionKind.REDUCE, target=9),
    },
    34: {
        "string": Action(kind=ActionKind.SHIFT, target=27),
    },
    35: {
        "EOF": Action(kind=ActionKind.REDUCE, target=10),
    },
    36: {
        "false": Action(kind=ActionKind.SHIFT, target=44),
        "lbracket": Action(kind=ActionKind.SHIFT, target=45),
        "lcurly": Action(kind=ActionKind.SHIFT, target=46),
        "null": Action(kind=ActionKind.SHIFT, target=47),
        "number": Action(kind=ActionKind.SHIFT, target=48),
        "string": Action(kind=ActionKind.SHIFT, target=49),
        "true": Action(kind=ActionKind.SHIFT, target=50),
    },
    37: {
        "comma": Action(kind=ActionKind.REDUCE, target=17),
        "rbracket": Action(kind=ActionKind.REDUCE, target=17),
    },
    38: {
        "comma": Action(kind=ActionKind.REDUCE, target=15),
        "rbracket": Action(kind=ActionKind.REDUCE, target=15),
    },
    39: {
        "comma": Action(kind=ActionKind.REDUCE, target=10),
        "rbracket": Action(kind=ActionKind.REDUCE, target=10),
    },
    40: {
        "comma": Action(kind=ActionKind.REDUCE, target=12),
        "rcurly": Action(kind=ActionKind.REDUCE, target=12),
    },
    41: {
        "comma": Action(kind=ActionKind.REDUCE, target=3),
        "rcurly": Action(kind=ActionKind.REDUCE, target=3),
    },
    42: {
        "comma": Action(kind=ActionKind.REDUCE, target=2),
        "rcurly": Action(kind=ActionKind.REDUCE, target=2),
    },
    43: {
        "comma": Action(kind=ActionKind.REDUCE, target=13),
        "rcurly": Action(kind=ActionKind.REDUCE, target=13),
    },
    44: {
        "comma": Action(kind=ActionKind.REDUCE, target=7),
        "rcurly": Action(kind=ActionKind.REDUCE, target=7),
    },
    45: {
        "false": Action(kind=ActionKind.SHIFT, target=16),
        "lbracket": Action(kind=ActionKind.SHIFT, target=17),
        "lcurly": Action(kind=ActionKind.SHIFT, target=18),
        "null": Action(kind=ActionKind.SHIFT, target=19),
        "number": Action(kind=ActionKind.SHIFT, target=20),
        "rbracket": Action(kind=ActionKind.SHIFT, target=52),
        "string": Action(kind=ActionKind.SHIFT, target=22),
        "true": Action(kind=ActionKind.SHIFT, target=23),
    },
    46: {
        "rcurly": Action(kind=ActionKind.SHIFT, target=54),
        "string": Action(kind=ActionKind.SHIFT, target=27),
    },
    47: {
        "comma": Action(kind=ActionKind.REDUCE, target=8),
        "rcurly": Action(kind=ActionKind.REDUCE, target=8),
    },
    48: {
        "comma": Action(kind=ActionKind.REDUCE, target=5),
        "rcurly": Action(kind=ActionKind.REDUCE, target=5),
    },
    49: {
        "comma": Action(kind=ActionKind.REDUCE, target=4),
        "rcurly": Action(kind=ActionKind.REDUCE, target=4),
    },
    50: {
        "comma": Action(kind=ActionKind.REDUCE, target=6),
        "rcurly": Action(kind=ActionKind.REDUCE, target=6),
    },
    51: {
        "comma": Action(kind=ActionKind.SHIFT, target=28),
        "rbracket": Action(kind=ActionKind.SHIFT, target=55),
    },
    52: {
        "comma": Action(kind=ActionKind.REDUCE, target=14),
        "rcurly": Action(kind=ActionKind.REDUCE, target=14),
    },
    53: {
        "comma": Action(kind=ActionKind.SHIFT, target=34),
        "rcurly": Action(kind=ActionKind.SHIFT, target=56),
    },
    54: {
        "comma": Action(kind=ActionKind.REDUCE, target=9),
        "rcurly": Action(kind=ActionKind.REDUCE, target=9),
    },
    55: {
        "comma": Action(kind=ActionKind.REDUCE, target=15),
        "rcurly": Action(kind=ActionKind.REDUCE, target=15),
    },
    56: {
        "comma": Action(kind=ActionKind.REDUCE, target=10),
        "rcurly": Action(kind=ActionKind.REDUCE, target=10),
    },
}

GOTOS: dict[int, dict[str, int]] = {
    0: {
        "Array": 1,
        "Json": 2,
        "Object": 3,
        "Value": 4,
    },
    6: {
        "Array": 12,
        "Elements": 13,
        "Object": 14,
        "Value": 15,
    },
    7: {
        "Member": 24,
        "Members": 25,
    },
    17: {
        "Array": 12,
        "Elements": 30,
        "Object": 14,
        "Value": 15,
    },
    18: {
        "Member": 24,
        "Members": 32,
    },
    28: {
        "Array": 12,
        "Object": 14,
        "Value": 37,
    },
    34: {
        "Member": 40,
    },
    36: {
        "Array": 41,
        "Object": 42,
        "Value": 43,
    },
    45: {
        "Array": 12,
        "Elements": 51,
        "Object": 14,
        "Value": 15,
    },
    46: {
        "Member": 24,
        "Members": 53,
    },
}

PRODUCTIONS: list[Production] = [
    Production(
        lhs="__pgpg_start_1",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Json",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Value",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Value",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Value",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Value",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Value",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Value",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Value",
        rhs_count=1,
        has_hint=False,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Object",
        rhs_count=2,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=True,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="{}",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="object",
    ),
    Production(
        lhs="Object",
        rhs_count=3,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=True,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=True,
        parent_index=0,
        passthrough_index=0,
        parent_literal="{}",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[1],
        node_type="object",
    ),
    Production(
        lhs="Members",
        rhs_count=1,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=True,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="{temp}",
        child_indices=[0],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Members",
        rhs_count=3,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=True,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[2],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Member",
        rhs_count=3,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=1,
        passthrough_index=0,
        parent_literal="",
        child_indices=[0, 2],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Array",
        rhs_count=2,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=True,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="[]",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="array",
    ),
    Production(
        lhs="Array",
        rhs_count=3,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=True,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=True,
        parent_index=0,
        passthrough_index=0,
        parent_literal="[]",
        child_indices=[],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[1],
        node_type="array",
    ),
    Production(
        lhs="Elements",
        rhs_count=1,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=True,
        has_with_appended_children=False,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="[temp]",
        child_indices=[0],
        with_appended_children=[],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
    Production(
        lhs="Elements",
        rhs_count=3,
        has_hint=True,
        has_passthrough=False,
        has_parent_literal=False,
        has_with_appended_children=True,
        has_with_prepended_children=False,
        has_with_adopted_grandchildren=False,
        parent_index=0,
        passthrough_index=0,
        parent_literal="",
        child_indices=[],
        with_appended_children=[2],
        with_prepended_children=[],
        with_adopted_grandchildren=[],
        node_type="",
    ),
]


class TraceHooks:
    on_token: Optional[callable] = None
    on_action: Optional[callable] = None
    on_stack: Optional[callable] = None


class pgpg_JSONParser:
    def __init__(self) -> None:
        self.trace: Optional[TraceHooks] = None

    def parse(
        self,
        lexer: AbstractLexer,
        ast_mode: str = "",
    ) -> Optional[AST]:
        if lexer is None:
            raise ValueError("parser: nil lexer")
        state_stack: list[int] = [0]
        node_stack: list[ASTNode] = []
        lookahead = lexer.scan()
        if self.trace and self.trace.on_token and lookahead:
            self.trace.on_token(lookahead)
        while True:
            if lookahead is None:
                raise ValueError("parser: lexer returned None token")
            if lookahead.type == TOKEN_TYPE_ERROR:
                raise ValueError(f"lexer error: {lookahead.lexeme}")
            state = state_stack[-1]
            state_actions = ACTIONS.get(state, {})
            action = state_actions.get(lookahead.type)
            if action is None:
                raise ValueError(
                    f"parse error: unexpected {lookahead.type!r} ({lookahead.lexeme!r})"
                )
            if self.trace and self.trace.on_action:
                self.trace.on_action(state, action, lookahead)
            if action.kind == ActionKind.SHIFT:
                if ast_mode == "noast":
                    node_stack.append(_pgpg_JSONParser_NO_AST_SENTINEL)
                else:
                    node_stack.append(new_ast_node_terminal(lookahead, lookahead.type))
                state_stack.append(action.target)
                lookahead = lexer.scan()
                if self.trace and self.trace.on_token and lookahead:
                    self.trace.on_token(lookahead)
                if self.trace and self.trace.on_stack:
                    self.trace.on_stack(state_stack, node_stack)
            elif action.kind == ActionKind.REDUCE:
                prod = PRODUCTIONS[action.target]
                rhs_nodes: list[ASTNode] = [None] * prod.rhs_count  # type: ignore
                for i in range(prod.rhs_count - 1, -1, -1):
                    state_stack.pop()
                    rhs_nodes[i] = node_stack.pop()
                if ast_mode == "noast":
                    node_stack.append(_pgpg_JSONParser_NO_AST_SENTINEL)
                else:
                    node = _reduce_pgpg_JSONParser(prod, rhs_nodes, ast_mode)
                    node_stack.append(node)
                state = state_stack[-1]
                state_gotos = GOTOS.get(state, {})
                next_state = state_gotos.get(prod.lhs)
                if next_state is None:
                    raise ValueError(f"parse error: missing goto for {prod.lhs}")
                state_stack.append(next_state)
                if self.trace and self.trace.on_stack:
                    self.trace.on_stack(state_stack, node_stack)
            else:  # ACCEPT
                if len(node_stack) != 1:
                    raise ValueError(
                        f"parse error: unexpected parse stack size {len(node_stack)}"
                    )
                if self.trace and self.trace.on_stack:
                    self.trace.on_stack(state_stack, node_stack)
                if ast_mode == "noast":
                    return None
                return AST(root_node=node_stack[0])

    def attach_cli_trace(
        self,
        trace_tokens: bool = False,
        trace_states: bool = False,
        trace_stack: bool = False,
    ) -> None:
        if not (trace_tokens or trace_states or trace_stack):
            return
        import sys

        self.trace = TraceHooks()
        if trace_tokens:

            def on_token(tok: Optional[Token]) -> None:
                if tok:
                    loc = (
                        f" line={tok.location.line} col={tok.location.column}"
                        if tok.location
                        else ""
                    )
                    print(
                        f"TOK type={tok.type} lexeme={tok.lexeme!r}{loc}",
                        file=sys.stderr,
                    )

            self.trace.on_token = on_token
        if trace_states:

            def on_action(
                state: int, action: Action, lookahead: Optional[Token]
            ) -> None:
                kind_str = (
                    "shift"
                    if action.kind == ActionKind.SHIFT
                    else ("reduce" if action.kind == ActionKind.REDUCE else "accept")
                )
                target = (
                    f"({action.target})" if action.kind != ActionKind.ACCEPT else ""
                )
                la_type = lookahead.type if lookahead else ""
                la_lex = lookahead.lexeme if lookahead else ""
                print(
                    f"STATE {state} {kind_str}{target} on {la_type}({la_lex!r})",
                    file=sys.stderr,
                )

            self.trace.on_action = on_action
        if trace_stack:

            def on_stack(state_stack: list[int], node_stack: list[ASTNode]) -> None:
                states = "[" + " ".join(str(s) for s in state_stack) + "]"
                nodes = "[" + " ".join(n.type for n in node_stack) + "]"
                print(f"STACK states={states} nodes={nodes}", file=sys.stderr)

            self.trace.on_stack = on_stack


def _reduce_pgpg_JSONParser(
    prod: Production, rhs_nodes: list[ASTNode], ast_mode: str
) -> ASTNode:
    use_full_tree = ast_mode == "fullast"
    if not use_full_tree and prod.has_passthrough:
        return rhs_nodes[prod.passthrough_index]

    if not use_full_tree and prod.has_with_appended_children:
        if prod.has_parent_literal:
            parent_token = new_token(prod.parent_literal, prod.parent_literal)
            parent_type = prod.parent_literal
            parent = None
        else:
            parent = rhs_nodes[prod.parent_index]
            parent_token = parent.token
            parent_type = parent.type
        node_type = prod.node_type or parent_type
        new_children: list[ASTNode] = []
        if parent is not None and parent.children:
            new_children.extend(parent.children)
        for ci in prod.with_appended_children:
            new_children.append(rhs_nodes[ci])
        return new_ast_node(parent_token, node_type, new_children)
    if not use_full_tree and prod.has_with_prepended_children:
        if prod.has_parent_literal:
            parent_token = new_token(prod.parent_literal, prod.parent_literal)
            parent_type = prod.parent_literal
            parent = None
        else:
            parent = rhs_nodes[prod.parent_index]
            parent_token = parent.token
            parent_type = parent.type
        node_type = prod.node_type or parent_type
        new_children = []
        for ci in prod.with_prepended_children:
            new_children.append(rhs_nodes[ci])
        if parent is not None and parent.children:
            new_children.extend(parent.children)
        return new_ast_node(parent_token, node_type, new_children)
    if not use_full_tree and prod.has_with_adopted_grandchildren:
        if prod.has_parent_literal:
            parent_token = new_token(prod.parent_literal, prod.parent_literal)
            parent_type = prod.parent_literal
            parent = None
        else:
            parent = rhs_nodes[prod.parent_index]
            parent_token = parent.token
            parent_type = parent.type
        node_type = prod.node_type or parent_type
        new_children = []
        for ci in prod.with_adopted_grandchildren:
            child_node = rhs_nodes[ci]
            if child_node is not None and child_node.children:
                new_children.extend(child_node.children)
        return new_ast_node(parent_token, node_type, new_children)
    if not use_full_tree and prod.has_hint:
        node_type = prod.node_type or prod.lhs
        if prod.has_parent_literal:
            parent_token = new_token(prod.parent_literal, prod.parent_literal)
        elif (
            0 <= prod.parent_index < len(rhs_nodes)
            and rhs_nodes[prod.parent_index].token
        ):
            parent_token = rhs_nodes[prod.parent_index].token
        else:
            parent_token = None
        hint_children = [rhs_nodes[ci] for ci in prod.child_indices]
        return new_ast_node(parent_token, node_type, hint_children)

    if prod.rhs_count == 1:
        return rhs_nodes[0]
    if prod.rhs_count == 0:
        return new_ast_node(None, prod.lhs, [])
    return new_ast_node(None, prod.lhs, rhs_nodes)
