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

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}