Jump to content

The Principia Machina/TPC-PY/tpc hunk.py

From The Invariance Itself
"""tpc_hunk 0.1.0 (DRAFT, pending D151): hunk_apply, the page write of a
PATCH falcon's approved hunks.

Reference implementation, Python standard library only. DATA for landing
round 8 of branch tpm_project_closeout (the falcon speculation 0.1.0
section 7's approval-form step and section 8's signature; PMP-TPM 0.12.0
FALCONS, APPROVAL AND APPLICATION; record D151.1 (b), (g)); not a
declared TPC-PY version and not TPX code. A pure function of its
arguments. Every refused argument raises ValueError with a short fixed
message. render_hunk() gives the canonical text the PHP residence
(tpc_hunk.php) reproduces byte for byte. Kept in its own module so that
tpc_falcon 0.1.1's source_match amendment (agent BC) and this function
never touch one file.

hunk_apply(base_text, hunks) -> dict
  base_text: the target page's text at the falcon's base revision, a
    string (TPX has already refused BASE MOVED, D151.1 (g)).
  hunks: the approved hunks, a list of objects {old, new} (the optional
    keys n, anchor and count of the landing rounds' hunk files are
    accepted and read by nothing), old and new strings; applied in list
    order. Anything else raises ValueError("hunks"); a base that is not a
    string raises ValueError("base").
  Result: {status: OK, applied: the hunk count, sha256: the SHA-256 of
    the result's UTF-8, text: the result}, or {status: REFUSED, reason,
    hunk: the 1-based index of the hunk refused}. TPX saves the OK text
    as the approver's own revision and reads back its SHA-1 (D151.1 (b));
    on REFUSED nothing is saved.
  Rule (the falcon speculation 8, hunk_apply: "each old string occurs
  exactly once in the base and no two hunks overlap, else REFUSED;
  applied in order"):
  HK1 EMPTY. An old string that is empty occurs at every position, never
      exactly once: REFUSED, reason EMPTY.
  HK2 EXACTLY ONCE. Each old string is located in the BASE TEXT, exactly
      as given (no normalization: the approved text was drawn from the
      same base by exact replacement, D151.1 (b)); every start position
      is counted, overlapping occurrences included, so "aa" in "aaa"
      occurs twice. None: REFUSED, reason ABSENT; two or more: REFUSED,
      reason MULTIPLE.
  HK3 NO OVERLAP. The located ranges [start, start + len(old)) of any two
      hunks are disjoint; adjacent ranges (one ending where the next
      begins) do not overlap. A hunk whose range meets an earlier hunk's
      range: REFUSED, reason OVERLAP.
  HK4 APPLICATION. Each old string is replaced by its new string at its
      located position; the ranges being disjoint in the base, the
      result is the same whichever hunk is applied first, and equals the
      sequential application of the list. Reading taken by delegation
      2026-09-26 (spec note PD-OC-3): "in order" names the sequence of
      application, not the positions: the hunk list need not be sorted
      by position (the landing rounds' hunk sets are not, and every one
      of them replays under this rule). The alternative, REFUSED with a
      reason ORDER when positions do not ascend with the list, is in the
      spec note.
  HK5 An empty hunk list gives OK with the base unchanged (spec note
      PD-OC-4; alternative: REFUSED, reason NONE). A hunk whose new
      string equals its old string is a no-op and is applied.
  The first hunk that fails HK1-HK3 in list order names the refusal;
  every hunk is located before any is applied, so a refusal changes
  nothing.
"""

import hashlib

VERSION = "0.1.0"
REASONS = ("EMPTY", "ABSENT", "MULTIPLE", "OVERLAP")


def _esc(s):
    """Render escape: "|" and "\\" backslashed; C0 controls and DEL as
    \\xHH; every other character as it is."""
    out = []
    for ch in s:
        o = ord(ch)
        if ch == "|" or ch == "\\":
            out.append("\\" + ch)
        elif o < 0x20 or o == 0x7F:
            out.append("\\x%02x" % o)
        else:
            out.append(ch)
    return "".join(out)


def _is_text(s):
    if type(s) is not str:
        return False
    for ch in s:
        if 0xD800 <= ord(ch) <= 0xDFFF:
            return False
    return True


def _check(base_text, hunks):
    if not _is_text(base_text):
        raise ValueError("base")
    if type(hunks) is not list:
        raise ValueError("hunks")
    for h in hunks:
        if type(h) is not dict or "old" not in h or "new" not in h:
            raise ValueError("hunks")
        for k in h:
            if k not in ("old", "new", "n", "anchor", "count"):
                raise ValueError("hunks")
        if not _is_text(h["old"]) or not _is_text(h["new"]):
            raise ValueError("hunks")


def count_occurrences(text, needle):
    """Every start position of a non-empty needle, overlapping included."""
    n = 0
    i = text.find(needle)
    while i != -1:
        n += 1
        i = text.find(needle, i + 1)
    return n


def hunk_apply(base_text, hunks):
    _check(base_text, hunks)
    located = []
    for idx, h in enumerate(hunks):
        old = h["old"]
        if old == "":
            return {"status": "REFUSED", "reason": "EMPTY", "hunk": idx + 1}
        c = count_occurrences(base_text, old)
        if c == 0:
            return {"status": "REFUSED", "reason": "ABSENT", "hunk": idx + 1}
        if c > 1:
            return {"status": "REFUSED", "reason": "MULTIPLE",
                    "hunk": idx + 1}
        start = base_text.find(old)
        end = start + len(old)
        for s2, e2 in located:
            if start < e2 and s2 < end:
                return {"status": "REFUSED", "reason": "OVERLAP",
                        "hunk": idx + 1}
        located.append((start, end))
    order = sorted(range(len(hunks)), key=lambda i: located[i][0])
    pieces = []
    cur = 0
    for i in order:
        start, end = located[i]
        pieces.append(base_text[cur:start])
        pieces.append(hunks[i]["new"])
        cur = end
    pieces.append(base_text[cur:])
    text = "".join(pieces)
    return {"status": "OK", "applied": len(hunks),
            "sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(),
            "text": text}


def render_hunk(d):
    if d["status"] == "REFUSED":
        return "REFUSED|%s|%d" % (d["reason"], d["hunk"])
    return "OK|%d|%s\ntext|%s" % (d["applied"], d["sha256"], _esc(d["text"]))


FNS = ("hunk",)


def run_case(fn, args):
    """One corpus case: the render, or "ERROR:" and the message."""
    if fn not in FNS:
        raise ValueError("fn")
    try:
        return render_hunk(hunk_apply(args["base"], args["hunks"]))
    except ValueError as ex:
        return "ERROR:" + str(ex)