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:
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 {