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(
--- /dev/null
+#!/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 duck
+
+class TM_ND_A:
+ """
+ TM_ND_A
+
+ TM Tape Machine
+ ND Non Destructive, no cell deletion or insertion
+ A Address supports addressing of cells
+
+ Machine does not support status, so if the machine exists, the tape has
+ at least one cell.
+
+ Head starts on cell 0 and by contract with the user is always on a valid cell.
+ I.e. it is the user's responsibility to check `can_step` before stepping.
+ """
+
+ # ----------------------------------------------------------------------
+ def __init__(self ,tape):
+ duck.require(duck.class_of(tape) ,"__len__" ,"__getitem__" ,"__setitem__")
+ self._extent: int = len(tape) - 1
+ self._tape = tape
+ self._head_position: int = 0
+
+ # ----------------------------------------------------------------------
+ def extent(self) -> int: return self._extent
+ def tape(self) -> Any: return self._tape
+ def where(self) -> int: return self._head_position
+ def cue_leftmost(self) -> None: self._head_position = 0
+ def cue_rightmost(self) -> None: self._head_position = self._extent
+ def cue(self ,new_position: int) -> int: self._head_position = new_position
+ def can_step(self ,n: int = 1) -> bool: return 0 <= self._head_position + n <= self._extent
+ def step(self ,n: int = 1) -> None: self._head_position += n
+ def read(self ,n: int = 0) -> Any: return self._tape[self._head_position + n]
+ def write(self ,value: Any ,n: int = 0) -> None: self._tape[self._head_position + n] = value
+
+ # because this is an ND machine it is safe to entangle heads
+ def entangle(self) -> "TM_ND_A":
+ tm = object.__new__(TM_ND_A)
+ tm._tape = self._tape
+ tm._extent = self._extent
+ tm._head_position = self._head_position
+ return tm
+
--- /dev/null
+#!/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="<init>" ,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()
+++ /dev/null
-#!/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 duck
-
-class TM_ND_A:
- """
- TM_ND_A
-
- TM Tape Machine
- ND Non Destructive, no cell deletion or insertion
- A Address supports addressing of cells
-
- Machine does not support status, so if the machine exists, the tape has
- at least one cell.
-
- Head starts on cell 0 and by contract with the user is always on a valid cell.
- I.e. it is the user's responsibility to check `can_step` before stepping.
- """
-
- # ----------------------------------------------------------------------
- def __init__(self ,tape):
- duck.require(duck.class_of(tape) ,"__len__" ,"__getitem__" ,"__setitem__")
- self._extent: int = len(tape) - 1
- self._tape = tape
- self._head_position: int = 0
-
- # ----------------------------------------------------------------------
- def extent(self) -> int: return self._extent
- def tape(self) -> Any: return self._tape
- def where(self) -> int: return self._head_position
- def cue_leftmost(self) -> None: self._head_position = 0
- def cue_rightmost(self) -> None: self._head_position = self._extent
- def cue(self ,new_position: int) -> int: self._head_position = new_position
- def can_step(self ,n: int = 1) -> bool: return 0 <= self._head_position + n <= self._extent
- def step(self ,n: int = 1) -> None: self._head_position += n
- def read(self ,n: int = 0) -> Any: return self._tape[self._head_position + n]
- def write(self ,value: Any ,n: int = 0) -> None: self._tape[self._head_position + n] = value
-
- # because this is an ND machine it is safe to entangle heads
- def entangle(self) -> "TM_ND_A":
- tm = object.__new__(TM_ND_A)
- tm._tape = self._tape
- tm._extent = self._extent
- tm._head_position = self._head_position
- return tm
-
# "TreeMachine" -> enable debug for TreeMachine operations.
#
DEBUG: set[str] = set()
-
+#DEBUG.add("COLOR")
def version() -> None:
"""
+++ /dev/null
-
- 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"<bytes len={len(value)} hex={hexval}>"
-
- 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": "<hex id>"
- ,"head": <int>
- ,"Tape": [ <Value0> ,<Value1> , ... ]
- }
-
- 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}")
-
-