--- /dev/null
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+from __future__ import annotations
+
+import sys
+from typing import List, Tuple
+
+import meta
+from TreeMachine import TreeMachine
+
+
+class BreadthMachine:
+ """
+ BreadthMachine — breadth-first directory walker built on TreeMachine.
+
+ External usage:
+
+ bm = BreadthMachine(root_dp)
+ while True:
+ path, props, addr = bm.read()
+ # ... process node ...
+ if not bm.can_step(): break
+ bm.step()
+ """
+
+ def __init__(self, root_dp: str | None = None) -> None:
+ if root_dp is None: root_dp = "."
+ self.root_dp: str = root_dp
+ self._tree_machine: TreeMachine = TreeMachine(root_dp)
+ self._has_node: bool = False
+
+ # Bootstrap: move to the first node, if any.
+ if self._tree_machine.context_depth() > 0:
+ self._tree_machine.step()
+ self._has_node = True
+
+ # ------------------------------------------------------------------
+ # External interface
+ # ------------------------------------------------------------------
+ def read(self) -> Tuple[str, List[str], Tuple[int, int]]:
+ """
+ Read the current node.
+
+ Returns:
+ (path_relative_to_root, property_list, address)
+ """
+ if not self._has_node:
+ raise RuntimeError("BreadthMachine.read() with no current node.")
+ return self._tree_machine.read()
+
+ def can_step(self) -> bool:
+ """
+ True if there is another node after the current one.
+
+ Logic:
+
+ - If current node is a non-empty directory, we can always push("back")
+ and eventually visit its children.
+
+ - Otherwise, delegate to TreeMachine.can_step(), which ripples through
+ all Contexts to see whether any Context is not rightmost.
+ """
+ if not self._has_node:
+ return False
+
+ _path, prop_list, _addr = self._tree_machine.read()
+ is_non_empty_dir = ("directory" in prop_list and "empty" not in prop_list)
+
+ if is_non_empty_dir:
+ return True
+
+ return self._tree_machine.can_step()
+
+ def step(self) -> None:
+ """
+ Advance to the next node (breadth-first flavor).
+
+ Steps:
+
+ 1. If current node is a non-empty directory, push it to the BACK of
+ the context queue. (We do NOT descend immediately.)
+
+ 2. If current context is not rightmost, step within it.
+
+ 3. If current context is rightmost, pop contexts until a context is
+ found that can step, then step there.
+ """
+ if not self.can_step():
+ raise RuntimeError("BreadthMachine.step() called when can_step() is False.")
+
+ _path, prop_list, _addr = self._tree_machine.read()
+ is_non_empty_dir = ("directory" in prop_list and "empty" not in prop_list)
+
+ # 1. Queue non-empty directory for later (back of the queue).
+ if is_non_empty_dir:
+ self._tree_machine.push_current_directory("back")
+
+ # 2. Try to step within current context.
+ if not self._tree_machine.rightmost():
+ self._tree_machine.step()
+ return
+
+ # 3. Current context is rightmost -> pop until we can step.
+ while True:
+ self._tree_machine.pop_context()
+ if self._tree_machine.context_depth() == 0:
+ # Defensive: should not happen if can_step() was True.
+ self._has_node = False
+ return
+ if not self._tree_machine.rightmost():
+ self._tree_machine.step()
+ 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."""
+ 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."""
+ return BreadthMachine(root_dp)
+
+
+def CLI(argv: List[str]) -> int:
+ """
+ Command-line interface for BreadthMachine.
+
+ Usage:
+
+ ./BreadthMachine.py [root_dp]
+
+ If root_dp is omitted, "." is used.
+ """
+ root_dp = argv[1] if len(argv) > 1 else "."
+ bm = build_breadth_machine(root_dp)
+
+ print("BreadthMachine")
+ meta.version()
+ print(f"# root = {root_dp!r}")
+
+ if not bm.has_node():
+ print("# (no nodes)")
+ return 0
+
+ while True:
+ path, _props, _addr = bm.read()
+ print(path)
+ if not bm.can_step(): break
+ bm.step()
+
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(CLI(sys.argv))
--- /dev/null
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+from __future__ import annotations
+
+import sys
+from typing import List, Tuple
+
+import meta
+from TreeMachine import TreeMachine
+
+
+class DepthMachine:
+ """
+ DepthMachine — depth-first directory walker built on TreeMachine.
+
+ External usage:
+
+ dm = DepthMachine(root_dp)
+ while True:
+ path, props, addr = dm.read()
+ # ... process node ...
+ if not dm.can_step(): break
+ dm.step()
+ """
+
+ def __init__(self, root_dp: str | None = None) -> None:
+ if root_dp is None: root_dp = "."
+ self.root_dp: str = root_dp
+ self._tree_machine: TreeMachine = TreeMachine(root_dp)
+ self._has_node: bool = False
+
+ # Bootstrap: move to the first node, if any.
+ if self._tree_machine.context_depth() > 0:
+ self._tree_machine.step()
+ self._has_node = True
+
+ # ------------------------------------------------------------------
+ # External interface
+ # ------------------------------------------------------------------
+ def read(self) -> Tuple[str, List[str], Tuple[int, int]]:
+ """
+ Read the current node.
+
+ Returns:
+ (path_relative_to_root, property_list, address)
+ """
+ if not self._has_node:
+ raise RuntimeError("DepthMachine.read() with no current node.")
+ return self._tree_machine.read()
+
+ def can_step(self) -> bool:
+ """
+ True if there is another node after the current one.
+
+ Logic:
+
+ - If current node is a non-empty directory, we can always push("front")
+ and descend into it.
+
+ - Otherwise, delegate to TreeMachine.can_step(), which ripples over
+ all Contexts to see whether any Context is not rightmost.
+ """
+ if not self._has_node:
+ return False
+
+ _path, prop_list, _addr = self._tree_machine.read()
+ is_non_empty_dir = ("directory" in prop_list and "empty" not in prop_list)
+
+ if is_non_empty_dir:
+ return True
+
+ return self._tree_machine.can_step()
+
+ def step(self) -> None:
+ """
+ Advance to the next node.
+
+ Cases:
+
+ A) Current node is a non-empty directory:
+ - push it to the FRONT of the context queue
+ - step into its first child.
+
+ B) Current node is not a non-empty directory AND current context
+ is not rightmost:
+ - step within current context.
+
+ C) Current node is not a non-empty directory AND current context
+ is rightmost:
+ - pop contexts until a non-rightmost context is found,
+ then step within that context.
+ """
+ if not self.can_step():
+ raise RuntimeError("DepthMachine.step() called when can_step() is False.")
+
+ _path, prop_list, _addr = self._tree_machine.read()
+ is_non_empty_dir = ("directory" in prop_list and "empty" not in prop_list)
+
+ # Case A: non-empty directory -> push front + descend.
+ if is_non_empty_dir:
+ self._tree_machine.push_current_directory("front")
+ # New front Context is the directory we just pushed; step into its first child.
+ self._tree_machine.step()
+ return
+
+ # Case B: not a non-empty dir and not rightmost -> simple step.
+ if not self._tree_machine.rightmost():
+ self._tree_machine.step()
+ return
+
+ # Case C: not a non-empty dir and rightmost -> pop until we can step.
+ while True:
+ self._tree_machine.pop_context()
+ if self._tree_machine.context_depth() == 0:
+ # Defensive: should not happen if can_step() was True.
+ self._has_node = False
+ return
+ if not self._tree_machine.rightmost():
+ self._tree_machine.step()
+ 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."""
+ 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."""
+ return DepthMachine(root_dp)
+
+
+def CLI(argv: List[str]) -> int:
+ """
+ Command-line interface for DepthMachine.
+
+ Usage:
+
+ ./DepthMachine.py [root_dp]
+
+ If root_dp is omitted, "." is used.
+ """
+ root_dp = argv[1] if len(argv) > 1 else "."
+ dm = build_depth_machine(root_dp)
+
+ print("DepthMachine")
+ meta.version()
+ print(f"# root = {root_dp!r}")
+
+ if not dm.has_node():
+ print("# (no nodes)")
+ return 0
+
+ # Knuth-style loop: read, test, step.
+ while True:
+ path, _props, _addr = dm.read()
+ print(path)
+ if not dm.can_step(): break
+ dm.step()
+
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(CLI(sys.argv))
else:
self._queue_list = list(initial_iter)
+ def snapshot(self) -> list:
+ """
+ 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 list(self._items)
+
# ----------------------------------------------------------------------
# Basic properties
# ----------------------------------------------------------------------
new_ctx._started = self._started
return new_ctx
+ def can_step(self) -> bool:
+ """
+ True if a step/pop sequence would find another node without
+ exhausting the entire context queue.
+
+ Implementation:
+
+ - If the queue is empty, there is nothing left to visit.
+ - Otherwise, walk the Contexts from front to back:
+
+ * If we find any Context that is NOT rightmost, then there
+ exists some directory in the tree where a step can advance
+ (possibly after one or more pops).
+
+ * If ALL Contexts report rightmost(), then any attempt to
+ step/pop would eventually pop the entire queue, so there
+ is no next node.
+ """
+ if self._context_queue.is_empty():
+ return False
+
+ ctx_list = self._context_queue.snapshot()
+ for ctx in ctx_list:
+ if not ctx.rightmost():
+ return True
+
+ # Reached the back; every Context is rightmost.
+ return False
+
def rightmost(self) -> bool:
"""
True when this context cannot be stepped further without error.
--- /dev/null
+1. **2025-11-26 14:58:00Z**
+
+2. Here are cleaned-up versions of `DepthMachine.py` and `BreadthMachine.py`:
+
+* Single `if __name__ == "__main__":` branch.
+* A `CLI` function that:
+
+ * unpacks `argv`,
+ * calls a worker,
+ * returns an `int`.
+* A small worker function that can be called directly from other code without going through CLI packing/parsing.
+
+---
+
+### `DepthMachine.py`
+
+```python
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+from __future__ import annotations
+
+import sys
+from typing import Any, List, Optional
+
+import meta
+from TapeMachine import TapeMachine
+from TreeMachine import TreeMachine
+
+
+def _depth_debug(dm: Any, msg: str) -> None:
+ """Emit a DepthMachine-related debug message if meta.DEBUG has 'DepthMachine'."""
+ if "DepthMachine" not in meta.DEBUG: return
+ print(f"[DepthMachine {hex(id(dm))}] {msg}")
+
+
+class DepthMachine(TapeMachine):
+ """
+ DepthMachine — depth-first directory walker as a TapeMachine.
+
+ - Wraps a TreeMachine.
+ - For each visited node, appends its relative path to a tape.
+ - For directories with children, pushes a Context with where_tag 'front'.
+ """
+
+ def __init__(self, root_dp: Optional[str] = None) -> None:
+ if root_dp is None: root_dp = "."
+ node_list: List[str] = self._build_node_list(root_dp)
+ if not node_list:
+ raise ValueError("DepthMachine requires at least one node under root.")
+ _depth_debug(self, f"init root_dp={root_dp!r}, n_nodes={len(node_list)}")
+ super().__init__(node_list)
+
+ def _build_node_list(self, root_dp: str) -> List[str]:
+ """Build a depth-first ordered list of node paths under root_dp."""
+ tm = TreeMachine(root_dp)
+ nodes: List[str] = []
+
+ while tm.context_depth() > 0:
+ tm.step()
+ value, prop_list, _addr = tm.read()
+ nodes.append(value)
+
+ if "directory" in prop_list and "empty" not in prop_list:
+ tm.push_current_directory("front")
+
+ if tm.rightmost():
+ tm.pop_context()
+
+ return nodes
+
+
+def build_depth_machine(root_dp: str) -> DepthMachine:
+ """Worker: construct and return a DepthMachine for root_dp."""
+ return DepthMachine(root_dp)
+
+
+def CLI(argv: List[str]) -> int:
+ """Command-line interface wrapper for DepthMachine."""
+ root_dp = argv[1] if len(argv) > 1 else "."
+ dm = build_depth_machine(root_dp)
+
+ print("DepthMachine")
+ meta.version()
+ print(f"# root = {root_dp!r}")
+ print(f"# length = {dm.length}")
+ dm.rewind()
+ while True:
+ print(dm.read())
+ if not dm.can_step(1): break
+ dm.step(1)
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(CLI(sys.argv))
+```
+
+---
+
+### `BreadthMachine.py`
+
+```python
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+from __future__ import annotations
+
+import sys
+from typing import Any, List, Optional
+
+import meta
+from TapeMachine import TapeMachine
+from TreeMachine import TreeMachine
+
+
+def _breadth_debug(bm: Any, msg: str) -> None:
+ """Emit a BreadthMachine-related debug message if meta.DEBUG has 'BreadthMachine'."""
+ if "BreadthMachine" not in meta.DEBUG: return
+ print(f"[BreadthMachine {hex(id(bm))}] {msg}")
+
+
+class BreadthMachine(TapeMachine):
+ """
+ BreadthMachine — breadth-first directory walker as a TapeMachine.
+
+ - Wraps a TreeMachine.
+ - For each visited node, appends its relative path to a tape.
+ - For directories with children, pushes a Context with where_tag 'back'.
+ """
+
+ def __init__(self, root_dp: Optional[str] = None) -> None:
+ if root_dp is None: root_dp = "."
+ node_list: List[str] = self._build_node_list(root_dp)
+ if not node_list:
+ raise ValueError("BreadthMachine requires at least one node under root.")
+ _breadth_debug(self, f"init root_dp={root_dp!r}, n_nodes={len(node_list)}")
+ super().__init__(node_list)
+
+ def _build_node_list(self, root_dp: str) -> List[str]:
+ """Build a breadth-first ordered list of node paths under root_dp."""
+ tm = TreeMachine(root_dp)
+ nodes: List[str] = []
+
+ while tm.context_depth() > 0:
+ tm.step()
+ value, prop_list, _addr = tm.read()
+ nodes.append(value)
+
+ if "directory" in prop_list and "empty" not in prop_list:
+ tm.push_current_directory("back")
+
+ if tm.rightmost():
+ tm.pop_context()
+
+ return nodes
+
+
+def build_breadth_machine(root_dp: str) -> BreadthMachine:
+ """Worker: construct and return a BreadthMachine for root_dp."""
+ return BreadthMachine(root_dp)
+
+
+def CLI(argv: List[str]) -> int:
+ """Command-line interface wrapper for BreadthMachine."""
+ root_dp = argv[1] if len(argv) > 1 else "."
+ bm = build_breadth_machine(root_dp)
+
+ print("BreadthMachine")
+ meta.version()
+ print(f"# root = {root_dp!r}")
+ print(f"# length = {bm.length}")
+ bm.rewind()
+ while True:
+ print(bm.read())
+ if not bm.can_step(1): break
+ bm.step(1)
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(CLI(sys.argv))
+```
+
+You can now:
+
+```bash
+./DepthMachine.py
+./DepthMachine.py /some/path
+./BreadthMachine.py
+./BreadthMachine.py /some/path
+```
+
+And from another Python module:
+
+```python
+from DepthMachine import build_depth_machine
+
+dm = build_depth_machine("/some/path")
+dm.rewind()
+while True:
+ print(dm.read())
+ if not dm.can_step(1): break
+ dm.step(1)
+```
+
+---
+
+3. **RT conventions we’ve touched in this session**
+
+Here’s a quick index of the conventions that surfaced so far, so we don’t have to rediscover them later:
+
+1. **Indentation and layout**
+
+ * Two-space indent.
+ * `indent-tabs-mode: nil` in the file header.
+ * Multi-line functions kept visually tight; formal parameter lists on a single line where they comfortably fit.
+ * Short, “inline” style for simple methods when reasonable.
+
+2. **Naming style**
+
+ * PascalCase for type / class names: `TapeMachine`, `TreeMachine`, `DepthMachine`, `BreadthMachine`, `Context`, `Queue`.
+ * snake_case for functions, methods, and variables: `can_step`, `cue_end`, `build_depth_machine`, `root_dp`.
+ * DEBUG helpers: `_tape_debug`, `_tm_debug`, `_depth_debug`, `_breadth_debug`.
+
+3. **Path suffix conventions**
+
+ * `*_dp` → directory path (unspecified relative vs absolute).
+ * `*_dpa` → absolute directory path.
+ * `*_dpr` → relative directory path.
+ * `*_fp` → file path (unspecified).
+ * `*_fpa` / `*_fpr` → absolute / relative file paths.
+ * `*_fs_nod_pr` / `*_fs_nod_pa` → file-system node paths (relative / absolute) when type (file/dir/other) isn’t fixed.
+
+4. **TapeMachine semantics**
+
+ * First-rest pattern:
+
+ * Tape must be non-empty.
+ * Head (`_head_position`) always on a valid cell; starts at `0`.
+ * `can_step(n)` checks bounds; `step(n)` assumes caller checked.
+ * `read()` returns *just* the current value.
+ * `step()` returns nothing; it just moves the head.
+ * `cue()` is the public accessor for `_head_position`.
+ * `rewind()` sets cue to `0`.
+ * `cue_end()` sets cue to `length-1`.
+ * `entangle()` creates another machine sharing the same tape but with its own head.
+
+5. **Context / Queue conventions**
+
+ * Context objects hold:
+
+ * `dir_dpr` (relative directory path, `'.'` for root),
+ * `level` (depth, root == 1),
+ * child traversal state.
+ * `Context` internally uses a `TapeMachine` for child names; no separate index bookkeeping.
+ * Contexts are only created for directories with **non-empty** child lists.
+ * `Queue` holds `Context` objects and supports tagged pushes:
+
+ * `"front"` (depth-ish),
+ * `"back"` (breadth-ish),
+ * `"throw-away"` (discard).
+ * `Context.clone()` uses entangled `TapeMachine` for children.
+
+6. **TreeMachine semantics**
+
+ * Root stored as absolute `root_dpa`; external API uses paths relative to root.
+ * `step()` advances within the current `Context` and sets the current node.
+ * `read()` returns `(value, property_list, address)` where:
+
+ * `value` is a `fs_nod_pr` relative path,
+ * `property_list` ⊆ `{"directory","file","other","empty"}`,
+ * `address` is `(level, index)`; `level` not derived from path text.
+ * `rightmost()` checks if current context has any more nodes.
+ * `push_current_directory(where_tag)` enqueues non-empty directory contexts.
+ * `pop_context()` drops current context and clears the current head.
+ * `context_depth()` returns the active context depth.
+ * `entangle()` clones the context queue and current node/address, using `Context.clone()`.
+
+7. **CLI vs worker separation**
+
+ * Worker functions take structured arguments (e.g. `root_dp: str`) and do the real work:
+
+ * `build_depth_machine(root_dp)`,
+ * `build_breadth_machine(root_dp)`.
+ * `CLI(argv)`:
+
+ * unpacks `argv`,
+ * calls the worker,
+ * handles printing / side effects,
+ * returns an `int` status.
+ * `if __name__ == "__main__": raise SystemExit(CLI(sys.argv))` – single branch.
+
+8. **Debugging conventions**
+
+ * `meta.DEBUG` is a **set** of tags; use `add`, `discard`, membership tests.
+ * Debug helpers:
+
+ * Check presence of tag (`"TapeMachine"`, `"TreeMachine"`, `"Queue"`, `"DepthMachine"`, `"BreadthMachine"`, etc.).
+ * Print with class name and `hex(id(instance))` prefix:
+
+ * `[TapeMachine 0x...] msg`
+ * `[TreeMachine 0x...] msg`
+ * Tests can enable tags with:
+
+ ```python
+ meta.DEBUG.add("TapeMachine")
+ meta.DEBUG.add("TreeMachine")
+ ```
+
+9. **Versioning via `meta`**
+
+ * `meta` holds at least:
+
+ * `DEBUG` (set of tags),
+ * `version()` that prints `"version <major> <minor>"`.
+ * Modules print version in tests / CLI to track which build is running.
+
+If you’d like, next step could be a tiny “course” doc that shows:
+
+* raw `TreeMachine` usage,
+* `DepthMachine` / `BreadthMachine` as derived tapes,
+* and a short example of entangling and doing a sub-search without losing place.