Download hdc/ide.py from AEUPH/hdcstorage: direct link, hf CLI and curl.
- Browser
- Download file 16.6 kB
-
https://huggingface.co/AEUPH/hdcstorage/resolve/main/hdc/ide.py
- Command line
-
hf download hf://AEUPH/hdcstorage/hdc/ide.py
-
curl -L -o ide.py https://huggingface.co/AEUPH/hdcstorage/resolve/main/hdc/ide.py
16.6 kB
| """The native binary-logic IDE. | |
| A line-oriented front end to the whole machine: edit source, assemble it into | |
| hypervector cells, run and step it, inspect registers symbolically, read raw | |
| bit planes, call HDC-resident tools, and save the storage image. | |
| The IDE is not a separate program with its own model of the code — every view | |
| it shows is produced by the machine: disassembly is a cleanup decode of stored | |
| vectors, the register table's SYMBOL column is a cleanup query, and ``cell`` | |
| prints the bits that are literally resident in storage. When a decode is | |
| uncertain it says so instead of inventing a mnemonic. | |
| Commands (``help`` in the REPL prints this list): | |
| new [file] clear the source buffer | |
| open <file> load a .hdcasm file into the buffer | |
| append <text> append a source line | |
| insert <n> <text> insert before line n | |
| delete <n> delete line n | |
| show [from to] list the buffer with line numbers | |
| save [file] write the buffer back out | |
| asm assemble the buffer into hypervectors | |
| run | go run to halt or the next breakpoint | |
| step [n] | next single step; next steps over a CALL | |
| bp <label|pc> set a breakpoint | |
| unbp <label|pc> clear a breakpoint | |
| bps list breakpoints | |
| regs registers with integer payload and nearest symbol | |
| sim [n] pairwise similarity of the first n registers | |
| disasm [start] [n] disassembly with decode confidence | |
| bits <reg> bit planes of a register | |
| cell <name> bit planes of a storage cell | |
| mem [limit] storage listing | |
| sym cleanup memory contents | |
| trace [n] last n executed instructions | |
| stat storage statistics | |
| tools tool registry | |
| tool <name> [args] call a tool directly | |
| adder [a b] run one addition through the HDC full adder | |
| shader HDC image pipeline: dim, edges, depth, normals | |
| net BitNet layer self-check | |
| opcodes instruction set reference | |
| decode <hex> decode an arbitrary hypervector | |
| reset reset registers; keep storage | |
| saveimg <file.hdc> persist the storage image | |
| openimg <file.hdc> load a storage image | |
| help this list | |
| quit leave | |
| """ | |
| from __future__ import annotations | |
| import os | |
| import shlex | |
| from .agent import ToolRegistry | |
| from .assembler import AsmError, assemble | |
| from .debugger import Debugger | |
| from .hypervec import DEFAULT_DIM, bits, hvc | |
| from .interpreter import Machine | |
| from .isa import DecodeError, decode_instr, opcode_table_text | |
| from .memory import HDCStorage | |
| DEMO_SOURCE = """\ | |
| ; --------------------------------------------------------------- | |
| ; A program that lives entirely in hyperdimensional storage. | |
| ; --------------------------------------------------------------- | |
| .str greeting "hdc storage online" | |
| .data accumulator 0 | |
| start: | |
| ldi R0, 5 | |
| ldi R1, 37 | |
| add R2, R0, R1 ; 42 | |
| out R2 | |
| prints greeting | |
| bind R3, R0, R1 ; bind the two operands | |
| sim R4, R3, R0 ; unrelated: ~0 | |
| out R4 | |
| bind R5, R3, R1 ; unbind recovers R0 | |
| sim R6, R5, R0 ; 1000 milli | |
| out R6 | |
| tool R7, R0, R1, hdc.similarity | |
| out R7 | |
| store accumulator, R2 | |
| load R8, accumulator | |
| out R8 | |
| halt | |
| """ | |
| class IDE: | |
| """Editor + assembler + machine + debugger in one console.""" | |
| def __init__( | |
| self, | |
| d: int = DEFAULT_DIM, | |
| storage: HDCStorage | None = None, | |
| out=print, | |
| ): | |
| self.d = d | |
| self.out = out | |
| self.storage = storage or HDCStorage(d=d, label="ide") | |
| self.registry = ToolRegistry(storage=self.storage, d=d) | |
| self.machine = Machine( | |
| storage=self.storage, d=d, tools=self.registry, echo=self._emit | |
| ) | |
| self.dbg = Debugger(self.machine) | |
| self.src: list[str] = [] | |
| self.program = None | |
| self.filename: str | None = None | |
| self.running = True | |
| self._printed = 0 | |
| # ------------------------------------------------------------- plumbing | |
| def _emit(self, text: str) -> None: | |
| self.out(" | " + text) | |
| def _flush_output(self) -> None: | |
| for line in self.machine.output[self._printed :]: | |
| self.out(" | " + line) | |
| self._printed = len(self.machine.output) | |
| # -------------------------------------------------------------- dispatch | |
| def execute(self, line: str) -> str: | |
| """Run one IDE command; returns text to display.""" | |
| line = line.strip() | |
| if not line or line.startswith("#"): | |
| return "" | |
| try: | |
| argv = shlex.split(line) | |
| except ValueError as exc: | |
| return "parse error: %s" % exc | |
| cmd = argv[0].lower() | |
| args = argv[1:] | |
| handler = getattr(self, "_cmd_" + cmd, None) | |
| aliases = {"go": "run", "next": "step_over", "quit": "quit", "exit": "quit"} | |
| if handler is None and cmd in aliases: | |
| handler = getattr(self, "_cmd_" + aliases[cmd], None) | |
| if handler is None: | |
| return "unknown command %r (try: help)" % cmd | |
| try: | |
| return handler(args) or "" | |
| except (AsmError, DecodeError, KeyError, ValueError, OSError) as exc: | |
| return "%s: %s" % (type(exc).__name__, exc) | |
| # ---------------------------------------------------------------- editor | |
| def _cmd_new(self, args) -> str: | |
| self.src = [] | |
| self.filename = args[0] if args else None | |
| self.program = None | |
| return "buffer cleared" | |
| def _cmd_open(self, args) -> str: | |
| if not args: | |
| return "usage: open <file.hdcasm>" | |
| with open(args[0], "r", encoding="utf-8") as fh: | |
| self.src = fh.read().splitlines() | |
| self.filename = args[0] | |
| self.program = None | |
| return "loaded %d line(s) from %s" % (len(self.src), args[0]) | |
| def _cmd_append(self, args) -> str: | |
| if not args: | |
| return "usage: append <source text>" | |
| self.src.append(" ".join(args)) | |
| return "line %d appended" % len(self.src) | |
| def _cmd_insert(self, args) -> str: | |
| if len(args) < 2: | |
| return "usage: insert <n> <source text>" | |
| n = int(args[0]) | |
| self.src.insert(max(0, n - 1), " ".join(args[1:])) | |
| return "inserted at %d" % n | |
| def _cmd_delete(self, args) -> str: | |
| if not args: | |
| return "usage: delete <n>" | |
| n = int(args[0]) | |
| if not 1 <= n <= len(self.src): | |
| return "no line %d" % n | |
| removed = self.src.pop(n - 1) | |
| return "deleted: %s" % removed | |
| def _cmd_show(self, args) -> str: | |
| if not self.src: | |
| return "<buffer empty>" | |
| lo = int(args[0]) if args else 1 | |
| hi = int(args[1]) if len(args) > 1 else len(self.src) | |
| rows = [] | |
| for i in range(lo, min(hi, len(self.src)) + 1): | |
| rows.append("%3d %s" % (i, self.src[i - 1])) | |
| return "\n".join(rows) | |
| def _cmd_save(self, args) -> str: | |
| path = args[0] if args else self.filename | |
| if not path: | |
| return "usage: save <file.hdcasm>" | |
| with open(path, "w", encoding="utf-8") as fh: | |
| fh.write("\n".join(self.src) + "\n") | |
| self.filename = path | |
| return "wrote %d line(s) to %s" % (len(self.src), path) | |
| # ------------------------------------------------------------- assembling | |
| def _cmd_asm(self, args) -> str: | |
| source = "\n".join(self.src) if self.src else DEMO_SOURCE | |
| if not self.src: | |
| self.src = DEMO_SOURCE.splitlines() | |
| self.program = assemble(source, d=self.d) | |
| self.machine.load(self.program) | |
| self.dbg.attach_program(self.program) | |
| self._printed = 0 | |
| rows = [ | |
| "assembled %d cell(s), %d literal(s), %d string(s), %d cell name(s)" | |
| % ( | |
| len(self.program.instrs), | |
| len(self.program.literals), | |
| len(self.program.strings), | |
| len(self.program.cell_names), | |
| ), | |
| "weakest decode confidence: %.2f" | |
| % min(d.confidence for d in self.program.listing), | |
| self.program.listing_text(), | |
| ] | |
| return "\n".join(rows) | |
| # ---------------------------------------------------------------- running | |
| def _cmd_run(self, args=None) -> str: | |
| stop = self.dbg.continue_() | |
| self._flush_output() | |
| return "stopped: %s" % stop | |
| def _cmd_step(self, args) -> str: | |
| n = int(args[0]) if args else 1 | |
| last = None | |
| for _ in range(max(1, n)): | |
| last = self.dbg.step() | |
| self._flush_output() | |
| if last is None: | |
| return "machine is halted" | |
| return last.render() | |
| def _cmd_step_over(self, args) -> str: | |
| entry = self.dbg.step_over() | |
| self._flush_output() | |
| return entry.render() if entry else "machine is halted" | |
| def _cmd_reset(self, args) -> str: | |
| self.machine.reset() | |
| self._printed = 0 | |
| return "registers cleared; storage kept (%d cells)" % len(self.storage) | |
| # ------------------------------------------------------------ breakpoints | |
| def _cmd_bp(self, args) -> str: | |
| if not args: | |
| return "usage: bp <label|pc>" | |
| where = args[0] | |
| pc = self.dbg.add_break(int(where) if where.isdigit() else where) | |
| return "breakpoint at cell %d" % pc | |
| def _cmd_unbp(self, args) -> str: | |
| if not args: | |
| return "usage: unbp <label|pc>" | |
| where = args[0] | |
| ok = self.dbg.remove_break(int(where) if where.isdigit() else where) | |
| return "cleared" if ok else "no such breakpoint" | |
| def _cmd_bps(self, args) -> str: | |
| return "breakpoints: %s" % (sorted(self.dbg.breakpoints) or "none") | |
| # ------------------------------------------------------------------ views | |
| def _cmd_regs(self, args) -> str: | |
| return self.dbg.regs() | |
| def _cmd_sim(self, args) -> str: | |
| n = int(args[0]) if args else 8 | |
| return self.dbg.sim_table(list(range(min(n, len(self.machine.regs))))) | |
| def _cmd_disasm(self, args) -> str: | |
| start = int(args[0]) if args else None | |
| count = int(args[1]) if len(args) > 1 else 12 | |
| return self.dbg.disasm(start, count) | |
| def _cmd_bits(self, args) -> str: | |
| if not args: | |
| return "usage: bits <reg>" | |
| reg = args[0].upper().lstrip("R") | |
| return self.dbg.bits(int(reg)) | |
| def _cmd_cell(self, args) -> str: | |
| if not args: | |
| return "usage: cell <name>" | |
| return self.dbg.cell_bits(args[0]) | |
| def _cmd_mem(self, args) -> str: | |
| limit = int(args[0]) if args else 24 | |
| return self.storage.dump(limit=limit) | |
| def _cmd_sym(self, args) -> str: | |
| syms = self.storage.symbols() | |
| if not syms: | |
| return "<cleanup memory empty>" | |
| return "\n".join("%3d %s" % (i, s) for i, s in enumerate(syms)) | |
| def _cmd_trace(self, args) -> str: | |
| n = int(args[0]) if args else 10 | |
| return self.dbg.trace_tail(n) | |
| def _cmd_stat(self, args) -> str: | |
| st = self.storage.stat() | |
| rows = ["%-14s %s" % (k, v) for k, v in st.items()] | |
| if self.program is not None: | |
| rows.append("%-14s %d" % ("program_cells", len(self.program.instrs))) | |
| rows.append( | |
| "%-14s %.2f" | |
| % ("min_decode_conf", min(d.confidence for d in self.program.listing)) | |
| ) | |
| return "\n".join(rows) | |
| def _cmd_tools(self, args) -> str: | |
| return self.registry.specs_text() + "\n\n" + self.registry.memory_report() | |
| def _cmd_tool(self, args) -> str: | |
| if not args: | |
| return "usage: tool <name> [args...]" | |
| spec, conf = self.registry.resolve(args[0]) | |
| if spec is None: | |
| return "no tool resembling %r (best %.3f)" % (args[0], conf) | |
| values = [int(a, 0) for a in args[1:]] | |
| args_map = {} | |
| for (pname, kind), value in zip(spec.params, values): | |
| args_map[pname] = value | |
| result = self.registry.call_named(spec.name, args_map) | |
| shown = "0x%x" % result if spec.returns == "hv" else result | |
| return "%s (conf %.3f) -> %s" % (spec.name, conf, shown) | |
| def _cmd_adder(self, args) -> str: | |
| from .adder import HyperAdder, wrap | |
| a = int(args[0]) if args else 7 | |
| b = int(args[1]) if len(args) > 1 else 35 | |
| adder = HyperAdder(d=self.d) | |
| out = adder.add(a, b) | |
| return "\n".join( | |
| [ | |
| adder.table_text(), | |
| "", | |
| "ripple for %d + %d:" % (a, b), | |
| adder.trace_text(8), | |
| " ... %d more stages" % (adder.width - 8), | |
| "\nsum = %d (expected %d) -> %s" | |
| % ( | |
| adder.decode(out), | |
| wrap(a + b), | |
| "ok" if adder.decode(out) == wrap(a + b) else "MISMATCH", | |
| ), | |
| ] | |
| ) | |
| def _cmd_shader(self, args) -> str: | |
| from .shader import demo_report | |
| return demo_report(d=self.d) | |
| def _cmd_net(self, args) -> str: | |
| from .net import selfcheck | |
| report = selfcheck() | |
| return "\n".join("%-20s %s" % (k, v) for k, v in report.items()) | |
| def _cmd_opcodes(self, args) -> str: | |
| return opcode_table_text() | |
| def _cmd_decode(self, args) -> str: | |
| if not args: | |
| return "usage: decode <hex-or-integer>" | |
| hv = int(args[0], 0) | |
| ctx = self.program.context() if self.program else None | |
| dec = decode_instr(hv, ctx) | |
| return "%s %s (conf %.2f, margins %s)" % ( | |
| dec.op, | |
| dec.regs, | |
| dec.confidence, | |
| {k: round(v, 3) for k, v in dec.margins.items()}, | |
| ) | |
| def _cmd_saveimg(self, args) -> str: | |
| path = args[0] if args else "hdcstorage.hdc" | |
| size = self.storage.save(path) | |
| return "saved %s (%d bytes, %d cells)" % (path, size, len(self.storage)) | |
| def _cmd_openimg(self, args) -> str: | |
| if not args: | |
| return "usage: openimg <file.hdc>" | |
| self.storage = HDCStorage.load(args[0]) | |
| self.d = self.storage.d | |
| self.registry = ToolRegistry(storage=self.storage, d=self.d) | |
| self.machine = Machine( | |
| storage=self.storage, d=self.d, tools=self.registry, echo=self._emit | |
| ) | |
| self.dbg = Debugger(self.machine) | |
| return "loaded %r (%d cells, d=%d)" % (args[0], len(self.storage), self.d) | |
| def _cmd_help(self, args) -> str: | |
| return __doc__ or "" | |
| def _cmd_quit(self, args) -> str: | |
| self.running = False | |
| return "bye" | |
| # ------------------------------------------------------------------- loop | |
| def repl(self) -> int: | |
| self.out("HDCSTORAGE IDE d=%d cells=%d tools=%d" | |
| % (self.d, len(self.storage), len(self.registry.specs))) | |
| self.out('type "help" for commands, "asm" to assemble, "run" to execute') | |
| while self.running: | |
| try: | |
| line = input("hdc> ") | |
| except (EOFError, KeyboardInterrupt): | |
| self.out("") | |
| break | |
| text = self.execute(line) | |
| if text: | |
| self.out(text) | |
| return 0 | |
| def run_script(self, text: str) -> int: | |
| """Execute a batch of IDE commands (used by demos and tests).""" | |
| for line in text.splitlines(): | |
| text_out = self.execute(line) | |
| if text_out: | |
| self.out(text_out) | |
| return 0 | |
| def main(argv: list[str] | None = None) -> int: | |
| import sys | |
| argv = list(sys.argv[1:] if argv is None else argv) | |
| d = DEFAULT_DIM | |
| files = [] | |
| i = 0 | |
| while i < len(argv): | |
| if argv[i] == "--d": | |
| d = int(argv[i + 1]) | |
| i += 2 | |
| continue | |
| files.append(argv[i]) | |
| i += 1 | |
| ide = IDE(d=d) | |
| if files: | |
| path = files[0] | |
| if path.endswith(".hdc"): | |
| print(ide._cmd_openimg([path])) | |
| else: | |
| print(ide._cmd_open([path])) | |
| print(ide._cmd_asm([])) | |
| print(ide._cmd_run([])) | |
| if sys.stdin.isatty(): | |
| return ide.repl() | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |