## This script was generated using Claude Sonnet 4.6 with some iterative prompting
## It requires the python libclang package (pip install libclang in a venv works nicely)
## It is run with "python3 ./ast_builder.py file_name.c"

import clang.cindex
import sys

def get_operator_token(node):
    children = list(node.get_children())
    if len(children) < 2:
        return get_tokens(node)
    child1_end = children[0].extent.end.offset
    child2_start = children[1].extent.start.offset
    tokens = list(node.get_tokens())
    for t in tokens:
        if t.extent.start.offset >= child1_end and t.extent.end.offset <= child2_start:
            return t.spelling
    return ""

def get_tokens(node):
    tokens = list(node.get_tokens())
    if tokens:
        return " ".join(t.spelling for t in tokens)
    return ""

def print_tree(node, indent=0, prefix="", is_last=True):
    connector = "└── " if is_last else "├── "
    label = node.kind.name
    if node.spelling:
        label += f": {node.spelling}"
    if node.kind.name in ("INTEGER_LITERAL", "FLOATING_LITERAL",
                           "STRING_LITERAL", "CHARACTER_LITERAL"):
        label += f" [{get_tokens(node)}]"
    elif node.kind.name in ("BINARY_OPERATOR", "COMPOUND_ASSIGNMENT_OPERATOR"):
        label += f" [{get_operator_token(node)}]"
    elif node.kind.name == "UNARY_OPERATOR":
        tokens = list(node.get_tokens())
        if tokens:
            label += f" [{tokens[0].spelling}]"

    if indent == 0:
        print(label)
    else:
        print(prefix + connector + label)

    children = list(node.get_children())
    for i, child in enumerate(children):
        is_last_child = (i == len(children) - 1)
        if indent == 0:
            extension = "    " if is_last_child else "│   "
        else:
            extension = "    " if is_last else "│   "
        print_tree(child, indent + 1, prefix + extension, is_last_child)

idx = clang.cindex.Index.create()
tu = idx.parse(sys.argv[1])
print_tree(tu.cursor)
