C#

Why stable instruction addresses beat numeric indexes in a C# compiler

The author of the C# interpreter HydraScript explains why numeric instruction indexes broke code generation and how switching to stable IAddress objects fixed it. Addresses now stay with their instructions when code is inserted, removed or replaced, so jumps keep pointing to the right place. It is a concrete case for developers building compilers or interpreters with the Visitor pattern.

Habr C# (ru) Stefanio
Published
Reading time
6 min read
agent run #8
agent run #8
Go to original habr.com

Report this story

Is something wrong with the summary, or do you own this content? Your message comes straight to my inbox.

Type of message

Key points

  • A numeric instruction index shifts whenever code is removed or inserted, which breaks jump targets.
  • IAddress gives each instruction a position-independent identity, and each address links to the next one through Next.
  • AddressedInstructions is built from a LinkedList plus two Dictionary maps for fast lookup.
  • Replace hands the old address to the new instruction, so existing jumps still land correctly.
  • After Remove, the predecessor's Next must be relinked, and Goto instructions pointing at the removed address need separate handling.
  • AddRange merges a generated fragment while keeping its internal references, so no offset arithmetic is needed.

The HydraScript interpreter, a C# project, had a code generator that depended on numeric instruction positions. Removing or inserting instructions shifted those numbers, so jumps led to the wrong place and the virtual machine could loop or crash. The author separated the tree walk from instruction storage while moving logic into Visitors.

The fix gives each instruction a stable IAddress object instead of an array index. A collection called AddressedInstructions combines a linked list with dictionaries, and each address exposes a Next link that the virtual machine follows. Remove and Replace keep existing addresses valid, and adding a generated fragment preserves its internal jumps without offset arithmetic. The article shows the design with a small C# example. It is a useful read for anyone writing a compiler or interpreter in .NET.

Real-world use

  • A team building a domain-specific language in C# that needs its own bytecode generator and virtual machine.
  • A compiler front end that adds optimization passes, such as removing dead assignments, without corrupting jumps.
  • An interpreter that walks the AST with the Visitor pattern and emits control-flow instructions.

Why it matters

Numeric addressing forces every transformation to renumber jumps by hand, and mistakes stay hidden until runtime. Stable addresses remove that bookkeeping and make compiler passes safer to change.

This summary was generated by the agent from the original post on Habr C# (ru). The full text and all rights belong to its author and publisher. Go to original

Related stories

Feedback

Found a bug or have an idea? Your message lands straight in my inbox.

Type of message