SearchA-ZF › ><> (Fish) Interpreter

><> (Fish) Interpreter

2009 Open source · Public domain Online

A clean, from-scratch ><> (Fish) interpreter written for emulators.org and running entirely in your browser. ><> (pronounced "fish") is a two-dimensional stack esolang published on esolangs.org in 2009, a playful cousin of Befunge: a program is a 2D codebox of instruction cells, an instruction pointer walks it in one of four directions executing each cell, and a stack of stacks holds the data. Mirrors such as /, \ and # bend the pointer, and the p and g commands write and read the codebox itself, so a program can rewrite its own code as it runs.

The view is the whole machine at once: the codebox with the current instruction-pointer cell highlighted, its direction, the stack, and the output line. It boots running a bouncing "Hello, World!", and plugs into the shared debugger so you can pause, single-step one grid cell at a time, set breakpoints on any (x,y) cell, and watch the stack or a self-modified cell. Any unknown command or an empty-stack pop is the ><> runtime error "something smells fishy...", which is how many ><> programs terminate.

><> (Fish) language specification ↗

Visit the official site ↗

Runs on: Web browser

><> (Fish) Interpreter Online Emulator

Play ><> (Fish) Interpreter using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Hello, World!><> (Fish) InterpreteropenOpen ⛶
Echo input><> (Fish) InterpreteropenOpen ⛶

Chips

Notes

Embedding

><> (Fish) is authored here from scratch, so embedding is just three small files: the VM (fish.js), the canvas view (fish-render.js), and a boot shim that owns the loop. There is no CPU and no ROM. The "machine" is a 2D codebox of instruction cells plus a stack of stacks.

Boot. Create the VM, load a program, wire a renderer, and run your own loop built on vm.step() (execute exactly one grid cell):

var vm = new Fish();
vm.load('"!dlroW ,olleH">o<');        // bounces and prints Hello, World!
var view = new FishRender(canvas, vm);
(function loop(){
  for (var i = 0; i < 200 && !vm.halted; i++) vm.step();  // one cell each
  view.draw();
  if (!vm.halted) requestAnimationFrame(loop);
})();

The machine is plain objects. Everything the debugger needs is a live field; there is no wasm heap:

MemberKindWhat it does
vm.step()methodExecute exactly one grid cell (the current IP cell), then move the IP. The single-step primitive; returns {ok}, {halted} or {waiting}.
vm.gridfieldThe codebox as an Int32Array (row-major, y*96 + x). Poke it to edit the program live.
vm.stackfieldThe active stack (a JS array; top is the last element). vm.stacks is the full stack of stacks.
vm.ip / vm.dirfieldThe instruction pointer {x,y} and its travel direction {x,y}.
vm.stringModefieldThe active quote (' or ") while in string mode, else null (each cell is pushed as its code instead of executed).
vm.queueInput(v)methodFeed stdin for i (input character): pass a string or a number.
vm.load(text) / vm.reset()methodParse a program into the codebox / clear the machine to its initial state.

Debugger integration

Because the VM is ordinary JavaScript and vm.step() is one grid cell, the debugger drives it directly. The boot shim publishes window.EMU_BOOT with a transport whose stepInsn(n) runs n cells, and a run loop that checks the current IP against a breakpoint set every cell:

function runBatch(){
  for (var i = 0; i < speed; i++){
    var idx = vm.ip.y * 96 + vm.ip.x;                // linear cell address
    if (bps.has(idx) && !justResumed) return 'bp';   // break when IP reaches (x,y)
    var ev = vm.step();
    if (ev.halted) return 'halt';
    if (ev.waiting) return 'wait';                // blocked on i input
  }
}

Breakpoints are (x,y) cells, stored as the linear address y*96 + x - the same address pc() returns and the disasm/hex views use, so clicking the gutter next to a cell sets a breakpoint there. Watchpoints cover a written grid cell (via the p path) and the stack (any push/pop). The "registers" are the real machine state: IPX, IPY, direction, string mode and stack depth.

The disassembler is unusual: ><> has no linear instruction stream, so /debugger/src/cpus/fish.js decodes each grid cell as its one-cell command (with the cell's (x,y) and a description). That annotates the codebox-as-memory view without pretending the grid is executed top-to-bottom.

Architecture

><> (Fish) is a two-dimensional stack esolang (esolangs.org, 2009), a playful cousin of Befunge:

  • The codebox - a 2D grid of instruction cells. The instruction pointer walks it in one of four directions and wraps at the edges of the content box.
  • The stack - a stack of integers (in fact a stack of stacks); every operator pushes and pops it. Division makes floating-point values.
  • Mirrors - / \ | _ # bend the pointer, x sends it a random way, and > < ^ v steer it outright.
  • String mode - ' and " each toggle pushing every cell's code until the matching quote returns.
  • Self-modification - p (put) writes a cell and g (get) reads one, so a program can rewrite its own code as it runs. That is why the codebox is the memory the debugger shows.
  • I/O and halting - o/n output a character/number, i reads one, ; halts, and any unknown command or empty-stack pop is the runtime error "something smells fishy...".

This interpreter is a faithful clean-room implementation of that public specification. The ><> language is public domain. There is no external code to trust. The entire machine is a couple of hundred lines of readable JavaScript, which is exactly what makes it a clear debugging target.