commit 00876115566638e66631e314820773450c177294
parent 0ee0159b513122c43680c1e98a2838a322d32ba9
Author: lash <dev@holbrook.no>
Date: Fri, 30 Aug 2024 16:23:24 +0100
Merge branch 'lash/data-db' into lash/gdbm-resources
Diffstat:
30 files changed, 954 insertions(+), 10 deletions(-)
diff --git a/asm/flag.go b/asm/flag.go
@@ -0,0 +1,124 @@
+package asm
+
+import (
+ "encoding/csv"
+ "fmt"
+ "io"
+ "os"
+ "strconv"
+
+ "git.defalsify.org/vise.git/state"
+)
+
+// FlagParser is used to resolve flag strings to corresponding
+// flag index integer values.
+type FlagParser struct {
+ flag map[string]string
+ flagDescription map[uint32]string
+ hi uint32
+}
+
+// NewFlagParser creates a new FlagParser
+func NewFlagParser() *FlagParser {
+ return &FlagParser{
+ flag: make(map[string]string),
+ flagDescription: make(map[uint32]string),
+ }
+}
+
+// GetFlag returns the flag index value for a given flag string
+// as a numeric string.
+//
+// If flag string has not been registered, an error is returned.
+func(pp *FlagParser) GetAsString(key string) (string, error) {
+ v, ok := pp.flag[key]
+ if !ok {
+ return "", fmt.Errorf("no flag registered under key: %s", key)
+ }
+ return v, nil
+}
+
+// GetFlag returns the flag index integer value for a given
+// flag string
+//
+// If flag string has not been registered, an error is returned.
+func(pp *FlagParser) GetFlag(key string) (uint32, error) {
+ v, err := pp.GetAsString(key)
+ if err != nil {
+ return 0, err
+ }
+ r, err := strconv.Atoi(v) // cannot fail
+ return uint32(r), nil
+}
+
+// GetDescription returns a flag description for a given flag index,
+// if available.
+//
+// If no description has been provided, an error is returned.
+func(pp *FlagParser) GetDescription(idx uint32) (string, error) {
+ v, ok := pp.flagDescription[idx]
+ if !ok {
+ return "", fmt.Errorf("no description for flag idx: %v", idx)
+ }
+ return v, nil
+}
+
+// Last returns the highest registered flag index value
+func(pp *FlagParser) Last() uint32 {
+ return pp.hi
+}
+
+// Load parses a Comma Seperated Value file under the given filepath
+// to provide mappings between flag strings and flag indices.
+//
+// The expected format is:
+//
+// Field 1: The literal string "flag"
+// Field 2: Flag string
+// Field 3: Flag index
+// Field 4: Flag description (optional)
+func(pp *FlagParser) Load(fp string) (int, error) {
+ var i int
+ f, err := os.Open(fp)
+ if err != nil {
+ return 0, err
+ }
+ defer f.Close()
+ r := csv.NewReader(f)
+ r.FieldsPerRecord = -1
+ for i = 0; true; i++ {
+ v, err := r.Read()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return 0, err
+ }
+ if v[0] == "flag" {
+ if len(v) < 3 {
+ return 0, fmt.Errorf("Not enough fields for flag setting in line %d", i)
+ }
+ vv, err := strconv.Atoi(v[2])
+ if err != nil {
+ return 0, fmt.Errorf("Flag translation value must be numeric")
+ }
+ if vv < state.FLAG_USERSTART {
+ return 0, fmt.Errorf("Minimum flag value is FLAG_USERSTART (%d)", FLAG_USERSTART)
+ }
+ fl := uint32(vv)
+ pp.flag[v[1]] = v[2]
+ if fl > pp.hi {
+ pp.hi = fl
+ }
+
+ if (len(v) > 3) {
+ pp.flagDescription[uint32(fl)] = v[3]
+ Logg.Debugf("added flag translation", "from", v[1], "to", v[2], "description", v[3])
+ } else {
+ Logg.Debugf("added flag translation", "from", v[1], "to", v[2])
+ }
+ }
+ }
+
+ return i, nil
+}
diff --git a/db/db.go b/db/db.go
@@ -0,0 +1,53 @@
+package db
+
+import (
+ "context"
+ "errors"
+
+ "git.defalsify.org/vise.git/lang"
+)
+
+const (
+ DATATYPE_UNKNOWN = iota
+ DATATYPE_BIN
+ DATATYPE_TEMPLATE
+ DATATYPE_STATE
+ DATATYPE_USERSTART
+)
+
+type Db interface {
+ Connect(ctx context.Context, connStr string) error
+ Close() error
+ Get(ctx context.Context, key []byte) ([]byte, error)
+ Put(ctx context.Context, key []byte, val []byte) error
+}
+
+func ToDbKey(typ uint8, b []byte, l *lang.Language) []byte {
+ k := []byte{typ}
+ if l != nil && l.Code != "" {
+ k = append(k, []byte("_" + l.Code)...)
+ //s += "_" + l.Code
+ }
+ return append(k, b...)
+}
+
+type BaseDb struct {
+ pfx uint8
+ sid []byte
+}
+
+func(db *BaseDb) SetPrefix(pfx uint8) {
+ db.pfx = pfx
+}
+
+func(db *BaseDb) SetSession(sessionId string) {
+ db.sid = append([]byte(sessionId), 0x2E)
+}
+
+func(db *BaseDb) ToKey(key []byte) ([]byte, error) {
+ if db.pfx == DATATYPE_UNKNOWN {
+ return nil, errors.New("datatype prefix cannot be UNKNOWN")
+ }
+ b := append(db.sid, key...)
+ return ToDbKey(db.pfx, b, nil), nil
+}
diff --git a/db/error.go b/db/error.go
@@ -0,0 +1,17 @@
+package db
+
+import (
+ "fmt"
+)
+
+type ErrNotFound struct {
+ k []byte
+}
+
+func NewErrNotFound(k []byte) error {
+ return ErrNotFound{k}
+}
+
+func(e ErrNotFound) Error() string {
+ return fmt.Sprintf("key not found: %x", e.k)
+}
diff --git a/db/fs.go b/db/fs.go
@@ -0,0 +1,64 @@
+package db
+
+import (
+ "context"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+)
+
+type FsDb struct {
+ BaseDb
+ dir string
+}
+
+func(fdb *FsDb) Connect(ctx context.Context, connStr string) error {
+ fi, err := os.Stat(connStr)
+ if err != nil {
+ return err
+ }
+ if !fi.IsDir() {
+ return fmt.Errorf("fs db %s is not a directory", connStr)
+ }
+ fdb.dir = connStr
+ return nil
+}
+
+func(fdb *FsDb) Get(ctx context.Context, key []byte) ([]byte, error) {
+ fp, err := fdb.pathFor(key)
+ if err != nil {
+ return nil, err
+ }
+ f, err := os.Open(fp)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ b, err := ioutil.ReadAll(f)
+ if err != nil {
+ return nil, err
+ }
+ return b, nil
+}
+
+func(fdb *FsDb) Put(ctx context.Context, key []byte, val []byte) error {
+ fp, err := fdb.pathFor(key)
+ if err != nil {
+ return err
+ }
+ return ioutil.WriteFile(fp, val, 0600)
+}
+
+func(fdb *FsDb) Close() error {
+ return nil
+}
+
+func(fdb *FsDb) pathFor(key []byte) (string, error) {
+ kb, err := fdb.ToKey(key)
+ if err != nil {
+ return "", err
+ }
+ kb[0] += 30
+ return path.Join(fdb.dir, string(kb)), nil
+}
diff --git a/db/fs_test.go b/db/fs_test.go
@@ -0,0 +1,39 @@
+package db
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "testing"
+)
+
+func TestPutGetFs(t *testing.T) {
+ ctx := context.Background()
+ sid := "ses"
+ d, err := ioutil.TempDir("", "vise-db-*")
+ if err != nil {
+ t.Fatal(err)
+ }
+ db := &FsDb{}
+ db.SetPrefix(DATATYPE_USERSTART)
+ db.SetSession(sid)
+ err = db.Connect(ctx, d)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = db.Put(ctx, []byte("foo"), []byte("bar"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ v, err := db.Get(ctx, []byte("foo"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(v, []byte("bar")) {
+ t.Fatalf("expected value 'bar', found '%s'", v)
+ }
+ _, err = db.Get(ctx, []byte("bar"))
+ if err == nil {
+ t.Fatal("expected get error for key 'bar'")
+ }
+}
diff --git a/db/gdbm.go b/db/gdbm.go
@@ -0,0 +1,50 @@
+package db
+
+import (
+ "context"
+ "errors"
+
+ gdbm "github.com/graygnuorg/go-gdbm"
+)
+
+type GdbmDb struct {
+ BaseDb
+ conn *gdbm.Database
+ prefix uint8
+}
+
+func(gdb *GdbmDb) Connect(ctx context.Context, connStr string) error {
+ db, err := gdbm.Open(connStr, gdbm.ModeWrcreat)
+ if err != nil {
+ return err
+ }
+ gdb.conn = db
+ return nil
+}
+
+func(gdb *GdbmDb) Put(ctx context.Context, key []byte, val []byte) error {
+ k, err := gdb.ToKey(key)
+ if err != nil {
+ return err
+ }
+ return gdb.conn.Store(k, val, true)
+}
+
+func(gdb *GdbmDb) Get(ctx context.Context, key []byte) ([]byte, error) {
+ k, err := gdb.ToKey(key)
+ if err != nil {
+ return nil, err
+ }
+ v, err := gdb.conn.Fetch(k)
+ if err != nil {
+ if errors.Is(gdbm.ErrItemNotFound, err) {
+ return nil, NewErrNotFound(k)
+ }
+ return nil, err
+ }
+ return v, nil
+}
+
+func(gdb *GdbmDb) Close() error {
+ return gdb.Close()
+}
diff --git a/db/gdbm_test.go b/db/gdbm_test.go
@@ -0,0 +1,40 @@
+package db
+
+import (
+ "bytes"
+ "context"
+ "io/ioutil"
+ "testing"
+)
+
+func TestPutGetGdbm(t *testing.T) {
+ ctx := context.Background()
+ sid := "ses"
+ f, err := ioutil.TempFile("", "vise-db-*")
+ if err != nil {
+ t.Fatal(err)
+ }
+ db := &GdbmDb{}
+ db.SetPrefix(DATATYPE_USERSTART)
+ db.SetSession(sid)
+ err = db.Connect(ctx, f.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = db.Put(ctx, []byte("foo"), []byte("bar"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ v, err := db.Get(ctx, []byte("foo"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(v, []byte("bar")) {
+ t.Fatalf("expected value 'bar', found '%s'", v)
+ }
+ _, err = db.Get(ctx, []byte("bar"))
+ if err == nil {
+ t.Fatal("expected get error for key 'bar'")
+ }
+
+}
diff --git a/db/pg.go b/db/pg.go
@@ -0,0 +1,139 @@
+package db
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/jackc/pgx/v5/pgxpool"
+)
+
+type PgDb struct {
+ BaseDb
+ conn *pgxpool.Pool
+ schema string
+ prefix uint8
+}
+
+func NewPgDb() *PgDb {
+ return &PgDb{
+ schema: "public",
+ }
+}
+
+func(pdb *PgDb) WithSchema(schema string) *PgDb {
+ pdb.schema = schema
+ return pdb
+}
+
+func(pdb *PgDb) Connect(ctx context.Context, connStr string) error {
+ var err error
+ conn, err := pgxpool.New(ctx, connStr)
+ if err != nil {
+ return err
+ }
+ pdb.conn = conn
+ return pdb.prepare(ctx)
+}
+
+func(pdb *PgDb) prepare(ctx context.Context) error {
+ tx, err := pdb.conn.Begin(ctx)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+// query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.kv_vise_domain (
+// id SERIAL PRIMARY KEY,
+// name VARCHAR(256) NOT NULL
+// );
+//`, pdb.schema)
+// _, err = tx.Exec(ctx, query)
+// if err != nil {
+// tx.Rollback(ctx)
+// return err
+// }
+//
+// query = fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.kv_vise (
+// id SERIAL NOT NULL,
+// domain_id INT NOT NULL,
+// key VARCHAR(256) NOT NULL,
+// value BYTEA NOT NULL,
+// constraint fk_domain
+// FOREIGN KEY (domain_id)
+// REFERENCES %s.kv_vise_domain(id)
+// );
+//`, pdb.schema, pdb.schema)
+// _, err = tx.Exec(ctx, query)
+// if err != nil {
+// tx.Rollback(ctx)
+// return err
+// }
+
+ query := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s.kv_vise (
+ id SERIAL NOT NULL,
+ key BYTEA NOT NULL UNIQUE,
+ value BYTEA NOT NULL
+ );
+`, pdb.schema)
+ _, err = tx.Exec(ctx, query)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+
+ err = tx.Commit(ctx)
+ if err != nil {
+ //if !errors.Is(pgx.ErrTxCommitRollback) {
+ tx.Rollback(ctx)
+ return err
+ //}
+ }
+ return nil
+}
+
+func(pdb *PgDb) Put(ctx context.Context, key []byte, val []byte) error {
+ k, err := pdb.ToKey(key)
+ if err != nil {
+ return err
+ }
+ tx, err := pdb.conn.Begin(ctx)
+ if err != nil {
+ return err
+ }
+ query := fmt.Sprintf("INSERT INTO %s.kv_vise (key, value) VALUES ($1, $2) ON CONFLICT(key) DO UPDATE SET value = $2;", pdb.schema)
+ _, err = tx.Exec(ctx, query, k, val)
+ if err != nil {
+ tx.Rollback(ctx)
+ return err
+ }
+ tx.Commit(ctx)
+ return nil
+}
+
+func(pdb *PgDb) Get(ctx context.Context, key []byte) ([]byte, error) {
+ k, err := pdb.ToKey(key)
+ if err != nil {
+ return nil, err
+ }
+ tx, err := pdb.conn.Begin(ctx)
+ if err != nil {
+ return nil, err
+ }
+ query := fmt.Sprintf("SELECT value FROM %s.kv_vise WHERE key = $1", pdb.schema)
+ rs, err := tx.Query(ctx, query, k)
+ if err != nil {
+ return nil, err
+ }
+ defer rs.Close()
+ if !rs.Next() {
+ return nil, NewErrNotFound(k)
+
+ }
+ r := rs.RawValues()
+ b := r[0]
+ return b, nil
+}
+
+func(pdb *PgDb) Close() error {
+ pdb.Close()
+ return nil
+}
diff --git a/db/pg_test.go b/db/pg_test.go
@@ -0,0 +1,43 @@
+package db
+
+import (
+ "bytes"
+ "context"
+ "testing"
+)
+
+func TestPutGetPg(t *testing.T) {
+ //t.Skip("need postgresql mock")
+ ses := "xyzzy"
+ db := NewPgDb().WithSchema("vvise")
+ db.SetPrefix(DATATYPE_USERSTART)
+ db.SetSession(ses)
+ ctx := context.Background()
+ err := db.Connect(ctx, "postgres://vise:esiv@localhost:5432/visedb")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = db.Put(ctx, []byte("foo"), []byte("bar"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ b, err := db.Get(ctx, []byte("foo"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(b, []byte("bar")) {
+ t.Fatalf("expected 'bar', got %x", b)
+ }
+ err = db.Put(ctx, []byte("foo"), []byte("plugh"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ b, err = db.Get(ctx, []byte("foo"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(b, []byte("plugh")) {
+ t.Fatalf("expected 'plugh', got %x", b)
+ }
+
+}
diff --git a/dev/asm/main.go b/dev/asm/main.go
@@ -1,27 +1,154 @@
package main
import (
+ "flag"
"fmt"
"io/ioutil"
"log"
"os"
+ "strconv"
+ "strings"
+
+ "github.com/alecthomas/participle/v2"
+ "github.com/alecthomas/participle/v2/lexer"
"git.defalsify.org/vise.git/asm"
)
+
+type arg struct {
+ One *string `(@Sym | @NumFirst)`
+ Two *string `((@Sym | @NumFirst) Whitespace?)?`
+ Three *string `((@Sym | @NumFirst) Whitespace?)?`
+}
+
+type instruction struct {
+ OpCode string `@Ident`
+ OpArg arg `(Whitespace @@)?`
+ Comment string `Comment? EOL`
+}
+
+type asmAsm struct {
+ Instructions []*instruction `@@*`
+}
+
+type processor struct {
+ *asm.FlagParser
+
+}
+
+func newProcessor(fp string) (*processor, error) {
+ o := &processor{
+ asm.NewFlagParser(),
+ }
+ _, err := o.Load(fp)
+ return o, err
+}
+
+
+func(p *processor) processFlag(s []string, one *string, two *string) ([]string, error) {
+ _, err := strconv.Atoi(*one)
+ if err != nil {
+ r, err := p.GetAsString(*one)
+ if err != nil {
+ return nil, err
+ }
+ log.Printf("translated flag %s to %s", *one, r)
+ s = append(s, r)
+ } else {
+ s = append(s, *one)
+ }
+ return append(s, *two), nil
+}
+
+func(p *processor) pass(s []string, a arg) []string {
+ for _, r := range []*string{a.One, a.Two, a.Three} {
+ if r == nil {
+ break
+ }
+ s = append(s, *r)
+ }
+ return s
+}
+
+func(pp *processor) run(b []byte) ([]byte, error) {
+ asmLexer := lexer.MustSimple([]lexer.SimpleRule{
+ {"Comment", `(?:#)[^\n]*`},
+ {"Ident", `^[A-Z]+`},
+ {"NumFirst", `[0-9][a-zA-Z0-9]*`},
+ {"Sym", `[a-zA-Z_\*\.\^\<\>][a-zA-Z0-9_]*`},
+ {"Whitespace", `[ \t]+`},
+ {"EOL", `[\n\r]+`},
+ {"Quote", `["']`},
+ })
+ asmParser := participle.MustBuild[asmAsm](
+ participle.Lexer(asmLexer),
+ participle.Elide("Comment", "Whitespace"),
+ )
+ ast, err := asmParser.ParseString("preprocessor", string(b))
+ if err != nil {
+ return nil, err
+ }
+
+ b = []byte{}
+ for _, v := range ast.Instructions {
+ s := []string{v.OpCode}
+ if v.OpArg.One != nil {
+ switch v.OpCode {
+ case "CATCH":
+ s = append(s, *v.OpArg.One)
+ s, err = pp.processFlag(s, v.OpArg.Two, v.OpArg.Three)
+ if err != nil {
+ return nil, err
+ }
+ case "CROAK":
+ s, err = pp.processFlag(s, v.OpArg.One, v.OpArg.Two)
+ if err != nil {
+ return nil, err
+ }
+ default:
+ s = pp.pass(s, v.OpArg)
+ }
+ }
+ b = append(b, []byte(strings.Join(s, " "))...)
+ b = append(b, 0x0a)
+ }
+
+ return b, nil
+}
+
func main() {
- if (len(os.Args) < 2) {
+ var ppfp string
+ flag.StringVar(&ppfp, "f", "", "preprocessor data to load")
+ flag.Parse()
+ if (len(flag.Args()) < 1) {
os.Exit(1)
}
- fp := os.Args[1]
+ fp := flag.Arg(0)
v, err := ioutil.ReadFile(fp)
if err != nil {
- fmt.Fprintf(os.Stderr, "read error: %v", err)
+ fmt.Fprintf(os.Stderr, "read error: %v\n", err)
os.Exit(1)
}
+
+ if len(ppfp) > 0 {
+ pp, err := newProcessor(ppfp)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "preprocessor load error: %v\n", err)
+ os.Exit(1)
+ }
+
+ v, err = pp.run(v)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "preprocess error: %v\n", err)
+ os.Exit(1)
+ }
+ }
+ log.Printf("preprocessor done")
+
n, err := asm.Parse(string(v), os.Stdout)
if err != nil {
- fmt.Fprintf(os.Stderr, "parse error: %v", err)
+ fmt.Fprintf(os.Stderr, "parse error: %v\n", err)
os.Exit(1)
}
log.Printf("parsed total %v bytes", n)
diff --git a/examples/preprocessor/Makefile b/examples/preprocessor/Makefile
@@ -0,0 +1,10 @@
+INPUTS = $(wildcard ./*.vis)
+TXTS = $(wildcard ./*.txt.orig)
+
+%.vis:
+ go run ../../dev/asm -f pp.csv $(basename $@).vis > $(basename $@).bin
+
+all: $(INPUTS) $(TXTS)
+
+%.txt.orig:
+ cp -v $(basename $@).orig $(basename $@)
diff --git a/examples/preprocessor/first b/examples/preprocessor/first
@@ -0,0 +1 @@
+this is the first page
diff --git a/examples/preprocessor/first.vis b/examples/preprocessor/first.vis
@@ -0,0 +1,3 @@
+LOAD flag_foo 0
+HALT
+INCMP ^ *
diff --git a/examples/preprocessor/last b/examples/preprocessor/last
@@ -0,0 +1 @@
+this is the last page
diff --git a/examples/preprocessor/last.vis b/examples/preprocessor/last.vis
@@ -0,0 +1,4 @@
+LOAD flag_bar 0
+HALT
+RELOAD flag_schmag
+INCMP ^ *
diff --git a/examples/preprocessor/main.go b/examples/preprocessor/main.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "path"
+ "strings"
+
+ testdataloader "github.com/peteole/testdata-loader"
+
+ "git.defalsify.org/vise.git/asm"
+ "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", "preprocessor")
+)
+
+type countResource struct {
+ parser *asm.FlagParser
+ count int
+}
+
+func newCountResource(fp string) (*countResource, error) {
+ var err error
+ pfp := path.Join(fp, "pp.csv")
+ parser := asm.NewFlagParser()
+ _, err = parser.Load(pfp)
+ if err != nil {
+ return nil, err
+ }
+ return &countResource{
+ count: 0,
+ parser: parser,
+ }, nil
+}
+
+func(rsc* countResource) poke(ctx context.Context, sym string, input []byte) (resource.Result, error) {
+ var r resource.Result
+
+ ss := strings.Split(sym, "_")
+
+ r.Content = "You will see this if this flag did not have a description"
+ r.FlagReset = []uint32{8, 9, 10}
+ v, err := rsc.parser.GetFlag(ss[1])
+ if err != nil {
+ v = 8 + uint32(rsc.count) + 1
+ r.FlagSet = []uint32{8 + uint32(rsc.count) + 1}
+ }
+ r.FlagSet = []uint32{uint32(v)}
+ s, err := rsc.parser.GetDescription(v)
+ if err == nil {
+ r.Content = s
+ }
+
+ rsc.count++
+
+ return r, nil
+}
+
+func main() {
+ root := "root"
+ fmt.Fprintf(os.Stderr, "starting session at symbol '%s' using resource dir: %s\n", root, scriptDir)
+
+ st := state.NewState(5)
+ st.UseDebug()
+ rsf := resource.NewFsResource(scriptDir)
+ rs, err := newCountResource(scriptDir)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "aux handler fail: %v\n", err)
+ os.Exit(1)
+ }
+ rsf.AddLocalFunc("flag_foo", rs.poke)
+ rsf.AddLocalFunc("flag_bar", rs.poke)
+ rsf.AddLocalFunc("flag_schmag", rs.poke)
+ rsf.AddLocalFunc("flag_start", rs.poke)
+ ca := cache.NewCache()
+ cfg := engine.Config{
+ Root: "root",
+ }
+ ctx := context.Background()
+ en := engine.NewEngine(ctx, cfg, &st, rsf, ca)
+
+ _, 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/preprocessor/mid b/examples/preprocessor/mid
@@ -0,0 +1,2 @@
+this is the middle page
+{{.flag_schmag}}
diff --git a/examples/preprocessor/mid.vis b/examples/preprocessor/mid.vis
@@ -0,0 +1,3 @@
+MAP flag_schmag
+HALT
+MOVE ^
diff --git a/examples/preprocessor/pp.csv b/examples/preprocessor/pp.csv
@@ -0,0 +1,3 @@
+flag,foo,8
+flag,bar,10,and this is the description of the flag 'bar'
+flag,baz,12
diff --git a/examples/preprocessor/root b/examples/preprocessor/root
@@ -0,0 +1 @@
+that's it
diff --git a/examples/preprocessor/root.vis b/examples/preprocessor/root.vis
@@ -0,0 +1,5 @@
+CROAK baz 1
+CATCH last bar 1
+CATCH first foo 0
+LOAD flag_schmag 0
+MOVE mid
diff --git a/examples/reuse/Makefile b/examples/reuse/Makefile
@@ -0,0 +1,10 @@
+INPUTS = $(wildcard ./*.vis)
+TXTS = $(wildcard ./*.txt.orig)
+
+%.vis:
+ go run ../../dev/asm $(basename $@).vis > $(basename $@).bin
+
+all: $(INPUTS) $(TXTS)
+
+%.txt.orig:
+ cp -v $(basename $@).orig $(basename $@)
diff --git a/examples/reuse/bar.vis b/examples/reuse/bar.vis
@@ -0,0 +1,3 @@
+LOAD do_bar 0
+MAP do_bar
+HALT
diff --git a/examples/reuse/foo.vis b/examples/reuse/foo.vis
@@ -0,0 +1,3 @@
+LOAD do_foo 0
+MAP do_foo
+HALT
diff --git a/examples/reuse/main.go b/examples/reuse/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"
+)
+
+const (
+ USERFLAG = iota + state.FLAG_USERSTART
+)
+
+var (
+ baseDir = testdataloader.GetBasePath()
+ scriptDir = path.Join(baseDir, "examples", "reuse")
+ emptyResult = resource.Result{}
+)
+
+func same(ctx context.Context, sym string, input []byte) (resource.Result, error) {
+ return resource.Result{
+ Content: "You came through the symbol " + sym,
+ }, nil
+}
+
+func main() {
+ root := "root"
+ fmt.Fprintf(os.Stderr, "starting session at symbol '%s' using resource dir: %s\n", root, scriptDir)
+
+ st := state.NewState(0)
+ rs := resource.NewFsResource(scriptDir)
+ rs.AddLocalFunc("do_foo", same)
+ rs.AddLocalFunc("do_bar", same)
+ ca := cache.NewCache()
+ cfg := engine.Config{
+ Root: "root",
+ }
+ ctx := context.Background()
+ en := engine.NewEngine(ctx, cfg, &st, rs, ca)
+ var err error
+ _, 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/reuse/root b/examples/reuse/root
@@ -0,0 +1 @@
+choose
diff --git a/examples/reuse/root.vis b/examples/reuse/root.vis
@@ -0,0 +1,6 @@
+MOUT foo 0
+MOUT bar 1
+HALT
+INCMP foo 0
+INCMP bar 1
+INCMP ^ *
diff --git a/go.mod b/go.mod
@@ -7,11 +7,18 @@ require (
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c
github.com/fxamacker/cbor/v2 v2.4.0
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4
+ github.com/jackc/pgx/v5 v5.6.0
github.com/peteole/testdata-loader v0.3.0
gopkg.in/leonelquinteros/gotext.v1 v1.3.1
)
require (
+ github.com/jackc/pgpassfile v1.0.0 // indirect
+ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
+ github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect
github.com/x448/float16 v0.8.4 // indirect
+ golang.org/x/crypto v0.17.0 // indirect
+ golang.org/x/sync v0.1.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
)
diff --git a/go.sum b/go.sum
@@ -4,16 +4,41 @@ github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmE
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE=
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
+github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
+github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
+github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY=
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk=
github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I=
github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc=
gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/vm/runner.go b/vm/runner.go
@@ -246,7 +246,7 @@ func(vm *Vm) runCatch(ctx context.Context, b []byte) ([]byte, error) {
if err != nil {
return b, err
}
- Logg.InfoCtxf(ctx, "catch!", "flag", sig, "sym", sym, "target", actualSym)
+ Logg.InfoCtxf(ctx, "catch!", "flag", sig, "sym", sym, "target", actualSym, "mode", mode)
sym = actualSym
bh, err := vm.rs.GetCode(sym)
if err != nil {
@@ -270,7 +270,7 @@ func(vm *Vm) runCroak(ctx context.Context, b []byte) ([]byte, error) {
vm.ca.Reset()
b = []byte{}
}
- return []byte{}, nil
+ return b, nil
}
// executes the LOAD opcode
@@ -494,17 +494,17 @@ func(vm *Vm) refresh(key string, rs resource.Resource, ctx context.Context) (str
_ = vm.st.SetFlag(state.FLAG_LOADFAIL)
return "", NewExternalCodeError(key, err).WithCode(r.Status)
}
- for _, flag := range r.FlagSet {
+ for _, flag := range r.FlagReset {
if !state.IsWriteableFlag(flag) {
continue
}
- vm.st.SetFlag(flag)
+ vm.st.ResetFlag(flag)
}
- for _, flag := range r.FlagReset {
+ for _, flag := range r.FlagSet {
if !state.IsWriteableFlag(flag) {
continue
}
- vm.st.ResetFlag(flag)
+ vm.st.SetFlag(flag)
}
haveLang := vm.st.MatchFlag(state.FLAG_LANG, true)