go-vise

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

commit 2c585fdd52a4b21304b4d386805f324ed3cf2d4e
parent 789f38411f8a83c69ea0968b25cbafc26bd6e84f
Author: lash <dev@holbrook.no>
Date:   Sun,  1 Sep 2024 00:07:51 +0100

More inline doc, add unregistered label in state flag debug output

Diffstat:
Mexamples/db/main.go | 2+-
Mexamples/gdbm/main.go | 2+-
Mresource/db.go | 21+++++++++++++--------
Mresource/db_test.go | 10+++++++---
Mresource/resource.go | 34++++++++++++++++++++++++----------
Mstate/debug.go | 10+++++++++-
Mstate/debug_test.go | 9+++++----
Mstate/flag.go | 21++++++++++++++++-----
Mstate/log.go | 2+-
Mstate/state.go | 32+++++++++++++++-----------------
Mvm/runner.go | 8+++++---
11 files changed, 97 insertions(+), 54 deletions(-)

diff --git a/examples/db/main.go b/examples/db/main.go @@ -122,7 +122,7 @@ func main() { } store.SetLock(db.DATATYPE_TEMPLATE | db.DATATYPE_MENU | db.DATATYPE_BIN, true) - tg, err := resource.NewDbFuncGetter(store, db.DATATYPE_TEMPLATE, db.DATATYPE_MENU, db.DATATYPE_BIN) + tg, err := resource.NewDbResource(store, db.DATATYPE_TEMPLATE, db.DATATYPE_MENU, db.DATATYPE_BIN) if err != nil { panic(err) } diff --git a/examples/gdbm/main.go b/examples/gdbm/main.go @@ -40,7 +40,7 @@ func main() { panic(err) } - tg, err := resource.NewDbFuncGetter(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN) + tg, err := resource.NewDbResource(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN) if err != nil { panic(err) } diff --git a/resource/db.go b/resource/db.go @@ -12,14 +12,16 @@ const ( resource_max_datatype = db.DATATYPE_TEMPLATE ) -type dbGetter struct { +type dbResource struct { + MenuResource typs uint8 db db.Db } -func NewDbFuncGetter(store db.Db, typs... uint8) (*dbGetter, error) { +// NewDbFuncGetter returns a MenuResource that uses the given db.Db implementation as data retriever. +func NewDbResource(store db.Db, typs... uint8) (*dbResource, error) { var v uint8 - g := &dbGetter{ + g := &dbResource{ db: store, } for _, v = range(typs) { @@ -31,11 +33,11 @@ func NewDbFuncGetter(store db.Db, typs... uint8) (*dbGetter, error) { return g, nil } -func(g *dbGetter) fn(ctx context.Context, sym string) ([]byte, error) { +func(g *dbResource) fn(ctx context.Context, sym string) ([]byte, error) { return g.db.Get(ctx, []byte(sym)) } -func(g *dbGetter) sfn(ctx context.Context, sym string) (string, error) { +func(g *dbResource) sfn(ctx context.Context, sym string) (string, error) { b, err := g.fn(ctx, sym) if err != nil { return "", err @@ -43,7 +45,8 @@ func(g *dbGetter) sfn(ctx context.Context, sym string) (string, error) { return string(b), nil } -func(g *dbGetter) GetTemplate(ctx context.Context, sym string) (string, error) { +// GetTemplate implements the Resource interface. +func(g *dbResource) GetTemplate(ctx context.Context, sym string) (string, error) { if g.typs & db.DATATYPE_TEMPLATE == 0{ return "", errors.New("not a template getter") } @@ -51,7 +54,8 @@ func(g *dbGetter) GetTemplate(ctx context.Context, sym string) (string, error) { return g.sfn(ctx, sym) } -func(g *dbGetter) GetMenu(ctx context.Context, sym string) (string, error) { +// GetTemplate implements the Resource interface. +func(g *dbResource) GetMenu(ctx context.Context, sym string) (string, error) { if g.typs & db.DATATYPE_MENU == 0{ return "", errors.New("not a menu getter") } @@ -59,7 +63,8 @@ func(g *dbGetter) GetMenu(ctx context.Context, sym string) (string, error) { return g.sfn(ctx, sym) } -func(g *dbGetter) GetCode(ctx context.Context, sym string) ([]byte, error) { +// GetTemplate implements the Resource interface. +func(g *dbResource) GetCode(ctx context.Context, sym string) ([]byte, error) { if g.typs & db.DATATYPE_BIN == 0{ return nil, errors.New("not a code getter") } diff --git a/resource/db_test.go b/resource/db_test.go @@ -9,13 +9,17 @@ import ( ) func TestDb(t *testing.T) { + var rsifc Resource ctx := context.Background() store := db.NewMemDb(ctx) store.Connect(ctx, "") - tg, err := NewDbFuncGetter(store, db.DATATYPE_TEMPLATE) + tg, err := NewDbResource(store, db.DATATYPE_TEMPLATE) if err != nil { t.Fatal(err) } + // check that it fulfills interface + rsifc = tg + _ = rsifc rs := NewMenuResource() rs.WithTemplateGetter(tg.GetTemplate) @@ -59,7 +63,7 @@ func TestDb(t *testing.T) { t.Fatal("expected error") } - tg, err = NewDbFuncGetter(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN) + tg, err = NewDbResource(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN) if err != nil { t.Fatal(err) } @@ -74,7 +78,7 @@ func TestDb(t *testing.T) { t.Fatalf("expected 'deadbeef', got %x", b) } - tg, err = NewDbFuncGetter(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN, db.DATATYPE_MENU) + tg, err = NewDbResource(store, db.DATATYPE_TEMPLATE, db.DATATYPE_BIN, db.DATATYPE_MENU) if err != nil { t.Fatal(err) } diff --git a/resource/resource.go b/resource/resource.go @@ -14,19 +14,33 @@ type Result struct { FlagReset []uint32 // request caller to reset error flags at given indices. } -// EntryFunc is a function signature for retrieving value for a key -type EntryFunc func(ctx context.Context, sym string, input []byte) (Result, error) -type CodeFunc func(ctx context.Context, sym string) ([]byte, error) -type MenuFunc func(ctx context.Context, sym string) (string, error) -type TemplateFunc func(ctx context.Context, sym string) (string, error) -type FuncForFunc func(sym string) (EntryFunc, error) +// EntryFunc is a function signature for a function that resolves the symbol of a LOAD instruction. +// +// The EntryFunc receives the current input buffer from the client, aswell as the symbol of the current state node being executed. +// +// The implementer MUST NOT modify state flags or cache inside the function. The resource.Result object MUST be used instead. +type EntryFunc func(ctx context.Context, nodeSym string, input []byte) (Result, error) +// CodeFunc is the function signature for retrieving bytecode for a given symbol. +type CodeFunc func(ctx context.Context, nodeSym string) ([]byte, error) +// MenuFunc is the function signature for retrieving menu symbol resolution. +type MenuFunc func(ctx context.Context, menuSym string) (string, error) +// TemplateFunc is the function signature for retrieving a render template for a given symbol. +type TemplateFunc func(ctx context.Context, nodeSym string) (string, error) +// FuncForFunc is a function that returns an EntryFunc associated with a LOAD instruction symbol. +type FuncForFunc func(loadSym string) (EntryFunc, error) // Resource implementation are responsible for retrieving values and templates for symbols, and can render templates from value dictionaries. +// +// All methods must fail if the symbol cannot be resolved. type Resource interface { - GetTemplate(ctx context.Context, sym string) (string, error) // Get the template for a given symbol. - GetCode(ctx context.Context, sym string) ([]byte, error) // Get the bytecode for the given symbol. - GetMenu(ctx context.Context, sym string) (string, error) // Receive menu test for menu symbol. - FuncFor(sym string) (EntryFunc, error) // Resolve symbol content point for. + // GetTemplate retrieves a render template associated with the given symbol. + GetTemplate(ctx context.Context, nodeSym string) (string, error) + // GetCode retrieves the bytecode associated with the given symbol. + GetCode(ctx context.Context, nodeSym string) ([]byte, error) + // GetMenu retrieves the menu label associated with the given symbol. + GetMenu(ctx context.Context, menuSym string) (string, error) + // FuncFor retrieves the external function (EntryFunc) associated with the given symbol. + FuncFor(loadSym string) (EntryFunc, error) } // MenuResource contains the base definition for building Resource implementations. diff --git a/state/debug.go b/state/debug.go @@ -5,6 +5,10 @@ import ( "strings" ) +const ( + unknown_flag_description = "?unreg?" +) + type flagDebugger struct { flagStrings map[uint32]string } @@ -44,7 +48,11 @@ func(fd *flagDebugger) AsList(flags []byte, length uint32) []string { var i uint32 for i = 0; i < length + 8; i++ { if getFlag(i, flags) { - s := fmt.Sprintf("%s(%v)", fd.flagStrings[i], i) + v, ok := fd.flagStrings[i] + if !ok { + v = unknown_flag_description + } + s := fmt.Sprintf("%s(%v)", v, i) r = append(r, s) } } diff --git a/state/debug_test.go b/state/debug_test.go @@ -25,9 +25,9 @@ func TestDebugFlagString(t *testing.T) { if err != nil { t.Fatal(err) } - flags := []byte{0x06, 0x09} - r := FlagDebugger.AsString(flags, 4) - expect := "INTERNAL_INMATCH(1),INTERNAL_DIRTY(2),FOO(8),BAZ(11)" + flags := []byte{0x06, 0x19} + r := FlagDebugger.AsString(flags, 5) + expect := "INTERNAL_INMATCH(1),INTERNAL_WAIT(2),FOO(8),BAZ(11),?unreg?(12)" if r != expect { t.Fatalf("expected '%s', got '%s'", expect, r) } @@ -45,8 +45,9 @@ func TestDebugState(t *testing.T) { st.Down("root") r := fmt.Sprintf("%s", st) - expect := "moves: 1 idx: 0 flags: INTERNAL_DIRTY(2),FOO(8) path: root lang: (default)" + expect := "moves: 1 idx: 0 flags: INTERNAL_DIRTY(4),FOO(8) path: root lang: (default)" if r != expect { t.Fatalf("expected '%s', got '%s'", expect, r) } } + diff --git a/state/flag.go b/state/flag.go @@ -1,24 +1,35 @@ package state const ( + // Currently reading input. Set by first INCMP instruction encontered. FLAG_READIN = iota + // Input matched a selector. Set by first INCMP matching input. FLAG_INMATCH - FLAG_DIRTY + // The instruction HALT has been encountered. FLAG_WAIT + // The last LOAD or RELOAD executed returneded an error. FLAG_LOADFAIL + // A LOAD or RELOAD has returned fresh data. + FLAG_DIRTY + // Not currently in use. FLAG_RESERVED + // VM execution is blocked. FLAG_TERMINATE + // The return value from a LOAD or RELOAD is a new language selection. FLAG_LANG + // User-defined flags start here. FLAG_USERSTART = 8 ) +const ( + nonwriteable_flag_threshold = FLAG_RESERVED +) + +// IsWriteableFlag returns true if flag can be set by implementer code. func IsWriteableFlag(flag uint32) bool { - if flag > 5 { + if flag > nonwriteable_flag_threshold { return true } - //if flag & FLAG_WRITEABLE > 0 { - // return true - //} return false } diff --git a/state/log.go b/state/log.go @@ -5,5 +5,5 @@ import ( ) var ( - Logg logging.Logger = logging.NewVanilla().WithDomain("state") + logg logging.Logger = logging.NewVanilla().WithDomain("state") ) diff --git a/state/state.go b/state/state.go @@ -7,12 +7,9 @@ import ( "git.defalsify.org/vise.git/lang" ) -type IndexError struct { -} - -func(err *IndexError) Error() string { - return fmt.Sprintf("already at first index") -} +var ( + IndexError = fmt.Errorf("already at first index") +) // State holds the command stack, error condition of a unique execution session. // @@ -20,9 +17,9 @@ func(err *IndexError) Error() string { // // Cached values are linked to the command stack level it which they were loaded. When they go out of scope they are freed. // -// Values must be mapped to a level in order to be available for retrieval and count towards size +// It can hold a single argument, which is freed once it is read. // -// It can hold a single argument, which is freed once it is read +// Values must be mapped to a level in order to be available for retrieval and count towards size. // // Symbols are loaded with individual size limitations. The limitations apply if a load symbol is updated. Symbols may be added with a 0-value for limits, called a "sink." If mapped, the sink will consume all net remaining size allowance unused by other symbols. Only one sink may be mapped per level. // @@ -90,6 +87,7 @@ func NewState(BitSize uint32) State { return st } +// UseDebug enables rendering of registered string values of state flags in the string representation. func(st *State) UseDebug() { st.debug = true } @@ -215,7 +213,7 @@ func(st *State) Next() (uint16, error) { } st.SizeIdx += 1 s, idx := st.Where() - Logg.Debugf("next page", "location", s, "index", idx) + logg.Debugf("next page", "location", s, "index", idx) st.Moves += 1 return st.SizeIdx, nil } @@ -232,11 +230,11 @@ func(st *State) Previous() (uint16, error) { return 0, fmt.Errorf("state root node not yet defined") } if st.SizeIdx == 0 { - return 0, &IndexError{} // ("already at first index") + return 0, IndexError } st.SizeIdx -= 1 s, idx := st.Where() - Logg.Debugf("previous page", "location", s, "index", idx) + logg.Debugf("previous page", "location", s, "index", idx) st.Moves += 1 return st.SizeIdx, nil } @@ -249,7 +247,7 @@ func(st *State) Sides() (bool, bool) { return false, false } next := true - Logg.Tracef("sides", "index", st.SizeIdx) + logg.Tracef("sides", "index", st.SizeIdx) if st.SizeIdx == 0 { return next, false } @@ -288,14 +286,14 @@ func(st *State) Up() (string, error) { if l == 0 { return "", fmt.Errorf("exit called beyond top frame") } - Logg.Tracef("execpath before", "path", st.ExecPath) + logg.Tracef("execpath before", "path", st.ExecPath) st.ExecPath = st.ExecPath[:l-1] sym := "" if len(st.ExecPath) > 0 { sym = st.ExecPath[len(st.ExecPath)-1] } st.SizeIdx = 0 - Logg.Tracef("execpath after", "path", st.ExecPath) + logg.Tracef("execpath after", "path", st.ExecPath) st.Moves += 1 return sym, nil } @@ -308,13 +306,13 @@ func(st *State) Depth() uint8 { // Appendcode adds the given bytecode to the end of the existing code. func(st *State) AppendCode(b []byte) error { st.Code = append(st.Code, b...) - Logg.Debugf("code changed (append)", "code", b) + logg.Debugf("code changed (append)", "code", b) return nil } // SetCode replaces the current bytecode with the given bytecode. func(st *State) SetCode(b []byte) { - Logg.Debugf("code changed (set)", "code", b) + logg.Debugf("code changed (set)", "code", b) st.Code = b } @@ -362,7 +360,7 @@ func(st *State) SetLanguage(code string) error { return err } st.Language = &l - Logg.Infof("language set", "language", l) + logg.Infof("language set", "language", l) return nil } diff --git a/vm/runner.go b/vm/runner.go @@ -2,6 +2,7 @@ package vm import ( "context" + "errors" "fmt" "git.defalsify.org/vise.git/cache" @@ -358,9 +359,10 @@ func(vm *Vm) runInCmp(ctx context.Context, b []byte) ([]byte, error) { vm.st.ResetFlag(state.FLAG_READIN) newSym, _, err := applyTarget([]byte(sym), vm.st, vm.ca, ctx) - - _, ok := err.(*state.IndexError) - if ok { + + //_, ok := err.(*state.IndexError) + //if ok { + if errors.Is(err, state.IndexError) { vm.st.SetFlag(state.FLAG_READIN) return b, nil } else if err != nil {