From: Thomas Walker Lynch Date: Tue, 2 Dec 2025 07:56:39 +0000 (+0000) Subject: TM_ND_A and TM_ND_DB machines X-Git-Url: https://git.reasoningtechnology.com/style/static/gitweb.css?a=commitdiff_plain;h=140ebfc216061bb9218493c1c14e275dd0942d41;p=Harmony.git TM_ND_A and TM_ND_DB machines --- 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/TM_ND_A.py b/shared/authored/dir-walk/TM_ND_A.py new file mode 100644 index 0000000..0dbccef --- /dev/null +++ b/shared/authored/dir-walk/TM_ND_A.py @@ -0,0 +1,49 @@ +#!/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 + 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/TapeMachine.py b/shared/authored/dir-walk/TapeMachine.py deleted file mode 100644 index 0dbccef..0000000 --- a/shared/authored/dir-walk/TapeMachine.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/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 - 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}") - -