From 140ebfc216061bb9218493c1c14e275dd0942d41 Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Tue, 2 Dec 2025 07:56:39 +0000 Subject: [PATCH] TM_ND_A and TM_ND_DB machines --- shared/authored/dir-walk/JSON.py | 44 ++++- .../dir-walk/{TapeMachine.py => TM_ND_A.py} | 0 shared/authored/dir-walk/TM_ND_A_DB.py | 164 ++++++++++++++++++ shared/authored/dir-walk/meta.py | 2 +- shared/authored/dir-walk/temp.py | 98 ----------- 5 files changed, 208 insertions(+), 100 deletions(-) rename shared/authored/dir-walk/{TapeMachine.py => TM_ND_A.py} (100%) create mode 100755 shared/authored/dir-walk/TM_ND_A_DB.py delete mode 100644 shared/authored/dir-walk/temp.py diff --git a/shared/authored/dir-walk/JSON.py b/shared/authored/dir-walk/JSON.py index f944cfb..f69716d 100755 --- a/shared/authored/dir-walk/JSON.py +++ b/shared/authored/dir-walk/JSON.py @@ -3,13 +3,55 @@ from __future__ import annotations from typing import Any +import sys import json as pyjson +import meta import duck -import TapeMachine +import TM_ND_A import builtins p = builtins.print +COLOR = { + "RESET" : "\033[0m" + ,"OP" : "\033[38;5;39m" + ,"RET" : "\033[38;5;82m" +} + +def _color(key: str) -> str: + """ + Return ANSI color code for key if COLOR debug is enabled and + stdout is a TTY, otherwise empty string. + """ + if "COLOR" not in meta.DEBUG: return "" + if not sys.stdout.isatty(): return "" + return COLOR.get(key ,"") + + +# ---------------------------------------------------------------------- +def debug( + msg: str + ,value: Any = None + ,indent: int = 0 +) -> None: + """ + Single-line debug helper: + + - If value is None: "op(args)" + - Else: "op(args) -> value" + """ + if "TM_ND_A_DB" not in meta.DEBUG: + return + + prefix = " " * indent + op = f"{_color('OP')}{msg}{_color('RESET')}" + if value is None: + out = op + else: + rv = f"{_color('RET')}{value}{_color('RESET')}" + out = f"{op} -> {rv}" + + builtins.print(prefix + out) # ---------------------------------------------------------------------- def to_JSON_TM( diff --git a/shared/authored/dir-walk/TapeMachine.py b/shared/authored/dir-walk/TM_ND_A.py similarity index 100% rename from shared/authored/dir-walk/TapeMachine.py rename to shared/authored/dir-walk/TM_ND_A.py diff --git a/shared/authored/dir-walk/TM_ND_A_DB.py b/shared/authored/dir-walk/TM_ND_A_DB.py new file mode 100755 index 0000000..33d9ae9 --- /dev/null +++ b/shared/authored/dir-walk/TM_ND_A_DB.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +from __future__ import annotations +from typing import Any + +import TM_ND_A +import JSON +import meta + + +# ---------------------------------------------------------------------- +_STACK_DEPTH = 0 + +def _indent() -> int: + return _STACK_DEPTH * 2 + + +# ---------------------------------------------------------------------- +class TM_ND_A_DB: + """ + Debug wrapper for TM_ND_A. + + For each method: + + - Calls the underlying TM_ND_A method. + - Emits a single-line debug trace via JSON.debug: + + name(args) -> result (for methods with a result) + name(args) (for void-returning methods) + + Nested calls (like entangle() creating a new TM_ND_A_DB) are + indented by stack depth. + """ + + # -------------------------------------------------------------------- + def __init__(self ,tape: Any): + global _STACK_DEPTH + _STACK_DEPTH += 1 + self._tm = TM_ND_A.TM_ND_A(tape) + self._id = f"{id(self)}" + JSON.debug(f"{self._id} __init__({tape})" ,value="" ,indent=_indent()) + _STACK_DEPTH -= 1 + + # -------------------------------------------------------------------- + def extent(self) -> int: + global _STACK_DEPTH + _STACK_DEPTH += 1 + r = self._tm.extent() + JSON.debug(f"{self._id} extent()" ,value=r ,indent=_indent()) + _STACK_DEPTH -= 1 + return r + + def tape(self) -> Any: + global _STACK_DEPTH + _STACK_DEPTH += 1 + r = self._tm.tape() + JSON.debug(f"{self._id} tape()" ,value=r ,indent=_indent()) + _STACK_DEPTH -= 1 + return r + + def where(self) -> int: + global _STACK_DEPTH + _STACK_DEPTH += 1 + r = self._tm.where() + JSON.debug(f"{self._id} where()" ,value=r ,indent=_indent()) + _STACK_DEPTH -= 1 + return r + + # -------------------------------------------------------------------- + def cue_leftmost(self) -> None: + global _STACK_DEPTH + _STACK_DEPTH += 1 + self._tm.cue_leftmost() + JSON.debug(f"{self._id} cue_leftmost()" ,value=None ,indent=_indent()) + _STACK_DEPTH -= 1 + + def cue_rightmost(self) -> None: + global _STACK_DEPTH + _STACK_DEPTH += 1 + self._tm.cue_rightmost() + JSON.debug(f"{self._id} cue_rightmost()" ,value=None ,indent=_indent()) + _STACK_DEPTH -= 1 + + def cue(self ,new_position: int) -> int: + global _STACK_DEPTH + _STACK_DEPTH += 1 + r = self._tm.cue(new_position) + JSON.debug(f"{self._id} cue({new_position})" ,value=r ,indent=_indent()) + _STACK_DEPTH -= 1 + return r + + # -------------------------------------------------------------------- + def can_step(self ,n: int = 1) -> bool: + global _STACK_DEPTH + _STACK_DEPTH += 1 + r = self._tm.can_step(n) + JSON.debug(f"{self._id} can_step({n})" ,value=r ,indent=_indent()) + _STACK_DEPTH -= 1 + return r + + def step(self ,n: int = 1) -> None: + global _STACK_DEPTH + _STACK_DEPTH += 1 + self._tm.step(n) + JSON.debug(f"{self._id} step({n})" ,value=None ,indent=_indent()) + _STACK_DEPTH -= 1 + + # -------------------------------------------------------------------- + def read(self ,n: int = 0) -> Any: + global _STACK_DEPTH + _STACK_DEPTH += 1 + r = self._tm.read(n) + JSON.debug(f"{self._id} read({n})" ,value=r ,indent=_indent()) + _STACK_DEPTH -= 1 + return r + + def write(self ,value: Any ,n: int = 0) -> None: + global _STACK_DEPTH + _STACK_DEPTH += 1 + self._tm.write(value ,n) + JSON.debug(f"{self._id} write(n={n}, value={value})" ,value=None ,indent=_indent()) + _STACK_DEPTH -= 1 + + # -------------------------------------------------------------------- + def entangle(self) -> "TM_ND_A_DB": + global _STACK_DEPTH + _STACK_DEPTH += 1 + + # Child __init__() will run its own debug line with its ID. + child = TM_ND_A_DB(self._tm.tape()) + + # Copy state + child._tm._tape = self._tm._tape + child._tm._extent = self._tm._extent + child._tm._head_position = self._tm._head_position + + # Parent reports child ID as return value + JSON.debug(f"{self._id} entangle()" ,value=child._id ,indent=_indent()) + + _STACK_DEPTH -= 1 + return child + +# ---------------------------------------------------------------------- +def CLI() -> None: + meta.DEBUG.add("TM_ND_A_DB") + + JSON.print("=== TM_ND_A_DB DEBUG DEMO ===") + + tm = TM_ND_A_DB([10 ,20 ,30]) + + tm.extent() + tm.read() + tm.step() + tm.read() + tm.write(777) + tm.entangle() + + JSON.print("=== END DEBUG DEMO ===") + + +# ---------------------------------------------------------------------- +if __name__ == "__main__": + CLI() diff --git a/shared/authored/dir-walk/meta.py b/shared/authored/dir-walk/meta.py index 299aef7..e4f8bd8 100644 --- a/shared/authored/dir-walk/meta.py +++ b/shared/authored/dir-walk/meta.py @@ -17,7 +17,7 @@ _MINOR = 2 # "TreeMachine" -> enable debug for TreeMachine operations. # DEBUG: set[str] = set() - +#DEBUG.add("COLOR") def version() -> None: """ diff --git a/shared/authored/dir-walk/temp.py b/shared/authored/dir-walk/temp.py deleted file mode 100644 index c546377..0000000 --- a/shared/authored/dir-walk/temp.py +++ /dev/null @@ -1,98 +0,0 @@ - - to_JSON: - For each cell of the tape: - 1. If the value has a to_JSON() method, use value.to_JSON(). - 2. If the value is a JSON primitive (str,int,float,bool,None), output it directly. - 3. If the value is a Python primitive that is not a JSON primitive, stringify it - with JSON-safe escaping. - 4. Otherwise: error — cannot represent in JSON. - - # ---------------------------------------------------------------------- - # JSON representation - # ---------------------------------------------------------------------- - - @classmethod - def is_a_JSON_primitive( - cls - ,value: Any - ) -> bool: - return ( - isinstance(value ,(str ,int ,float ,bool)) - or value is None - ) - - @classmethod - def has_to_JSON( - cls - ,value: Any - ) -> bool: - return hasattr(value ,"to_JSON") and callable(value.to_JSON) - - @classmethod - def normalize_value( - cls - ,value: Any - ) -> Any: - """ - Normalize a tape cell value into JSON-compatible output. - - Rules: - 1. If the value has to_JSON, call it. - 2. If the value is a JSON primitive, return it. - 3. If the value is a Python primitive but not JSON-safe, stringify it with escaping. - 4. Else: raise TypeError. - """ - # Rule 1: to_JSON - if cls.has_to_JSON(value): - return value.to_JSON() - - # Rule 2: JSON primitive - if cls.is_a_JSON_primitive(value): - return value - - # Rule 3: Python primitive → stringify - # Note: repr() is safe because JSONPretty will escape the resulting string. - if isinstance(value ,(bytes ,bytearray)): - # represent bytes safely in hex - hexval = value.hex() - return f"" - - if isinstance(value ,(complex ,range)): - return repr(value) - - # Rule 4: No idea what this is - raise TypeError(f"TapeMachine cannot JSON-normalize value: {value!r} of type {type(value)}") - - def to_JSON( - self - ) -> dict[str ,Any]: - """ - JSON representation: - - { - "TapeMachine": "" - ,"head": - ,"Tape": [ , , ... ] - } - - Where each Value is JSON-normalized by normalize_value(). - """ - normalized = [] - for raw in self._tape: - normalized.append(self.normalize_value(raw)) - - return { - "TapeMachine": hex(id(self)) - ,"head": self._head_position - ,"Tape": normalized - } - -def _tape_debug( - tm: Any - ,msg: str -) -> None: - """Emit a TapeMachine-related debug message if meta.DEBUG has 'TapeMachine'.""" - if "TapeMachine" not in meta.DEBUG: return - print(f"[TapeMachine {hex(id(tm))}] {msg}") - - -- 2.20.1