commit f06bca7abf19b787b23b193301700cf39d2e7684
parent bf1d6344744b012cfa95512d80fa17cf8c9f8441
Author: lash <dev@holbrook.no>
Date: Sun, 16 Apr 2023 12:15:57 +0100
Add nomove state transition option, session partitioned interactive example
Diffstat:
19 files changed, 177 insertions(+), 73 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,6 +1,9 @@
-examples: profile
+examples: profile session
-.PHONY: profile
+.PHONY: examples
profile:
bash examples/compile.bash examples/profile
+
+session:
+ bash examples/compile.bash examples/session
diff --git a/asm/asm.go b/asm/asm.go
@@ -260,7 +260,7 @@ var (
{"Comment", `(?:#)[^\n]*`},
{"Ident", `^[A-Z]+`},
{"Size", `[0-9]+`},
- {"Sym", `[a-zA-Z_\*][a-zA-Z0-9_]*`},
+ {"Sym", `[a-zA-Z_\*\.][a-zA-Z0-9_]*`},
{"Whitespace", `[ \t]+`},
{"EOL", `[\n\r]+`},
{"Quote", `["']`},
diff --git a/dev/interactive/main.go b/dev/interactive/main.go
@@ -22,12 +22,8 @@ func main() {
fmt.Fprintf(os.Stderr, "starting session at symbol '%s' using resource dir: %s\n", root, dir)
ctx := context.Background()
- en, err := engine.NewSizedEngine(dir, uint32(size))
- if err != nil {
- fmt.Fprintf(os.Stderr, "engine create fail: %v\n", err)
- os.Exit(1)
- }
- err = engine.Loop(&en, os.Stdin, os.Stdout, ctx)
+ en := engine.NewSizedEngine(dir, uint32(size))
+ err := engine.Loop(&en, os.Stdin, os.Stdout, ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err)
os.Exit(1)
diff --git a/engine/default.go b/engine/default.go
@@ -9,7 +9,7 @@ import (
)
// NewDefaultEngine is a convenience function to instantiate a filesystem-backed engine with no output constraints.
-func NewDefaultEngine(dir string) (Engine, error) {
+func NewDefaultEngine(dir string) Engine {
st := state.NewState(0)
rs := resource.NewFsResource(dir)
ca := cache.NewCache()
@@ -21,7 +21,7 @@ func NewDefaultEngine(dir string) (Engine, error) {
}
// NewSizedEngine is a convenience function to instantiate a filesystem-backed engine with a specified output constraint.
-func NewSizedEngine(dir string, size uint32) (Engine, error) {
+func NewSizedEngine(dir string, size uint32) Engine {
st := state.NewState(0)
rs := resource.NewFsResource(dir)
ca := cache.NewCache()
diff --git a/engine/engine.go b/engine/engine.go
@@ -33,7 +33,7 @@ type Engine struct {
}
// NewEngine creates a new Engine
-func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memory, ctx context.Context) (Engine, error) {
+func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memory, ctx context.Context) Engine {
var szr *render.Sizer
if cfg.OutputSize > 0 {
szr = render.NewSizer(cfg.OutputSize)
@@ -46,17 +46,19 @@ func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memor
vm: vm.NewVm(st, rs, ca, szr),
}
engine.root = cfg.Root
- return engine, nil
+
+ return engine
}
// Init must be explicitly called before using the Engine instance.
//
// It loads and executes code for the start node.
-func(en *Engine) Init(sym string, ctx context.Context) error {
+func(en *Engine) Init(ctx context.Context) error {
if en.initd {
log.Printf("already initialized")
return nil
}
+ sym := en.root
if sym == "" {
return fmt.Errorf("start sym empty")
}
@@ -97,7 +99,7 @@ func(en *Engine) Init(sym string, ctx context.Context) error {
func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
var err error
if en.st.Moves == 0 {
- err = en.Init(en.root, ctx)
+ err = en.Init(ctx)
if err != nil {
return false, err
}
@@ -165,6 +167,7 @@ func(en *Engine) WriteResult(w io.Writer, ctx context.Context) (int, error) {
return io.WriteString(w, r)
}
+// start execution over at top node while keeping current state of client error flags.
func(en *Engine) reset(ctx context.Context) error {
var err error
var isTop bool
@@ -181,5 +184,5 @@ func(en *Engine) reset(ctx context.Context) error {
}
en.st.Restart()
en.initd = false
- return en.Init(en.root, ctx)
+ return en.Init(ctx)
}
diff --git a/engine/engine_test.go b/engine/engine_test.go
@@ -81,14 +81,11 @@ func TestEngineInit(t *testing.T) {
rs := NewFsWrapper(dataDir, &st)
ca := cache.NewCache().WithCacheSize(1024)
- en, err := NewEngine(Config{
+ en := NewEngine(Config{
Root: "root",
}, &st, &rs, ca, ctx)
- if err != nil {
- t.Fatal(err)
- }
- err = en.Init("root", ctx)
+ err = en.Init(ctx)
if err != nil {
t.Fatal(err)
}
@@ -141,13 +138,10 @@ func TestEngineExecInvalidInput(t *testing.T) {
ca := cache.NewCache().WithCacheSize(1024)
- en, err := NewEngine(Config{
+ en := NewEngine(Config{
Root: "root",
}, &st, &rs, ca, ctx)
- if err != nil {
- t.Fatal(err)
- }
- err = en.Init("root", ctx)
+ err := en.Init(ctx)
if err != nil {
t.Fatal(err)
}
@@ -164,13 +158,10 @@ func TestEngineResumeTerminated(t *testing.T) {
rs := NewFsWrapper(dataDir, &st)
ca := cache.NewCache().WithCacheSize(1024)
- en, err := NewEngine(Config{
+ en := NewEngine(Config{
Root: "root",
}, &st, &rs, ca, ctx)
- if err != nil {
- t.Fatal(err)
- }
- err = en.Init("root", ctx)
+ err := en.Init(ctx)
if err != nil {
t.Fatal(err)
}
diff --git a/engine/loop_test.go b/engine/loop_test.go
@@ -23,11 +23,8 @@ func TestLoopTop(t *testing.T) {
cfg := Config{
Root: "root",
}
- en, err := NewEngine(cfg, &st, &rs, ca, ctx)
- if err != nil {
- t.Fatal(err)
- }
- err = en.Init("root", ctx)
+ en := NewEngine(cfg, &st, &rs, ca, ctx)
+ err := en.Init(ctx)
if err != nil {
t.Fatal(err)
}
@@ -62,11 +59,8 @@ func TestLoopBackForth(t *testing.T) {
cfg := Config{
Root: "root",
}
- en, err := NewEngine(cfg, &st, &rs, ca, ctx)
- if err != nil {
- t.Fatal(err)
- }
- err = en.Init("root", ctx)
+ en := NewEngine(cfg, &st, &rs, ca, ctx)
+ err := en.Init(ctx)
if err != nil {
t.Fatal(err)
}
@@ -99,11 +93,8 @@ func TestLoopBrowse(t *testing.T) {
OutputSize: 68,
Root: "root",
}
- en, err := NewEngine(cfg, &st, &rs, ca, ctx)
- if err != nil {
- t.Fatal(err)
- }
- err = en.Init("root", ctx)
+ en := NewEngine(cfg, &st, &rs, ca, ctx)
+ err := en.Init(ctx)
if err != nil {
t.Fatal(err)
}
diff --git a/engine/persist.go b/engine/persist.go
@@ -24,10 +24,7 @@ func RunPersisted(cfg Config, rs resource.Resource, pr persist.Persister, input
return err
}
- en, err := NewEngine(cfg, pr.GetState(), rs, pr.GetMemory(), ctx)
- if err != nil {
- return err
- }
+ en := NewEngine(cfg, pr.GetState(), rs, pr.GetMemory(), ctx)
c, err := en.WriteResult(w, ctx)
if err != nil {
diff --git a/examples/profile/main.go b/examples/profile/main.go
@@ -122,9 +122,10 @@ func main() {
OutputSize: uint32(size),
}
ctx := context.Background()
- en, err := engine.NewEngine(cfg, &st, rs, ca, ctx)
+ en := engine.NewEngine(cfg, &st, rs, ca, ctx)
+ err := en.Init(ctx)
if err != nil {
- fmt.Fprintf(os.Stderr, "engine create fail: %v\n", err)
+ fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err)
os.Exit(1)
}
diff --git a/examples/session/data.txt.orig b/examples/session/data.txt.orig
diff --git a/examples/session/input b/examples/session/input
@@ -0,0 +1,2 @@
+hey hey hey
+your data is {{.do_save}}
diff --git a/examples/session/input.vis b/examples/session/input.vis
@@ -0,0 +1,4 @@
+MAP do_save
+HALT
+RELOAD do_save
+INCMP * .
diff --git a/examples/session/main.go b/examples/session/main.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+
+ testdataloader "github.com/peteole/testdata-loader"
+
+ "git.defalsify.org/vise/cache"
+ "git.defalsify.org/vise/engine"
+ "git.defalsify.org/vise/resource"
+ "git.defalsify.org/vise/state"
+)
+
+var (
+ baseDir = testdataloader.GetBasePath()
+ scriptDir = path.Join(baseDir, "examples", "session")
+ emptyResult = resource.Result{}
+)
+
+func save(sym string, input []byte, ctx context.Context) (resource.Result, error) {
+ sessionId := ctx.Value("SessionId").(string)
+ sessionDir := path.Join(scriptDir, sessionId)
+ err := os.MkdirAll(sessionDir, 0700)
+ if err != nil {
+ return emptyResult, err
+ }
+ fp := path.Join(sessionDir, "data.txt")
+ if len(input) > 0 {
+ log.Printf("write data %s session %s", input, sessionId)
+ err = ioutil.WriteFile(fp, input, 0600)
+ if err != nil {
+ return emptyResult, err
+ }
+ }
+ r, err := ioutil.ReadFile(fp)
+ if err != nil {
+ err = ioutil.WriteFile(fp, []byte("(not set)"), 0600)
+ if err != nil {
+ return emptyResult, err
+ }
+ }
+ return resource.Result{
+ Content: string(r),
+ }, nil
+}
+
+func main() {
+ var root string
+ var size uint
+ var sessionId string
+ flag.UintVar(&size, "s", 0, "max size of output")
+ flag.StringVar(&root, "root", "root", "entry point symbol")
+ flag.StringVar(&sessionId, "session-id", "default", "session id")
+ flag.Parse()
+ 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_save", save)
+ ca := cache.NewCache()
+ cfg := engine.Config{
+ Root: "root",
+ SessionId: sessionId,
+ OutputSize: uint32(size),
+ }
+ ctx := context.Background()
+ ctx = context.WithValue(ctx, "SessionId", sessionId)
+ en := engine.NewEngine(cfg, &st, rs, ca, ctx)
+ err := en.Init(ctx)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err)
+ os.Exit(1)
+ }
+ err = engine.Loop(&en, os.Stdin, os.Stdout, ctx)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/session/root.vis b/examples/session/root.vis
@@ -0,0 +1,2 @@
+LOAD do_save 0
+MOVE input
diff --git a/state/flag.go b/state/flag.go
@@ -1,11 +1,12 @@
package state
const (
- FLAG_READIN = 1
- FLAG_INMATCH = 2
- FLAG_TERMINATE = 3
- FLAG_DIRTY = 4
- FLAG_LOADFAIL = 5
+ FLAG_READIN = iota
+ FLAG_INMATCH
+ FLAG_TERMINATE
+ FLAG_DIRTY
+ FLAG_WAIT
+ FLAG_LOADFAIL
)
func IsWriteableFlag(flag uint32) bool {
@@ -17,3 +18,6 @@ func IsWriteableFlag(flag uint32) bool {
//}
return false
}
+
+type FlagDebugger struct {
+}
diff --git a/state/state.go b/state/state.go
@@ -208,6 +208,10 @@ func(st *State) Next() (uint16, error) {
return st.SizeIdx, nil
}
+func(st *State) Same() {
+ st.Moves += 1
+}
+
// Previous moves to the next sink page index.
//
// Fails if try to move beyond index 0.
@@ -338,7 +342,7 @@ func(st *State) Restart() error {
// String implements String interface
func(st State) String() string {
- return fmt.Sprintf("moves %v idx %v path: %s", st.Moves, st.SizeIdx, strings.Join(st.ExecPath, "/"))
+ return fmt.Sprintf("moves %v idx %v flags: 0x%x path: %s", st.Moves, st.SizeIdx, st.Flags, strings.Join(st.ExecPath, "/"))
}
// initializes all flags not in control of client.
diff --git a/vm/input.go b/vm/input.go
@@ -12,7 +12,7 @@ import (
var (
inputRegexStr = "^[a-zA-Z0-9].*$"
inputRegex = regexp.MustCompile(inputRegexStr)
- ctrlRegexStr = "^[><_^]$"
+ ctrlRegexStr = "^[><_^.]$"
ctrlRegex = regexp.MustCompile(ctrlRegexStr)
symRegexStr = "^[a-zA-Z0-9][a-zA-Z0-9_]+$"
symRegex = regexp.MustCompile(symRegexStr)
@@ -76,7 +76,7 @@ func CheckTarget(target []byte, st *state.State) (bool, error) {
switch target[0] {
case '_':
topOk, err := st.Top()
- if err!= nil {
+ if err != nil {
return false, err
}
return topOk, nil
@@ -137,6 +137,10 @@ func applyTarget(target []byte, st *state.State, ca cache.Memory, ctx context.Co
return sym, idx, err
}
}
+ case '.':
+ st.Same()
+ location, idx := st.Where()
+ return location, idx, nil
default:
sym = string(target)
err := st.Down(sym)
diff --git a/vm/runner.go b/vm/runner.go
@@ -31,6 +31,7 @@ func NewVm(st *state.State, rs resource.Resource, ca cache.Memory, sizer *render
sizer: sizer,
}
vmi.Reset()
+ log.Printf("vm created with state: %v", st)
return vmi
}
@@ -65,6 +66,19 @@ func(vm *Vm) Run(b []byte, ctx context.Context) ([]byte, error) {
if err != nil {
panic(err)
}
+
+ waitChange, err := vm.st.ResetFlag(state.FLAG_WAIT)
+ if err != nil {
+ panic(err)
+ }
+ if waitChange {
+ log.Printf("waitchange")
+ _, err = vm.st.ResetFlag(state.FLAG_INMATCH)
+ if err != nil {
+ panic(err)
+ }
+ }
+
_, err = vm.st.SetFlag(state.FLAG_DIRTY)
if err != nil {
panic(err)
@@ -295,12 +309,7 @@ func(vm *Vm) RunInCmp(b []byte, ctx context.Context) ([]byte, error) {
panic(err)
}
if have {
- if !reading {
- _, err = vm.st.ResetFlag(state.FLAG_INMATCH)
- if err != nil {
- panic(err)
- }
- } else {
+ if reading {
log.Printf("ignoring input %s, already have match", sym)
return b, nil
}
@@ -320,11 +329,11 @@ func(vm *Vm) RunInCmp(b []byte, ctx context.Context) ([]byte, error) {
log.Printf("input wildcard match ('%s'), target '%s'", input, target)
} else {
if sym != string(input) {
+ log.Printf("foo")
return b, nil
}
log.Printf("input match for '%s', target '%s'", input, target)
}
-
_, err = vm.st.SetFlag(state.FLAG_INMATCH)
if err != nil {
panic(err)
@@ -334,13 +343,10 @@ func(vm *Vm) RunInCmp(b []byte, ctx context.Context) ([]byte, error) {
panic(err)
}
- target, _, err = applyTarget([]byte(target), vm.st, vm.ca, ctx)
+ newTarget, _, err := applyTarget([]byte(target), vm.st, vm.ca, ctx)
+
_, ok := err.(*state.IndexError)
if ok {
- _, err = vm.st.ResetFlag(state.FLAG_INMATCH)
- if err != nil {
- panic(err)
- }
_, err = vm.st.SetFlag(state.FLAG_READIN)
if err != nil {
panic(err)
@@ -349,12 +355,16 @@ func(vm *Vm) RunInCmp(b []byte, ctx context.Context) ([]byte, error) {
} else if err != nil {
return b, err
}
+
+ target = newTarget
+
vm.Reset()
code, err := vm.rs.GetCode(target)
if err != nil {
return b, err
}
+ log.Printf("bar")
log.Printf("loaded additional code for target '%s': %x", target, code)
b = append(b, code...)
return b, err
@@ -368,7 +378,12 @@ func(vm *Vm) RunHalt(b []byte, ctx context.Context) ([]byte, error) {
return b, err
}
log.Printf("found HALT, stopping")
- return b, err
+
+ _, err = vm.st.SetFlag(state.FLAG_WAIT)
+ if err != nil {
+ panic(err)
+ }
+ return b, nil
}
// RunMSize executes the MSIZE opcode
@@ -454,6 +469,7 @@ func(vm *Vm) refresh(key string, rs resource.Resource, ctx context.Context) (str
input, _ := vm.st.GetInput()
r, err := fn(key, input, ctx)
if err != nil {
+ log.Printf("loadfail %v", err)
var perr error
_, perr = vm.st.SetFlag(state.FLAG_LOADFAIL)
if perr != nil {
diff --git a/vm/runner_test.go b/vm/runner_test.go
@@ -541,6 +541,7 @@ func TestInputIgnore(t *testing.T) {
b := NewLine(nil, INCMP, []string{"foo", "one"}, nil, nil)
b = NewLine(b, INCMP, []string{"bar", "two"}, nil, nil)
+ b = NewLine(b, HALT, nil, nil, nil)
ctx := context.TODO()