go-vise

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

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:
Aasm/flag.go | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adb/db.go | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Adb/error.go | 17+++++++++++++++++
Adb/fs.go | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adb/fs_test.go | 39+++++++++++++++++++++++++++++++++++++++
Adb/gdbm.go | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Adb/gdbm_test.go | 40++++++++++++++++++++++++++++++++++++++++
Adb/pg.go | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adb/pg_test.go | 43+++++++++++++++++++++++++++++++++++++++++++
Mdev/asm/main.go | 135++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Aexamples/preprocessor/Makefile | 10++++++++++
Aexamples/preprocessor/first | 1+
Aexamples/preprocessor/first.vis | 3+++
Aexamples/preprocessor/last | 1+
Aexamples/preprocessor/last.vis | 4++++
Aexamples/preprocessor/main.go | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/preprocessor/mid | 2++
Aexamples/preprocessor/mid.vis | 3+++
Aexamples/preprocessor/pp.csv | 3+++
Aexamples/preprocessor/root | 1+
Aexamples/preprocessor/root.vis | 5+++++
Aexamples/reuse/Makefile | 10++++++++++
Aexamples/reuse/bar.vis | 3+++
Aexamples/reuse/foo.vis | 3+++
Aexamples/reuse/main.go | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/reuse/root | 1+
Aexamples/reuse/root.vis | 6++++++
Mgo.mod | 7+++++++
Mgo.sum | 25+++++++++++++++++++++++++
Mvm/runner.go | 12++++++------
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)