Skip to content

Add giles assembler, disassembler and a TUI inspector - #8

Open
gilesknap wants to merge 4 commits into
claude/parallel-enginefrom
claude/giles-tooling
Open

Add giles assembler, disassembler and a TUI inspector#8
gilesknap wants to merge 4 commits into
claude/parallel-enginefrom
claude/giles-tooling

Conversation

@gilesknap

@gilesknap gilesknap commented Jun 21, 2026

Copy link
Copy Markdown
Owner

What this does

Brings back the tooling the original eyes had for reading and editing creature code — an assembler, a disassembler, and an interactive inspector/debugger — for the new giles byte-code genome.

Stacked PR. Rebased onto claude/parallel-engine (the branch of #10, the multithreading PR), so the diff here is only the tooling. #10 is itself stacked on #5, so the merge order is #5#10#8 (or retarget to main once those land). The rebase resolved conflicts in genotype.rs (combined the new Send supertrait with the inspector's trait methods), creature.rs (kept the think/intent model, added the inspect snapshot), and world.rs (the inspector now reads the id_index + Vec creature store instead of the old HashMap).

It is delivered as discrete commits:

1. Disassembler (ec7539b)

  • Refactors giles into a module folder (giles/{genotype,isa,asm}.rs).
  • giles/isa.rs becomes the single source of truth for the instruction set (opcodes, variables, sizes, mnemonic/name tables, operand classification), shared by the VM and the tooling.
  • giles/asm.rs::disassemble decodes a genome into a readable listing, exactly as the VM would (every selector reduced modulo its range, so any byte block is a valid listing).
  • GilesGenotype::disassemble() dumps an evolved creature's code; a curated public facade is exposed at eyes2_lib::giles.

2. Assembler (e09e3f7)

  • giles/asm.rs::assemble parses a listing back into genome bytes — [ADDR] MNEMONIC [OPERAND], with ;/# comments, optional explicit addresses and NOP-filled gaps — returning a typed AssembleError.
  • The disassembler is made boundary-safe so the two are exact inverses on canonical programs: disassemble(assemble(disassemble(g))) == disassemble(g) (property-tested over random genomes).
  • GilesGenotype::from_genome() seeds a creature from assembled source.

3. TUI inspector / debugger (3a6ed09, plus ff44e49 restoring genotype config after world load)

  • A genotype-agnostic GenotypeInspect snapshot + a Genotype::inspect() trait method (default None); giles implements it to expose its registers and a disassembly with the active instruction marked.
  • Creature::inspect()CreatureInspect, carried on WorldGrid (serde-skipped) so it reaches the GUI thread; World gains creature selection that auto-advances if the inspected creature dies.
  • New TUI keys: i open/close inspector, n/p cycle creatures, . single-step the world. The overlay shows registers (IP, accumulator, I/O registers, energy, breed/mutation rate) and a scrolling code view centred on the current instruction.

This addresses your question about the original's assembler/disassembler + code inspection/editing — the disassembler doubles as the debugger's code view. (Full breakpoint-style debugging and an in-GUI editor are not included; you can author/seed creatures via assemble + from_genome in code.)

Verification (after the rebase onto #10)

  • cargo build --release ✅ (whole workspace, incl. the multithreaded engine + curses GUI)
  • cargo test ✅ (22 lib unit tests incl. ISA, disassembler, assembler round-trips and an assembled-program-executes test, all passing under the parallel engine; 2 evolution tests ignored)
  • cargo clippy — only pre-existing style nits remain; none introduced by the rebase

⚠️ The curses TUI can't be run in this headless environment, so the inspector itself is verified by build/clippy/tests only (not visually exercised). The assembler/disassembler it relies on are thoroughly unit-tested.

Example

use eyes2_lib::giles::asm::{assemble, disassemble_to_string};
let code = assemble("LOADV V3\nJNZ 0x0\nMOVC 0x2  ; head east").unwrap();
println!("{}", disassemble_to_string(&code));

🤖 Generated with Claude Code

https://claude.ai/code/session_01Qb4gora6fHgAR5y1WQS6C1

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e79a6ff1-8bc7-4191-b128-824fdcecc02a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/giles-tooling

Comment @coderabbitai help to get the list of available commands and usage tips.

claude added 4 commits June 21, 2026 18:43
Refactor the `giles` genotype into a module folder and add a disassembler
that turns a raw genome into a human readable instruction listing.

- split the shared instruction set architecture (opcodes, variables, sizes,
  mnemonic/name tables, operand classification) into giles/isa.rs as the
  single source of truth for the VM and the tooling
- add giles/asm.rs with disassemble() / disassemble_to_string(), decoding
  each instruction exactly as the VM does (every selector reduced modulo its
  range, so any byte block is a valid listing)
- add GilesGenotype::genome() and ::disassemble() to dump an evolved creature
- expose a curated public facade at eyes2_lib::giles (asm, isa, GilesGenotype)
- tests for the ISA tables/lookups and the disassembler (operand decoding,
  modulo reduction, no-panic on random genomes)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qb4gora6fHgAR5y1WQS6C1
Add the inverse of the disassembler: parse a listing back into genome bytes.

- assemble() parses `[ADDR] MNEMONIC [OPERAND]` lines (the format emitted by
  disassemble), with `;`/`#` comments, optional explicit addresses, and
  NOP-filled gaps; returns a typed AssembleError on bad input
- make disassemble boundary-safe (stop at the last fully-fitting instruction
  rather than wrapping) so disassemble/assemble are exact inverses on
  canonical programs
- add isa::operand_size() helper
- add GilesGenotype::from_genome() to seed a creature from assembled bytes
- tests: hand-written program assemble+disassemble, random-genome round trip
  (disassemble->assemble->disassemble is stable), jump-target round trip,
  assembled program executes in the VM, and error reporting

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qb4gora6fHgAR5y1WQS6C1
Add an interactive inspector so you can see what an evolved genome is doing,
plus the plumbing to surface genotype state to the GUI.

- add a genotype-agnostic GenotypeInspect / InspectLine snapshot and a
  Genotype::inspect() trait method (default None); implement it for giles to
  expose its registers (IP, accumulator, I/O registers, energy, breed and
  mutation rate) and a disassembly with the active instruction marked
- add Creature::inspect() -> CreatureInspect and carry an optional snapshot on
  WorldGrid (serde-skipped) so it reaches the GUI thread
- World gains creature selection (toggle_inspect / select_next / select_prev /
  refresh_inspection), auto-advancing if the inspected creature dies
- GUI: new keys i (open/close inspector), n/p (cycle creatures), . (single-step
  the world); render an overlay pane with registers and a scrolling code view
  centred on the current instruction
- update README and TODO for the inspector and asm tooling

Note: the curses GUI cannot be run in CI/headless, so the inspector is
verified by build, clippy and tests; the disassembler/assembler it relies on
are unit tested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qb4gora6fHgAR5y1WQS6C1
Address review feedback: genotype `config` is #[serde(skip)] and so reset to
Settings::default() on deserialization, leaving a loaded creature using the
wrong world size (movement wraparound) and reproduction energy (breed clamp).

- add set_config() to the Genotype trait (default no-op)
- forward it from Creature::set_config, which the load path already calls
  per creature in store.rs, so loaded genotypes get the real settings
- implement it for giles, random, looker and noop
- test that giles restores its settings via set_config

This is a project-wide fix (random/looker also read config in tick()), not
just giles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qb4gora6fHgAR5y1WQS6C1
@gilesknap
gilesknap force-pushed the claude/giles-tooling branch from aa6d779 to ff44e49 Compare June 21, 2026 18:47
@gilesknap
gilesknap changed the base branch from claude/github-eyes-repos-tadi28 to claude/parallel-engine June 21, 2026 18:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants