go-vise

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

commit 0ee0159b513122c43680c1e98a2838a322d32ba9
parent f322102e49b4c8f2a00679aee9428faf3c4e2241
Author: lash <dev@holbrook.no>
Date:   Wed, 28 Aug 2024 02:42:39 +0100

Add gdbm persister

Diffstat:
Mdev/gdbm/main.go | 10+++++-----
Aexamples/gdbm/Makefile | 11+++++++++++
Aexamples/gdbm/aiee.vis | 2++
Aexamples/gdbm/main.go | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/gdbm/menu | 2++
Aexamples/gdbm/menu.vis | 5+++++
Aexamples/gdbm/quit_menu | 2++
Aexamples/gdbm/root | 2++
Aexamples/gdbm/root.vis | 2++
Aexamples/gdbm/root_nor | 2++
Mpersist/fs.go | 11+++++++++--
Apersist/gdbm.go | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mresource/fs.go | 6------
Mresource/gdbm.go | 25++++++++++---------------
14 files changed, 203 insertions(+), 28 deletions(-)

diff --git a/dev/gdbm/main.go b/dev/gdbm/main.go @@ -12,7 +12,7 @@ import ( gdbm "github.com/graygnuorg/go-gdbm" - "git.defalsify.org/vise.git/resource" + "git.defalsify.org/vise.git/db" ) var ( @@ -45,7 +45,7 @@ func(sc *scanner) Scan(fp string, d fs.DirEntry, err error) error { if err != nil { return err } - typ = resource.FSRESOURCETYPE_UNKNOWN + typ = db.DATATYPE_UNKNOWN if d.IsDir() { return nil } @@ -53,9 +53,9 @@ func(sc *scanner) Scan(fp string, d fs.DirEntry, err error) error { fb := path.Base(fp) switch fx { case binaryPrefix: - typ = resource.FSRESOURCETYPE_BIN + typ = db.DATATYPE_BIN case templatePrefix: - typ = resource.FSRESOURCETYPE_TEMPLATE + typ = db.DATATYPE_TEMPLATE default: log.Printf("skip foreign file: %s", fp) return nil @@ -72,7 +72,7 @@ func(sc *scanner) Scan(fp string, d fs.DirEntry, err error) error { log.Printf("fx fb %s %s", fx, fb) ft := fb[:len(fb)-len(fx)] - k := resource.ToDbKey(typ, ft, nil) + k := db.ToDbKey(typ, ft, nil) err = sc.db.Store(k, v, true) if err != nil { return err diff --git a/examples/gdbm/Makefile b/examples/gdbm/Makefile @@ -0,0 +1,11 @@ +INPUTS = $(wildcard ./*.vis) +TXTS = $(wildcard ./*.txt.orig) + +%.vis: + go run ../../dev/asm $(basename $@).vis > $(basename $@).bin + go run ../../dev/gdbm/main.go . + +all: $(INPUTS) $(TXTS) + +%.txt.orig: + cp -v $(basename $@).orig $(basename $@) diff --git a/examples/gdbm/aiee.vis b/examples/gdbm/aiee.vis @@ -0,0 +1,2 @@ +LOAD do 0 +HALT diff --git a/examples/gdbm/main.go b/examples/gdbm/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "context" + "fmt" + "os" + "path" + + testdataloader "github.com/peteole/testdata-loader" + + "git.defalsify.org/vise.git/cache" + "git.defalsify.org/vise.git/engine" + "git.defalsify.org/vise.git/resource" + "git.defalsify.org/vise.git/state" +) + +var ( + baseDir = testdataloader.GetBasePath() + scriptDir = path.Join(baseDir, "examples", "gdbm") + dbFile = path.Join(scriptDir, "vise.gdbm") +) + +func do(ctx context.Context, sym string, input []byte) (resource.Result, error) { + return resource.Result{ + Content: "bye", + }, nil +} + +func main() { + var err error + root := "root" + fmt.Fprintf(os.Stderr, "starting session at symbol '%s' using resource dir: %s\n", root, scriptDir) + + st := state.NewState(0) + rs := resource.NewGdbmResource(dbFile) + ca := cache.NewCache() + if err != nil { + panic(err) + } + cfg := engine.Config{ + Root: "root", + Language: "nor", + } + ctx := context.Background() + en := engine.NewEngine(ctx, cfg, &st, rs, ca) + + rs.AddLocalFunc("do", do) + + _, err = en.Init(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err) + os.Exit(1) + } + err = engine.Loop(ctx, &en, os.Stdin, os.Stdout) + if err != nil { + fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err) + os.Exit(1) + } +} diff --git a/examples/gdbm/menu b/examples/gdbm/menu @@ -0,0 +1 @@ +welcome +\ No newline at end of file diff --git a/examples/gdbm/menu.vis b/examples/gdbm/menu.vis @@ -0,0 +1,5 @@ +MOUT again 0 +MOUT quit 1 +HALT +INCMP ^ 0 +INCMP aiee 1 diff --git a/examples/gdbm/quit_menu b/examples/gdbm/quit_menu @@ -0,0 +1 @@ +or quit +\ No newline at end of file diff --git a/examples/gdbm/root b/examples/gdbm/root @@ -0,0 +1 @@ +ready? +\ No newline at end of file diff --git a/examples/gdbm/root.vis b/examples/gdbm/root.vis @@ -0,0 +1,2 @@ +HALT +INCMP menu * diff --git a/examples/gdbm/root_nor b/examples/gdbm/root_nor @@ -0,0 +1 @@ +klar? +\ No newline at end of file diff --git a/persist/fs.go b/persist/fs.go @@ -67,8 +67,12 @@ func(p *FsPersister) Save(key string) error { return err } fp := path.Join(p.dir, key) + err = ioutil.WriteFile(fp, b, 0600) + if err != nil { + return err + } Logg.Debugf("saved state and cache", "key", key, "bytecode", p.State.Code, "flags", p.State.Flags) - return ioutil.WriteFile(fp, b, 0600) + return nil } // Load implements the Persister interface. @@ -79,6 +83,9 @@ func(p *FsPersister) Load(key string) error { return err } err = p.Deserialize(b) + if err != nil { + return err + } Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code) - return err + return nil } diff --git a/persist/gdbm.go b/persist/gdbm.go @@ -0,0 +1,92 @@ +package persist + +import ( + "github.com/fxamacker/cbor/v2" + gdbm "github.com/graygnuorg/go-gdbm" + + "git.defalsify.org/vise.git/cache" + "git.defalsify.org/vise.git/state" + "git.defalsify.org/vise.git/db" +) + +// gdbmPersister is an implementation of Persister that saves state to the file system. +type gdbmPersister struct { + State *state.State + Memory *cache.Cache + db *gdbm.Database +} + +func NewGdbmPersiser(fp string) *gdbmPersister { + gdb, err := gdbm.Open(fp, gdbm.ModeReader) + if err != nil { + panic(err) + } + return NewGdbmPersisterFromDatabase(gdb) +} + +func NewGdbmPersisterFromDatabase(gdb *gdbm.Database) *gdbmPersister { + return &gdbmPersister{ + db: gdb, + } +} + +// WithContent sets a current State and Cache object. +// +// This method is normally called before Serialize / Save. +func(p *gdbmPersister) WithContent(st *state.State, ca *cache.Cache) *gdbmPersister { + p.State = st + p.Memory = ca + return p +} + +// TODO: DRY +// GetState implements the Persister interface. +func(p *gdbmPersister) GetState() *state.State { + return p.State +} + +// GetMemory implements the Persister interface. +func(p *gdbmPersister) GetMemory() cache.Memory { + return p.Memory +} + +// Serialize implements the Persister interface. +func(p *gdbmPersister) Serialize() ([]byte, error) { + return cbor.Marshal(p) +} + +// Deserialize implements the Persister interface. +func(p *gdbmPersister) Deserialize(b []byte) error { + err := cbor.Unmarshal(b, p) + return err +} + +// Save implements the Persister interface. +func(p *gdbmPersister) Save(key string) error { + b, err := p.Serialize() + if err != nil { + return err + } + k := db.ToDbKey(db.DATATYPE_STATE, key, nil) + err = p.db.Store(k, b, true) + if err != nil { + return err + } + Logg.Debugf("saved state and cache", "key", key, "bytecode", p.State.Code, "flags", p.State.Flags) + return nil +} + +// Load implements the Persister interface. +func(p *gdbmPersister) Load(key string) error { + k := db.ToDbKey(db.DATATYPE_STATE, key, nil) + b, err := p.db.Fetch(k) + if err != nil { + return err + } + err = p.Deserialize(b) + if err != nil { + return err + } + Logg.Debugf("loaded state and cache", "key", key, "bytecode", p.State.Code) + return nil +} diff --git a/resource/fs.go b/resource/fs.go @@ -13,12 +13,6 @@ import ( "git.defalsify.org/vise.git/lang" ) -const ( - FSRESOURCETYPE_UNKNOWN = iota - FSRESOURCETYPE_BIN - FSRESOURCETYPE_TEMPLATE -) - type FsResource struct { MenuResource Path string diff --git a/resource/gdbm.go b/resource/gdbm.go @@ -6,44 +6,39 @@ import ( gdbm "github.com/graygnuorg/go-gdbm" "git.defalsify.org/vise.git/lang" + "git.defalsify.org/vise.git/db" ) - type gdbmResource struct { db *gdbm.Database fns map[string]EntryFunc } func NewGdbmResource(fp string) *gdbmResource { - db, err := gdbm.Open(fp, gdbm.ModeReader) + gdb, err := gdbm.Open(fp, gdbm.ModeReader) if err != nil { panic(err) } - return &gdbmResource{ - db: db, - } + return NewGdbmResourceFromDatabase(gdb) } -func ToDbKey(typ uint8, s string, l *lang.Language) []byte { - k := []byte{typ} - if l != nil && l.Code != "" { - s += "_" + l.Code +func NewGdbmResourceFromDatabase(gdb *gdbm.Database) *gdbmResource { + return &gdbmResource{ + db: gdb, } - return append(k, []byte(s)...) } - func(dbr *gdbmResource) GetTemplate(ctx context.Context, sym string) (string, error) { var ln lang.Language v := ctx.Value("Language") if v != nil { ln = v.(lang.Language) } - k := ToDbKey(FSRESOURCETYPE_TEMPLATE, sym, &ln) + k := db.ToDbKey(db.DATATYPE_TEMPLATE, sym, &ln) r, err := dbr.db.Fetch(k) if err != nil { if err.(*gdbm.GdbmError).Is(gdbm.ErrItemNotFound) { - k = ToDbKey(FSRESOURCETYPE_TEMPLATE, sym, nil) + k = db.ToDbKey(db.DATATYPE_TEMPLATE, sym, nil) r, err = dbr.db.Fetch(k) if err != nil { return "", err @@ -54,7 +49,7 @@ func(dbr *gdbmResource) GetTemplate(ctx context.Context, sym string) (string, er } func(dbr *gdbmResource) GetCode(sym string) ([]byte, error) { - k := ToDbKey(FSRESOURCETYPE_BIN, sym, nil) + k := db.ToDbKey(db.DATATYPE_BIN, sym, nil) return dbr.db.Fetch(k) } @@ -65,7 +60,7 @@ func(dbr *gdbmResource) GetMenu(ctx context.Context, sym string) (string, error) if v != nil { ln = v.(lang.Language) } - k := ToDbKey(FSRESOURCETYPE_TEMPLATE, msym, &ln) + k := db.ToDbKey(db.DATATYPE_TEMPLATE, msym, &ln) r, err := dbr.db.Fetch(k) if err != nil { if err.(*gdbm.GdbmError).Is(gdbm.ErrItemNotFound) {