Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

bf.gno

3.49 Kb · 108 lines
  1// Package bf is a Brainfuck machine for gno.land, and the measuring stick the
  2// rest of the guest-VM work is calibrated against.
  3//
  4// It ships two things that look alike and are not:
  5//
  6//   - [Execute], the naive interpreter: a switch over the source bytes, a
  7//     brace matcher that rescans the program on every loop edge. It is kept
  8//     verbatim as rung 0 of the optimization ladder, because a baseline you
  9//     have edited is not a baseline.
 10//   - [Compile] plus [Machine], the real one: the program is compiled to an
 11//     op array with resolved jumps, runs of identical operators are fused,
 12//     the common loop idioms become single ops, and execution is metered by
 13//     a caller-supplied fuel budget against a
 14//     [vmkit](/p/moul/x/vm/vmkit/v0) Host, so a program can pause when it
 15//     runs out and resume in a later transaction.
 16//
 17// The ladder between them is measured, not asserted: see the README for the
 18// table, and ladder_test.gno for the harness that produces it.
 19//
 20// Live demo: [r/moul/x/vm/bfdemo](/r/moul/x/vm/bfdemo/v0).
 21package bf
 22
 23import "strings"
 24
 25// NaiveTapeSize is the tape length used by [Execute]. It is 30,000 because
 26// that is what the original had, and rung 0 is not allowed to drift.
 27const NaiveTapeSize = 30000
 28
 29// Execute runs code on a fresh tape and returns everything it wrote, using
 30// the original 2023 implementation: no compilation step, one switch per
 31// source byte, and a brace matcher that rescans the source every time a loop
 32// opens or closes.
 33//
 34// It is rung 0 of the ladder and exists to be measured. Two of its properties
 35// are bugs that are deliberately preserved, and are the reason nothing should
 36// call it on untrusted input:
 37//
 38//   - `,` panics with "unsupported": there is no input.
 39//   - unbalanced brackets run the scan off the end of the source and panic
 40//     with an index out of range.
 41//
 42// Use [Compile] and [Machine] for anything real: they reject a malformed
 43// program up front, read input from the host, and cannot run unbounded.
 44func Execute(code string) string {
 45	var (
 46		memory  = make([]byte, NaiveTapeSize) // memory tape
 47		pointer = 0                           // initial memory pointer
 48		buf     strings.Builder
 49	)
 50
 51	// Loop through each character in the code
 52	for i := 0; i < len(code); i++ {
 53		switch code[i] {
 54		case '>':
 55			// Increment memory pointer
 56			pointer++
 57			if pointer >= NaiveTapeSize {
 58				pointer = 0
 59			}
 60		case '<':
 61			// Decrement memory pointer
 62			pointer--
 63			if pointer < 0 {
 64				pointer = NaiveTapeSize - 1
 65			}
 66		case '+':
 67			// Increment the byte at the memory pointer
 68			memory[pointer]++
 69		case '-':
 70			// Decrement the byte at the memory pointer
 71			memory[pointer]--
 72		case '.':
 73			// Output the byte at the memory pointer
 74			buf.WriteByte(memory[pointer])
 75		case ',':
 76			// Input a byte and store it in the memory
 77			panic("unsupported")
 78		case '[':
 79			// Jump forward past the matching ']' if the byte at the memory pointer is zero
 80			if memory[pointer] == 0 {
 81				braceCount := 1
 82				for braceCount > 0 {
 83					i++
 84					if code[i] == '[' {
 85						braceCount++
 86					} else if code[i] == ']' {
 87						braceCount--
 88					}
 89				}
 90			}
 91		case ']':
 92			// Jump backward to the matching '[' if the byte at the memory pointer is nonzero
 93			if memory[pointer] != 0 {
 94				braceCount := 1
 95				for braceCount > 0 {
 96					i--
 97					if code[i] == ']' {
 98						braceCount++
 99					} else if code[i] == '[' {
100						braceCount--
101					}
102				}
103				i-- // Move back one more to compensate for the upcoming increment in the loop
104			}
105		}
106	}
107	return buf.String()
108}