From af5aa3e49399e512f1ab959302845931f09691bc Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Tue, 2 Dec 2025 05:49:49 +0000 Subject: [PATCH] . --- shared/authored/dir-walk.zip | Bin 0 -> 20773 bytes shared/authored/dir-walk/BreadthMachine.py | 11 +- shared/authored/dir-walk/DepthMachine.py | 11 +- shared/authored/dir-walk/JSON.py | 250 ++++++++++++++ shared/authored/dir-walk/Queue.py | 343 ++++++------------- shared/authored/dir-walk/TapeMachine.py | 138 ++------ shared/authored/dir-walk/duck.py | 114 ++++++ shared/authored/dir-walk/temp.py | 98 ++++++ shared/authored/dir-walk/test_TapeMachine.py | 83 +++++ 9 files changed, 683 insertions(+), 365 deletions(-) create mode 100644 shared/authored/dir-walk.zip create mode 100755 shared/authored/dir-walk/JSON.py mode change 100755 => 100644 shared/authored/dir-walk/TapeMachine.py create mode 100755 shared/authored/dir-walk/duck.py create mode 100644 shared/authored/dir-walk/temp.py create mode 100755 shared/authored/dir-walk/test_TapeMachine.py diff --git a/shared/authored/dir-walk.zip b/shared/authored/dir-walk.zip new file mode 100644 index 0000000000000000000000000000000000000000..e46a1541405d7d9bdf556290512a4a5eb36ee26a GIT binary patch literal 20773 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 literal 0 HcmV?d00001 diff --git a/shared/authored/dir-walk/BreadthMachine.py b/shared/authored/dir-walk/BreadthMachine.py index 5f20fbc..6d489e1 100755 --- a/shared/authored/dir-walk/BreadthMachine.py +++ b/shared/authored/dir-walk/BreadthMachine.py @@ -113,16 +113,17 @@ class BreadthMachine: return # Still rightmost in new front context; keep popping. - # ------------------------------------------------------------------ - # Introspection helper - # ------------------------------------------------------------------ def has_node(self) -> bool: - """True if there is a current node to read.""" + """ + True if there is a current node to read. + """ return self._has_node and self._tree_machine.context_depth() > 0 def build_breadth_machine(root_dp: str) -> BreadthMachine: - """Worker: construct and return a BreadthMachine for root_dp.""" + """ + Worker: construct and return a BreadthMachine for root_dp. + """ return BreadthMachine(root_dp) diff --git a/shared/authored/dir-walk/DepthMachine.py b/shared/authored/dir-walk/DepthMachine.py index 5d30f9f..ab6700a 100755 --- a/shared/authored/dir-walk/DepthMachine.py +++ b/shared/authored/dir-walk/DepthMachine.py @@ -121,16 +121,17 @@ class DepthMachine: return # Still rightmost in new front context; keep popping. - # ------------------------------------------------------------------ - # Introspection helper - # ------------------------------------------------------------------ def has_node(self) -> bool: - """True if there is a current node to read.""" + """ + True if there is a current node to read. + """ return self._has_node and self._tree_machine.context_depth() > 0 def build_depth_machine(root_dp: str) -> DepthMachine: - """Worker: construct and return a DepthMachine for root_dp.""" + """ + Worker: construct and return a DepthMachine for root_dp. + """ return DepthMachine(root_dp) diff --git a/shared/authored/dir-walk/JSON.py b/shared/authored/dir-walk/JSON.py new file mode 100755 index 0000000..7a3336a --- /dev/null +++ b/shared/authored/dir-walk/JSON.py @@ -0,0 +1,250 @@ +#!/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 json as pyjson +import duck +import builtins + +# ---------------------------------------------------------------------- +def to_JSON_TM( + tm: Any +) -> str: + """ + Convert a Tape Machine (TM_ND_A or compatible) to a JSON string. + + JSON contains only: + - "tape": list of cell values + - "head": integer head position + - "extent": last valid index + """ + + head = tm.address() + tm.cue_leftmost() + tape_data = [tm.read(n) for n in range(0 ,tm.extent + 1)] + + obj = { + "tape" : tape_data + ,"head" : tm.address() + ,"extent": tm.extent + } + + return pyjson.dumps(tm ,ensure_ascii=False) + + +# ---------------------------------------------------------------------- +def print_TM( + tm: Any + ,indent: int = 0 +) -> None: + """ + Print the JSON representation of the TM nicely indented. + """ + s = to_JSON_TM(tm) + parsed = pyjson.loads(s) + out = pyjson.dumps(parsed ,indent=2 ,ensure_ascii=False) + prefix = " " * indent + for line in out.split("\n"): + print(prefix + line) + + +# ---------------------------------------------------------------------- +def pretty_TM( + tm0: Any + ,indent: int = 0 +) -> None: + """ + Walk the tape cells, printing: + + + + mark:: + ' ' normal cell + '*' head is here + '␀' empty (None, non-head) + """ + prefix = " " * indent + + tm1 = tm0.entangle() + tm1.cue_leftmost() + + while True: + v = tm1.read() + + if tm1.address() == tm0.address(): + mark = "*" + elif v is None: + mark = "␀" + else: + mark = " " + + s = "" if v is None else str(v) + print(f"{prefix}{mark}{s}") + + if not tm1.can_step(): break + tm1.step() + + +# ---------------------------------------------------------------------- +def is_a_TM( + value: Any +) -> bool: + """ + A Tape Machine is anything providing the attributes: + + entangle + cue_leftmost + read + can_step + step + address + + These are exactly what pretty_TM() uses. + """ + return duck.has( + value + ,"entangle" + ,"cue_leftmost" + ,"read" + ,"can_step" + ,"step" + ,"address" + ) + +# ---------------------------------------------------------------------- +def print( + value: Any + ,indent: int = 0 +) -> None: + """ + Print logic: + + - If TM: call print_TM + - Else: print raw value using Python print + (JSON primitives remain one-line) + """ + if is_a_TM(value): + print_TM(value ,indent) + else: + prefix = " " * indent + print(prefix + str(value)) + + +# ---------------------------------------------------------------------- +def pretty( + value: Any + ,indent: int = 0 +) -> None: + """ + Pretty-print logic: + + - If TM: pretty_TM + - If JSON primitive: print raw + - If non-JSON type: wrap into { "value": } and pretty-print + - If JSON object/array: pretty-print with indentation + """ + prefix = " " * indent + + # TM case + if is_a_TM(value): + pretty_TM(value ,indent) + return + + # JSON primitive: str, int, float, bool, None + if isinstance(value ,(str ,int ,float ,bool)) or value is None: + print(prefix + str(value)) + return + + # Try to JSON-dump; if it fails, wrap in {"value": repr} + try: + js = pyjson.dumps(value ,ensure_ascii=False) + parsed = pyjson.loads(js) + except Exception: + wrapped = { "value": repr(value) } + parsed = wrapped + + # Pretty-print JSON + out = pyjson.dumps(parsed ,indent=2 ,ensure_ascii=False) + for line in out.split("\n"): + print(prefix + line) + + + +# ---------------------------------------------------------------------- +def CLI() -> None: + """ + Demo for json helpers. Prints examples for: + + - to_JSON_TM + - print_TM + - pretty_TM + - is_a_TM + - print + - pretty + """ + p = builtins.print + + class DemoTM: + def __init__(self) -> None: + self._tape = [1 ,None ,"three"] + self._head_position = 1 # on the None cell + + @property + def extent(self) -> int: return len(self._tape) - 1 + def address(self) -> int: return self._head_position + def cue_leftmost(self) -> None: self._head_position = 0 + 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 + def entangle(self) -> "DemoTM": + tm = DemoTM() + tm._tape = self._tape + tm._head_position = self._head_position + return tm + + tm = DemoTM() + + p("json.is_a_TM examples:") + p(" is_a_TM(tm) ->" ,is_a_TM(tm)) + p(" is_a_TM(123) ->" ,is_a_TM(123)) + p() + + p("json.to_JSON_TM example:") + p(" to_JSON_TM(tm) ->") + p(" " + to_JSON_TM(tm)) + p() + + p("json.print_TM example:") + print_TM(tm ,indent=2) + p() + + p("json.pretty_TM example:") + pretty_TM(tm ,indent=2) + p() + + p("json.print() examples:") + print(tm ,indent=2) # TM case + print(42 ,indent=2) # primitive + print({"a": 1 ,"b": 2} ,indent=2) # dict, one-line + p() + + p("json.pretty() examples:") + pretty(tm ,indent=2) # TM + p("---") + pretty(42 ,indent=2) # primitive + p("---") + pretty({"a": 1 ,"b": [2 ,3]} ,indent=2) # JSON object + p("---") + pretty(object() ,indent=2) # non-JSON -> wrapped + + +# ---------------------------------------------------------------------- +if __name__ == "__main__": + CLI() + diff --git a/shared/authored/dir-walk/Queue.py b/shared/authored/dir-walk/Queue.py index da84999..5715169 100755 --- a/shared/authored/dir-walk/Queue.py +++ b/shared/authored/dir-walk/Queue.py @@ -3,306 +3,157 @@ from __future__ import annotations -from typing import Any, Generic, Iterable, List, Optional, TypeVar - import meta - - -T = TypeVar("T") - - -def _format_item_for_debug( - item: Any -) -> str: - """ - Convert a queued item to a human-readable string for debug output. - - If the item has a callable .print() method, we call it and use - the returned value (converted to str). Otherwise we fall back - to repr(item). - """ - if hasattr(item, "print"): - candidate = getattr(item, "print") - if callable(candidate): - try: - text = candidate() - if text is None: - return repr(item) - return str(text) - except Exception: - return repr(item) - return repr(item) +from typing import Any, List def _queue_debug( - queue: Any - ,op: str - ,item: Any | None = None + q: Any + ,msg: str ) -> None: """ - Internal helper: emit a queue-related debug message if - the meta.DEBUG set contains the tag "Queue". - - Includes the queue instance pointer in hex so we can - distinguish multiple Queue instances. + Emit a Queue-related debug message if meta.DEBUG has 'Queue'. """ - if "Queue" not in meta.DEBUG: - return - - qid = hex(id(queue)) - - if item is None: - print(f"[Queue {qid}] {op}") - else: - desc = _format_item_for_debug(item) - print(f"[Queue {qid}] {op}: {desc}") + if "Queue" not in meta.DEBUG: return + print(f"[Queue {hex(id(q))}] {msg}") -class Queue(Generic[T]): +class Queue: """ - Queue — ordered queue of items (e.g., Context objects). + Simple FIFO queue with tagged push operations. - Semantics: + Items are stored in a Python list, with index 0 as the "front". + """ - - The "front" of the queue is the current item. - - The "back" of the queue will eventually be visited. - - Popping an empty queue is an error. + def __init__(self) -> None: + self._items: List[Any] = [] - Interface: + def push( + self + ,where_tag: str + ,item: Any + ) -> None: + """ + Push an item into the queue. - front() -> T : current item (head), does not remove it. - back() -> T : last item in the queue, does not remove it. - top() -> T : alias for front(). - depth() -> int : number of entries in the queue. - pop() -> T : remove and return the front item. + where_tag: + - 'front' -> insert at front + - 'back' -> append at back + - 'throw-away' -> discard item (for debugging) + """ + if hasattr(item, "print"): + desc = item.print() + else: + desc = repr(item) - push(where_tag, item: T) -> None: + if where_tag == "throw-away": + _queue_debug(self, f"push throw-away (discard): {desc}") + return - where_tag: - "front" -> insert item at front of queue - "back" -> append item at back of queue - "throw-away" -> do not store, treated as intentionally ignored + if where_tag == "front": + self._items.insert(0, item) + _queue_debug(self, f"push front: {desc}") + return - clone() -> Queue[T] + if where_tag == "back": + self._items.append(item) + _queue_debug(self, f"push back: {desc}") + return - Creates a copy of the Queue. For each item: - - If item has a .clone() method, it is called. - - Otherwise the item reference is reused. - """ + raise ValueError(f"Queue.push: unknown where_tag {where_tag!r}") - def __init__( - self - ,initial_iter: Optional[Iterable[T]] = None - ) -> None: - if initial_iter is None: - self._queue_list: List[T] = [] + def pop(self) -> Any: + """ + Pop and return the front item. + """ + if not self._items: + raise IndexError("Queue.pop on empty queue.") + item = self._items.pop(0) + if hasattr(item, "print"): + desc = item.print() else: - self._queue_list = list(initial_iter) + desc = repr(item) + _queue_debug(self, f"pop: {desc}") + return item - def snapshot(self) -> list: + def front(self) -> Any: """ - Return a shallow copy of the items in queue order (front to back). - - This is intended for read-only traversal by higher-level logic - such as TreeMachine.can_step(). + Return the front item without removing it. """ - return list(self._items) - - # ---------------------------------------------------------------------- - # Basic properties - # ---------------------------------------------------------------------- - def is_empty(self) -> bool: - return len(self._queue_list) == 0 - - def depth(self) -> int: - return len(self._queue_list) + if not self._items: + raise IndexError("Queue.front on empty queue.") + return self._items[0] - # ---------------------------------------------------------------------- - # Cloning - # ---------------------------------------------------------------------- - def clone(self) -> "Queue[T]": + def back(self) -> Any: """ - Create a copy of this Queue. - - The new queue has its own internal list. If an item provides - a .clone() method, that is used to copy the item; otherwise - the item reference is reused. + Return the back item without removing it. """ - new_item_list: List[T] = [] - for item in self._queue_list: - if hasattr(item, "clone") and callable(getattr(item, "clone")): - new_item_list.append(item.clone()) # type: ignore[arg-type] - else: - new_item_list.append(item) - new_q = Queue[T](new_item_list) - _queue_debug(self, f"clone -> new {hex(id(new_q))}") - return new_q - - # ---------------------------------------------------------------------- - # Accessors - # ---------------------------------------------------------------------- - def front(self) -> T: - if self.is_empty(): - raise RuntimeError("Queue.front() on empty queue.") - return self._queue_list[0] - - def back(self) -> T: - if self.is_empty(): - raise RuntimeError("Queue.back() on empty queue.") - return self._queue_list[-1] + if not self._items: + raise IndexError("Queue.back on empty queue.") + return self._items[-1] - def top(self) -> T: + def depth(self) -> int: """ - Alias for front(). + Number of items in the queue. """ - return self.front() + return len(self._items) - # ---------------------------------------------------------------------- - # Mutating operations - # ---------------------------------------------------------------------- - def pop(self) -> T: + def is_empty(self) -> bool: """ - Remove and return the front item. - - Popping an empty queue is an error. + True if the queue is empty. """ - if self.is_empty(): - raise RuntimeError("Queue.pop() on empty queue.") - item = self._queue_list.pop(0) - _queue_debug(self, "pop", item) - return item + return not self._items - def push( - self - ,where_tag: str - ,item: T - ) -> None: + def snapshot(self) -> list: """ - Push an item according to where_tag: - - where_tag == "front": - insert item at front of the queue. - - where_tag == "back": - append item at back of the queue. + Return a shallow copy of items (front to back). - where_tag == "throw-away": - do not store item; treated as intentionally ignored. - - Any other where_tag is an error. + Intended for read-only inspection (e.g. TreeMachine.can_step()). """ - if where_tag == "front": - self._queue_list.insert(0, item) - _queue_debug(self, "push front", item) - return - - if where_tag == "back": - self._queue_list.append(item) - _queue_debug(self, "push back", item) - return - - if where_tag == "throw-away": - _queue_debug(self, "push throw-away (discard)", item) - return - - raise ValueError(f"Unknown Queue push tag: {where_tag!r}") + return list(self._items) - # ---------------------------------------------------------------------- - # Dispatcher - # ---------------------------------------------------------------------- - def dispatch( - self - ,op: str - ,*args - ,**kwargs - ): + def clone(self) -> "Queue": """ - Generic dispatcher for method-tag control. - - Supported op values: - - "front" -> front() - "back" -> back() - "top" -> top() - "depth" -> depth() - "pop" -> pop() + Clone the queue. - "push" -> push(where_tag, item) - - Any unknown op raises ValueError. + If an item has a .clone() method, use it. Otherwise copy the + reference directly. """ - if op == "front": - return self.front() - if op == "back": - return self.back() - if op == "top": - return self.top() - if op == "depth": - return self.depth() - if op == "pop": - return self.pop() - if op == "push": - if len(args) != 2: - raise ValueError("dispatch('push', where_tag, item) requires 2 arguments.") - where_tag = args[0] - item = args[1] - self.push(where_tag, item) - return None - - raise ValueError(f"Unknown Queue operation: {op!r}") - - -# ---------------------------------------------------------------------- -# Simple test harness -# ---------------------------------------------------------------------- -class TestItem: - """ - Minimal test item for the __main__ test(). - - It implements .print() so Queue can use it in debug messages. - """ - - def __init__( - self - ,name: str - ) -> None: - self.name = name - - def print(self) -> str: - return f"TestItem({self.name})" - - def clone(self) -> "TestItem": - # For the test, clone is just another instance with same name. - return TestItem(self.name) + new_q = Queue() + for item in self._items: + if hasattr(item, "clone"): + new_q._items.append(item.clone()) + else: + new_q._items.append(item) + return new_q -def test() -> None: +def CLI(argv: list[str]) -> int: """ - Simple test for Queue with debug enabled. + Simple self-test for Queue. """ - meta.DEBUG.add("Queue") - print("Queue test starting...") meta.version() + meta.DEBUG.add("Queue") - q = Queue[TestItem]() - q2 = q.clone() # exercise clone-debug once at the start - - q.push("front", TestItem("root")) - q.push("back", TestItem("child-A")) - q.push("back", TestItem("child-B")) - q.push("throw-away", TestItem("ignored-node")) + q = Queue() + q.push("back", "apple") + q.push("back", "banana") + q.push("front", "cherry") + q.push("throw-away", "ignored") print(f"depth = {q.depth()}") - print(f"front = {q.front().print()}") - print(f"back = {q.back().print()}") + print(f"front = {q.front()!r}") + print(f"back = {q.back()!r}") popped = q.pop() - print(f"popped = {popped.print()}") + print(f"popped = {popped!r}") print(f"depth = {q.depth()}") print("Queue test done.") + return 0 if __name__ == "__main__": - test() + import sys + raise SystemExit(CLI(sys.argv)) diff --git a/shared/authored/dir-walk/TapeMachine.py b/shared/authored/dir-walk/TapeMachine.py old mode 100755 new mode 100644 index 0ca1d21..abf2e9b --- a/shared/authored/dir-walk/TapeMachine.py +++ b/shared/authored/dir-walk/TapeMachine.py @@ -2,130 +2,50 @@ # -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- from __future__ import annotations +from typing import Any +import duck -from typing import Any, Optional - -import meta - +class TM_ND_A: + """ + TM_ND_A -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}") + 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. -class TapeMachine: + 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. """ - TapeMachine — single-tape, single-head, first-rest pattern. - - Tape is non-empty. - - Head starts on cell 0 and is always on a valid cell. - - Movement contract: caller checks can_step(n) before step(n). - """ - - def __init__(self, tape: Optional[Any] = None) -> None: - if tape is None: tape = [] - for name in ("__len__", "__getitem__", "__setitem__"): - if not hasattr(tape, name): raise TypeError(f"TapeMachine tape must support {name}: {tape!r}") - self._length: int = len(tape) - if self._length == 0: raise ValueError("TapeMachine requires a non-empty tape (first-rest pattern).") + # ---------------------------------------------------------------------- + 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 - _tape_debug(self, f"init length={self._length}, head_position=0") + # ---------------------------------------------------------------------- @property - def length(self) -> int: - """Length of the tape (number of cells).""" - return self._length - - def cue(self, new_position: Optional[int] = None) -> int: - """Get or set head (cue) position.""" - if new_position is None: return self._head_position - if not 0 <= new_position < self._length: raise IndexError(f"TapeMachine.cue: out of range ({new_position}).") - self._head_position = new_position - return self._head_position - - def can_step(self, n: int = 1) -> bool: - """True if step(n) would keep the head on the tape.""" - proposed_position = self._head_position + n - return 0 <= proposed_position < self.length - - def rewind(self) -> None: - """Move head to the first cell (index 0).""" - self._head_position = 0 - - def cue_end(self) -> None: - """Move head to the last cell (index length-1).""" - self._head_position = self._length - 1 - - def step(self, n: int = 1) -> None: - """Move head by n cells (n>0 right, n<0 left, n=0 no-op); caller must ensure can_step(n).""" - self._head_position += n - - def read(self) -> Any: - """Read value under head.""" - return self._tape[self._head_position] - - def write(self, value: Any) -> None: - """Write a new value into the current cell.""" - self._tape[self._head_position] = value - - def print(self) -> str: - """ - Printable view of the tape. - - Two spaces then value per line; line with the head is prefixed '* '. - """ - line_list = [] - for idx in range(self._length): - prefix = "* " if idx == self._head_position else " " - line_list.append(f"{prefix}{self._tape[idx]}") - return "\n".join(line_list) - + 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) -> "TapeMachine": """Create an entangled copy: shared tape, independent head_position.""" new_tm = object.__new__(TapeMachine) new_tm._tape = self._tape new_tm._length = self._length new_tm._head_position = self._head_position - _tape_debug(self, f"entangle -> new {hex(id(new_tm))} head_position={self._head_position}") return new_tm - -def test() -> None: - """Simple TapeMachine test with first-rest semantics.""" - meta.DEBUG.add("TapeMachine") - print("TapeMachine test starting..."); meta.version() - - tm = TapeMachine(["apple", "orange", "tangerine"]) - print(f"initial: cue={tm.cue()}, value={tm.read()!r}") - - while tm.can_step(1): - tm.step(1) - print(f"step(+1): cue={tm.cue()}, value={tm.read()!r}") - - if tm.can_step(-1): - tm.step(-1) - print(f"step(-1): cue={tm.cue()}, value={tm.read()!r}") - - tm.rewind() - print(f"rewind: cue={tm.cue()}, value={tm.read()!r}") - - tm2 = tm.entangle() - if tm.can_step(2): - tm.step(2) - print(f"tm step(+2): cue={tm.cue()}, value={tm.read()!r}") - if tm2.can_step(1): - tm2.step(1) - print(f"tm2 step(+1): cue={tm2.cue()}, value={tm2.read()!r}") - - tm.cue_end() - print(f"cue_end: cue={tm.cue()}, value={tm.read()!r}") - - print("print(tm):") - print(tm.print()) - print("TapeMachine test done.") - - -if __name__ == "__main__": - test() diff --git a/shared/authored/dir-walk/duck.py b/shared/authored/dir-walk/duck.py new file mode 100755 index 0000000..cc086d6 --- /dev/null +++ b/shared/authored/dir-walk/duck.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +from typing import Any, Optional + + +# ---------------------------------------------------------------------- +def class_of( + value: Any +) -> Optional[type]: + """ + Return the runtime class for a value. + + If the value has a __class__ attribute that is a real type, return it. + Otherwise return None. + """ + cls = getattr(value ,"__class__" ,None) + return cls if isinstance(cls ,type) else None + + +# ---------------------------------------------------------------------- +def has( + inspected_class: Any + ,*attribute_names: str +) -> bool: + """ + True iff `inspected_class` has *all* listed attributes. + + Usage: + has(MyClass ,"x" ,"y" ,"z") + """ + for name in attribute_names: + if not hasattr(inspected_class ,name): + return False + return True + + +# ---------------------------------------------------------------------- +def require( + inspected_class: Any + ,*attribute_names: str +) -> None: + """ + Guard: ensure `inspected_class` has the required attributes. + + Usage: + require(MyClass ,"x" ,"y" ,"z") + + Raise TypeError if any required attribute is missing. + """ + if has(inspected_class ,*attribute_names): + return + + # Collect missing names for detailed error. + missing_names = [ + name + for name in attribute_names + if not hasattr(inspected_class ,name) + ] + + raise TypeError( + f"require() failed: {inspected_class!r} " + f"is missing attributes {missing_names!r}" + ) + +# ---------------------------------------------------------------------- +def CLI() -> None: + """ + Demo for duck helpers. Prints examples for: + + - class_of + - has + - require + """ + + class Demo: + demo_attr = 1 + def demo_method(self) -> None: # short-stuff + pass + + print("duck.class_of examples:") + print(" class_of(Demo) ->" ,class_of(Demo)) + print(" class_of(Demo()) ->" ,class_of(Demo())) + print(" class_of(None) ->" ,class_of(None)) + print() + + print("duck.has examples:") + print(" has(Demo ,'demo_attr') ->" + ,has(Demo ,"demo_attr")) + print(" has(Demo ,'demo_method') ->" + ,has(Demo ,"demo_method")) + print(" has(Demo ,'missing_attr') ->" + ,has(Demo ,"missing_attr")) + print(" has(Demo ,'demo_attr','missing_attr') ->" + ,has(Demo ,"demo_attr" ,"missing_attr")) + print() + + print("duck.require examples:") + print(" require(Demo ,'demo_attr') (no error)") + require(Demo ,"demo_attr") + + print(" require(Demo ,'demo_attr','demo_method') (no error)") + require(Demo ,"demo_attr" ,"demo_method") + + print(" require(Demo ,'missing_attr') (should raise)") + try: + require(Demo ,"missing_attr") + except TypeError as exc: + print(" caught TypeError:" ,exc) + + +# ---------------------------------------------------------------------- +if __name__ == "__main__": + CLI() diff --git a/shared/authored/dir-walk/temp.py b/shared/authored/dir-walk/temp.py new file mode 100644 index 0000000..c546377 --- /dev/null +++ b/shared/authored/dir-walk/temp.py @@ -0,0 +1,98 @@ + + 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}") + + diff --git a/shared/authored/dir-walk/test_TapeMachine.py b/shared/authored/dir-walk/test_TapeMachine.py new file mode 100755 index 0000000..8701c64 --- /dev/null +++ b/shared/authored/dir-walk/test_TapeMachine.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; -*- + +from TapeMachine import TapeMachine +from JSONPretty import print_JSON ,pretty_JSON +import meta + + +def test_TapeMachine() -> None: + """Unit test for TapeMachine using JSONPretty output.""" + meta.DEBUG.add("TapeMachine") + print("TapeMachine test starting..."); meta.version() + + print("----------------------------------------") + print("instantiating tape machine") + print("") + + tm = TapeMachine(["apple", "orange", "tangerine"]) + + print("----------------------------------------") + print("printing newly instantiated tape machine") + print("") + + print_JSON(tm.to_JSON()) + + + print("----------------------------------------") + print("pretty printing newly instantiated tape machine") + print("") + + pretty_JSON(tm.to_JSON()) + + print("----------------------------------------") + print("one rightward step at a time through the tape") + print("") + + while tm.can_step(1): + tm.step(1) + print_JSON(tm.to_JSON()) + + print("----------------------------------------") + print("one leftward step at a time through the tape") + print("") + + if tm.can_step(-1): + tm.step(-1) + print_JSON(tm.to_JSON()) + + print("----------------------------------------") + print("printing machine again") + print("") + + tm.rewind() # this won't do anything + print_JSON(tm.to_JSON()) + + print("----------------------------------------") + print("making entangled copy") + print("") + + tm2 = tm.entangle() + + print("----------------------------------------") + print("stepping original two steps") + print("") + if tm.can_step(2): + tm.step(2) + print_JSON(tm.to_JSON()) + + print("----------------------------------------") + print("stepping copy one step") + print("") + if tm2.can_step(1): + tm2.step(1) + print_JSON(tm2.to_JSON()) + + tm.cue_end() + print_JSON(tm.to_JSON()) + + print("TapeMachine test done.") + + +if __name__ == "__main__": + test_TapeMachine() -- 2.20.1