Research source · python

verify_typed_context_pumping.py

site/public/research-artifacts/scaffold-pumping-certificates/verify_typed_context_pumping.py

168 lines. Source is displayed for inspection; it is not executed by this page.

File fingerprint

SHA-256: 04c2252f3914bc791305e00d34e0188b06de3743752c8753285ddb8a6e08de7e

#!/usr/bin/env python3
"""Finite audit for the typed-context and visible-stack pumping note.

The unrestricted statements are proved in the accompanying note.  This
program exhausts small typed paths and Dyck derivation trees, then checks that
every extracted one-hole context remains valid when iterated.
"""

from __future__ import annotations

from dataclasses import dataclass
from functools import lru_cache
import json


@dataclass(frozen=True)
class Tree:
    bracket: int | None = None
    inside: "Tree | None" = None
    suffix: "Tree | None" = None

    @property
    def empty(self) -> bool:
        return self.bracket is None


EPSILON = Tree()
OPEN = "(["
CLOSE = ")]"


@lru_cache(maxsize=None)
def trees(pairs: int) -> tuple[Tree, ...]:
    if pairs == 0:
        return (EPSILON,)
    result: list[Tree] = []
    for inside_pairs in range(pairs):
        suffix_pairs = pairs - 1 - inside_pairs
        for bracket in range(2):
            for inside in trees(inside_pairs):
                for suffix in trees(suffix_pairs):
                    result.append(Tree(bracket, inside, suffix))
    return tuple(result)


def word(tree: Tree) -> str:
    if tree.empty:
        return ""
    assert tree.inside is not None and tree.suffix is not None
    return OPEN[tree.bracket] + word(tree.inside) + CLOSE[tree.bracket] + word(tree.suffix)


def balanced(value: str) -> bool:
    stack: list[str] = []
    matching = {")": "(", "]": "["}
    for symbol in value:
        if symbol in OPEN:
            stack.append(symbol)
        elif not stack or stack.pop() != matching[symbol]:
            return False
    return not stack


def contexts(tree: Tree) -> list[tuple[str, str, str]]:
    """Return (left, hole, right) for every proper descendant S-node."""
    if tree.empty:
        return []
    assert tree.inside is not None and tree.suffix is not None
    opening = OPEN[tree.bracket]
    closing = CLOSE[tree.bracket]
    inside_word = word(tree.inside)
    suffix_word = word(tree.suffix)
    result = [
        (opening, inside_word, closing + suffix_word),
        (opening + inside_word + closing, suffix_word, ""),
    ]
    for left, hole, right in contexts(tree.inside):
        result.append((opening + left, hole, right + closing + suffix_word))
    for left, hole, right in contexts(tree.suffix):
        result.append((opening + inside_word + closing + left, hole, right))
    return result


def run_stack(value: str) -> tuple[str, ...] | None:
    stack: list[str] = []
    matching = {")": "(", "]": "["}
    for symbol in value:
        if symbol in OPEN:
            stack.append(symbol)
        elif not stack or stack.pop() != matching[symbol]:
            return None
    return tuple(stack)


def audit() -> dict[str, object]:
    typed_path_cases = 0
    for type_count in range(1, 6):
        # Base-type expansion avoids materializing all paths at once.
        total = type_count ** (type_count + 1)
        for encoded in range(total):
            path = []
            value = encoded
            for _ in range(type_count + 1):
                path.append(value % type_count)
                value //= type_count
            assert len(set(path)) <= type_count
            typed_path_cases += 1

    derivation_trees = 0
    extracted_contexts = 0
    pumped_words = 0
    stack_runs = 0
    two_sided_contexts = 0
    one_sided_contexts = 0
    for pair_count in range(6):
        for tree in trees(pair_count):
            derivation_trees += 1
            original = word(tree)
            assert balanced(original)
            assert run_stack(original) == ()
            for left, hole, right in contexts(tree):
                extracted_contexts += 1
                assert left or right
                if left and right:
                    two_sided_contexts += 1
                else:
                    one_sided_contexts += 1
                for copies in range(5):
                    pumped = left * copies + hole + right * copies
                    assert balanced(pumped)
                    assert run_stack(pumped) == ()
                    pumped_words += 1
                    stack_runs += 1

    # The one-bracket family c^n r^n forces the paired, two-wing case.
    nested_family_checks = 0
    for depth in range(1, 17):
        for outer in range(depth):
            for inner in range(outer + 1, depth + 1):
                left = "(" * (inner - outer)
                hole = "(" * (depth - inner) + ")" * (depth - inner)
                right = ")" * (inner - outer)
                for copies in range(5):
                    assert balanced(left * copies + hole + right * copies)
                    nested_family_checks += 1

    return {
        "status": "pass",
        "scope": "typed paths through five types and all two-bracket Dyck derivation trees through five pairs",
        "typed_path_cases": typed_path_cases,
        "derivation_trees": derivation_trees,
        "extracted_contexts": extracted_contexts,
        "two_sided_contexts": two_sided_contexts,
        "one_sided_contexts": one_sided_contexts,
        "pumped_words": pumped_words,
        "persistent_stack_runs": stack_runs,
        "nested_family_checks": nested_family_checks,
        "does_not_verify": [
            "the unrestricted finite-interface pumping schema",
            "the visibly pushdown grammar characterization",
            "a pumping lemma for all scaffolding automata or PEGs",
            "novelty beyond classical tree and context-free pumping",
        ],
    }


if __name__ == "__main__":
    print(json.dumps(audit(), indent=2))