go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

commit c0d38513d3619675cd7f614c41bbbd58f0cf368d
Author: lash <dev@holbrook.no>
Date:   Fri, 31 Mar 2023 10:52:04 +0100

Initial commit

Diffstat:
Adraft.txt | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ago/go.mod | 3+++
Ago/resource/fs.go | 25+++++++++++++++++++++++++
Ago/resource/fs_test.go | 11+++++++++++
Ago/resource/resource.go | 7+++++++
Ago/state/state.go | 36++++++++++++++++++++++++++++++++++++
Ago/state/state_test.go | 33+++++++++++++++++++++++++++++++++
Ago/vm/opcodes.go | 11+++++++++++
Ago/vm/vm.go | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Ago/vm/vm_test.go | 44++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 416 insertions(+), 0 deletions(-)

diff --git a/draft.txt b/draft.txt @@ -0,0 +1,196 @@ +Input handler: + +Must handle inputs that contain full history aswell as just last input. + +- Previous input +- Current input +- browse state (stack of all selectors) + + +--- + +session state: + +- session id +- input stack +- router +- user state +- symbol stack and output cache (need to know whether list or not) +- current cumulative size of symbol stack +- sizelimit for symbol stack + +debug state: + +- each step: + * (session) + * last input + * state + * symbol + + +--- + +Account states: + +Should be refreshed before each step. + +Interpreted state used to conditionally redirect locations (needs loop detection). + +Always part of context. May change asynchronously. + +Allow for global state overrides (routes immediately, no state changes) + +catch jumps to symbol with back option (e.g. invalid input) +panic jump to symbol and terminates session + +States are defined in VM. The must be REQUIREd before use. + +If states are changed, ALL sessions MUST be invalidated. + +CATCH <symbol> <[!]state> +CROAK <symbol> <[!]state> + + +--- + +Parameter requires: + +Every node should declare zero or more parameter requirements. + +Parameter requirements are inherited in a branch. + +Compiler should complain about redeclarations in same branch or gosub + +Parameter must resolve to a code symbol + +include command to refresh (for example reload list render) + +parameter symbol may only appear once for all branches (when jumping, a parameter require must originate in same place in tree) + +load symbol -> load symbol from this point on up to size. +reload symbol -> flush symbol before rendering, will fail of not previously required + +LOAD <symbol> <size> +RELOAD <symbol> + + +--- + +Navigation: + +Down - keeps inherited requirements +Up - invalidates requirements out of scope, (symbol filled automatically in context of previous "down") +Next - advance content same level (list browsing) +Previous - go back content same level (list browsing) +Goto - jump to other branch clears requirements stack (should still traverse up and down) +Exit - terminate session + +Next and Previous needs to keep an index state. + +List content display chunking must be cached. List content cache must have a limited size. + +0 = UP (if valid) +1 = NEXT +2 = PREVIOUS +... = GOTO / COND / DOWN +00 = EXIT +99 - GOTO top + +(selector = input, display is description displayed in menu) + +UP <selector> <display> <symbol> +DOWN <selector> <display> <symbol> +NEXT <selector> <display> +PREV <selector> <display> +GOTO <selector> <display> <symbol> +EXIT <selector> <display> +COND <selector> <display> <symbol> <[!]state> + +kept in session as a router object: + +SELECTORHASH|SYMBOLHASH + + +--- + +Parameter mapping + +Used to render display + +Referenced by symbol name. + +Must set minsize, maxsize, used by compiler to evaluate display size. + +For symbols having data larger than maxsize, data should be lazily split up to each threshold (to enable previous screens same as initial). Can be defined by byte markers (not substring copies). + +Total capacity for mapping is cumulative maxsize. Next param in execution has available up to net capacity after consume. + +May define ONE sink, which consumes all remaining data. + +Compiler must croak if: + +- parameter has not been loaded in tree +- symbol is not handled by any of the translations. + +should generate warnings if sink cannot render a single enrry (of list) + +MAP <symbol> <maxsize> <minsize> +SINK <symbol> + + +--- + +Display + +matched with this node id. node ids must be unique. should contain only letters and numbers. + +ID <identifier string> + +compile displays with menus. + +--- + +Compiler croaks: + +- Render larger than display size (using maxsize) +- Parameter duplicates between branches +- Parameter mapped outsize context +- Unhandled parameter mappings +- Duplicate navigation selectors in a menu +- Parameter loads exceeding global capacity limit + + +--- + +All code execution symbols only take state object as input and return it as output. + +The output is committed within session. + + +--- + +Header + +Engine version definition file was written for + +VERSION + + +--- + +Debug: + +- full input history +- parameter dump +- capacities, load capacities, used capacities + + +--- + +Tool to generate template. + +- parent id +- generate id +- UP, EXIT, GOTO TOP selector +- browser? if so, NEXT and PREVIOUS +- skip top option diff --git a/go/go.mod b/go/go.mod @@ -0,0 +1,3 @@ +module git.defalsify.org/festive + +go 1.20 diff --git a/go/resource/fs.go b/go/resource/fs.go @@ -0,0 +1,25 @@ +package resource + +import ( + "context" +) + +type FsResource struct { + path string + ctx context.Context +} + +func NewFsResource(path string, ctx context.Context) (FsResource) { + return FsResource{ + path: path, + ctx: ctx, + } +} + +func(fs *FsResource) Get(sym string) (string, error) { + return "", nil +} + +func(fs *FsResource) Render(sym string, values []string) (string, error) { + return "", nil +} diff --git a/go/resource/fs_test.go b/go/resource/fs_test.go @@ -0,0 +1,11 @@ +package resource + +import ( + "context" + "testing" +) + +func TestNewFs(t *testing.T) { + n := NewFsResource("./testdata", context.TODO()) + _ = n +} diff --git a/go/resource/resource.go b/go/resource/resource.go @@ -0,0 +1,7 @@ +package resource + + +type Fetcher interface { + Get(symbol string) (string, error) + Render(symbol string, values map[string]string) (string, error) +} diff --git a/go/state/state.go b/go/state/state.go @@ -0,0 +1,36 @@ +package state + +import ( + "io" +) + +type State struct { + Flags []byte + OutputSize uint16 + CacheSize uint32 + CacheUseSize uint32 + Cache io.ReadWriteSeeker +} + +func NewState(bitSize uint64, outputSize uint16) State { + if bitSize == 0 { + panic("bitsize cannot be 0") + } + n := bitSize % 8 + if n > 0 { + bitSize += (8 - n) + } + + return State{ + Flags: make([]byte, bitSize / 8), + OutputSize: outputSize, + CacheSize: 0, + CacheUseSize: 0, + Cache: nil, + } +} + +func(st State) WithCacheSize(cacheSize uint32) State { + st.CacheSize = cacheSize + return st +} diff --git a/go/state/state_test.go b/go/state/state_test.go @@ -0,0 +1,33 @@ +package state + +import ( + "testing" +) + +func TestNewStateFlags(t *testing.T) { + st := NewState(5, 0) + if len(st.Flags) != 1 { + t.Errorf("invalid state flag length: %v", len(st.Flags)) + } + st = NewState(8, 0) + if len(st.Flags) != 1 { + t.Errorf("invalid state flag length: %v", len(st.Flags)) + } + st = NewState(17, 0) + if len(st.Flags) != 3 { + t.Errorf("invalid state flag length: %v", len(st.Flags)) + + } +} + +func TestNewStateCache(t *testing.T) { + st := NewState(17, 0) + if st.CacheSize != 0 { + t.Errorf("cache size not 0") + } + st = st.WithCacheSize(102525) + if st.CacheSize != 102525 { + t.Errorf("cache size not 102525") + } + +} diff --git a/go/vm/opcodes.go b/go/vm/opcodes.go @@ -0,0 +1,11 @@ +package vm + +const VERSION = 0 + +const ( + CATCH = iota + CROAK + LOAD + RELOAD + _MAX +) diff --git a/go/vm/vm.go b/go/vm/vm.go @@ -0,0 +1,50 @@ +package vm + +import ( + "encoding/binary" + "fmt" + "context" + + "git.defalsify.org/festive/state" + "git.defalsify.org/festive/resource" +) + +type Runner func(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) + +func Run(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + op := binary.BigEndian.Uint16(instruction[:2]) + if op > _MAX { + return st, fmt.Errorf("opcode value %v out of range (%v)", op, _MAX) + } + switch op { + case CATCH: + RunCatch(instruction[2:], st, rs, ctx) + case CROAK: + RunCroak(instruction[2:], st, rs, ctx) + case LOAD: + RunLoad(instruction[2:], st, rs, ctx) + case RELOAD: + RunReload(instruction[2:], st, rs, ctx) + default: + err := fmt.Errorf("Unhandled state: %v", op) + return st, err + } + return st, nil +} + +func RunCatch(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + return st, nil +} + + +func RunCroak(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + return st, nil +} + +func RunLoad(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + return st, nil +} + +func RunReload(instruction []byte, st state.State, rs resource.Fetcher, ctx context.Context) (state.State, error) { + return st, nil +} diff --git a/go/vm/vm_test.go b/go/vm/vm_test.go @@ -0,0 +1,44 @@ +package vm + +import ( + "context" + "fmt" + "testing" + + "git.defalsify.org/festive/state" +) + +type TestResource struct { +} + +func (r *TestResource) Get(sym string) (string, error) { + switch sym { + case "foo": + return "inky pinky blinky clyde", nil + case "bar": + return "inky pinky {.one} blinky {.two} clyde", nil + } + return "", fmt.Errorf("unknown symbol %s", sym) +} + +func (r *TestResource) Render(sym string, values map[string]string) (string, error) { + v, err := r.Get(sym) + return v, err +} + +func TestRun(t *testing.T) { + st := state.NewState(5, 255) + rs := TestResource{} + b := []byte{0x00, 0x02} + r, err := Run(b, st, &rs, context.TODO()) + if err != nil { + t.Errorf("error on valid opcode: %v", err) + } + + b = []byte{0x01, 0x02} + r, err = Run(b, st, &rs, context.TODO()) + if err == nil { + t.Errorf("no error on invalid opcode") + } + _ = r +}