PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/moul/x/vm/bf/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
moul / x / vm / bf
Files
9 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/moul/x/vm/bf/v0
gno
0.9

Files (9)

  • README.mdmarkdown
  • gnomod.tomltoml
  • bf.gnogno
  • compile.gnogno
  • machine.gnogno
  • bf_test.gnogno
  • ladder_test.gnogno
  • machine_test.gnogno
  • micro_test.gnogno
  • bf_test.gnogno
    1package bf23import (4	"testing"56	"gno.land/p/moul/x/vm/vmkit/v0"7	"gno.land/p/nt/uassert/v0"8

    Functions

    not supported for pure packages by the node (vm/qfuncs)

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    )
    9
    10// hello is the classic 106 byte hello-world. It writes "Hello World", 11
    11// bytes: the original run.gno comment was optimistic about the trailing "!\n".
    12const hello = "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------."
    13
    14// corpus is the set of programs every ladder level must agree on. Each one is
    15// here because it exercises a rewrite that could plausibly be wrong.
    16var corpus = []struct {
    17 name string
    18 src string
    19 want string
    20}{
    21 {"empty", "", ""},
    22 {"comment only", "this is not brainfuck", ""},
    23 {"hello", hello, "Hello World"},
    24 {"clear idiom", "+++++[-]+++++++++++++++++++++++++++++++++++++++++++++++.", "/"},
    25 {"move idiom", "+++++++++++++++++++++++++++++++++++++++++++++++++++[->+<]>.", "3"},
    26 {"multiply idiom", "+++++[->++++++++++<]>++.", "4"},
    27 {"two targets", "+++[->+>++<<]>+++++++++++++++++++++++++++++++++++++++.>+++++++++++++++++++++++++++++++++++++++++.", "*/"},
    28 {"scan walks", ">+>+>+>+[<]++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.", "H"},
    29 {"scan right", ">>>+++++++++<<<[>]+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.", "?"},
    30 {"nested loops", "++[>++[>+<-]<-]>>+++++++++++++++++++++++++++++++++++++++++++++++++.", "5"},
    31 {"dead run", "+++--+++--+++++++++++++++++++++++++++++++++++++++++++.", "-"},
    32 {"skipped loop", "[++++++++++++++++++++]+++++++++++++++++++++++++++++++++++++++++++++++.", "/"},
    33 {"wraps under zero", "-[>+<-]>++++++++++.", "\t"},
    34}
    35
    36func runLevel(t *testing.T, src string, lvl Level, input string) string {
    37 t.Helper()
    38 prog, err := Compile(src, lvl)
    39 uassert.NoError(t, err)
    40 if prog == nil {
    41 return ""
    42 }
    43 h := vmkit.NewTestHost().WithInput([]byte(input))
    44 m := NewMachine(prog)
    45 _, status := m.Step(h, vmkit.Unmetered)
    46 uassert.Equal(t, "halted", status.String())
    47 return h.OutString()
    48}
    49
    50// TestLadderLevelsAgree is the test the whole ladder rests on: an
    51// optimization that changes the output is not an optimization.
    52func TestLadderLevelsAgree(t *testing.T) {
    53 for _, tc := range corpus {
    54 naive := Execute(tc.src)
    55 uassert.Equal(t, tc.want, naive)
    56
    57 for _, lvl := range []Level{LevelJumps, LevelFuse, LevelIdioms} {
    58 got := runLevel(t, tc.src, lvl, "")
    59 if got != tc.want {
    60 t.Errorf("%s at level %s: got %q, want %q",
    61 tc.name, lvl.String(), got, tc.want)
    62 }
    63 }
    64 }
    65}
    66
    67func TestExecuteBaseline(t *testing.T) {
    68 // Rung 0 is kept verbatim, so this is also a regression test on the
    69 // thing the published cycles-per-op number was measured against.
    70 uassert.Equal(t, "Hello World", Execute(hello))
    71 uassert.Equal(t, 11, len(Execute(hello)))
    72}
    73
    74func TestCompileRejectsUnbalanced(t *testing.T) {
    75 cases := []string{"[", "]", "[[]", "+[->+<", "][", "[[[]]"}
    76 for _, src := range cases {
    77 _, err := CompileDefault(src)
    78 if err == nil {
    79 t.Errorf("Compile(%q) should have failed", src)
    80 }
    81 }
    82}
    83
    84func TestCompileRejectsOversizeSource(t *testing.T) {
    85 src := "+"
    86 for len(src) <= MaxSource {
    87 src += src // doubling, not O(n^2) appending
    88 }
    89 _, err := CompileDefault(src)
    90 uassert.ErrorIs(t, err, ErrSourceTooLong)
    91}
    92
    93func TestCompileRejectsUnknownLevel(t *testing.T) {
    94 _, err := Compile("+", Level(42))
    95 uassert.Error(t, err)
    96 _, err = Compile("+", Level(-1))
    97 uassert.Error(t, err)
    98}
    99
    100// TestLadderShrinksTheOpStream is the compile-time half of the ladder: each
    101// rung has to produce strictly fewer ops than the one below it, or it is not
    102// buying anything.
    103func TestLadderShrinksTheOpStream(t *testing.T) {
    104 jumps, err := Compile(hello, LevelJumps)
    105 uassert.NoError(t, err)
    106 fuse, err := Compile(hello, LevelFuse)
    107 uassert.NoError(t, err)
    108 idioms, err := Compile(hello, LevelIdioms)
    109 uassert.NoError(t, err)
    110
    111 uassert.True(t, jumps.Len() > fuse.Len())
    112 uassert.True(t, fuse.Len() > idioms.Len())
    113 uassert.Equal(t, "jumps", jumps.Level().String())
    114 uassert.Equal(t, hello, idioms.Source())
    115}
    116
    117func TestIdiomRecognition(t *testing.T) {
    118 cases := []struct {
    119 name string
    120 src string
    121 want int // ops, excluding the trailing halt
    122 }{
    123 {"clear", "[-]", 1}, // opSet
    124 {"move", "[->+<]", 2}, // opAddMul, opSet
    125 {"multiply two", "[->++>+++<<]", 3}, // two opAddMul, opSet
    126 {"scan right", "[>]", 1}, // opScan
    127 {"scan left", "[<]", 1}, // opScan
    128 }
    129 for _, tc := range cases {
    130 prog, err := Compile(tc.src, LevelIdioms)
    131 uassert.NoError(t, err)
    132 if prog == nil {
    133 continue
    134 }
    135 if got := prog.Len() - 1; got != tc.want {
    136 t.Errorf("%s: %q compiled to %d ops, want %d",
    137 tc.name, tc.src, got, tc.want)
    138 }
    139 }
    140}
    141
    142// TestPlusLoopIsNotRewritten pins the conservative half of the rewrite: a
    143// loop that adds one per iteration still reaches zero by wrapping, but only
    144// after 256 iterations, and folding it would be a different program. It has
    145// to stay a real loop.
    146func TestPlusLoopIsNotRewritten(t *testing.T) {
    147 prog, err := Compile("[+]", LevelIdioms)
    148 uassert.NoError(t, err)
    149 // opJmpZ, opAdd, opJmpNZ, opHalt: untouched.
    150 uassert.Equal(t, 4, prog.Len())
    151}
    152
    153func TestInputThroughTheHost(t *testing.T) {
    154 // Read three bytes and echo them back.
    155 const echo3 = ",.,.,."
    156 h := vmkit.NewTestHost().WithInput([]byte("gno"))
    157 m, err := Load(echo3)
    158 uassert.NoError(t, err)
    159 _, status := m.Step(h, vmkit.Unmetered)
    160 uassert.Equal(t, "halted", status.String())
    161 uassert.Equal(t, "gno", h.OutString())
    162}
    163
    164func TestInputEndsAsZero(t *testing.T) {
    165 // Past the end of input, `,` yields a zero cell. Without input at all,
    166 // the naive interpreter would have panicked here.
    167 h := vmkit.NewTestHost().WithInput([]byte("a"))
    168 m, err := Load(",.+++++++++++++++++++++++++++++++++++++++++++++++.,+++++++++++++++++++++++++++++++++++++++++++++++++.")
    169 uassert.NoError(t, err)
    170 _, status := m.Step(h, vmkit.Unmetered)
    171 uassert.Equal(t, "halted", status.String())
    172 uassert.Equal(t, "a\x901", h.OutString())
    173}
    174
    175func TestExecutePanicsOnInput(cur realm, t *testing.T) {
    176 // Rung 0's second preserved bug, pinned so nobody "fixes" the baseline.
    177 uassert.PanicsWithMessage(t, cur, "unsupported", func() { Execute(",") })
    178}
    179
    180func TestMachineWithoutProgram(t *testing.T) {
    181 m := &Machine{status: vmkit.Running}
    182 used, status := m.Step(vmkit.NewTestHost(), 10)
    183 uassert.Equal(t, int64(0), used)
    184 uassert.Equal(t, "trapped", status.String())
    185 uassert.Equal(t, "no program", m.Trap())
    186}
    187