Add nomove state transition option, session partitioned interactive example

This commit is contained in:
lash 2023-04-16 12:15:57 +01:00
parent bf1d634474
commit f06bca7abf
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
19 changed files with 177 additions and 73 deletions

View File

@ -1,6 +1,9 @@
examples: profile examples: profile session
.PHONY: profile .PHONY: examples
profile: profile:
bash examples/compile.bash examples/profile bash examples/compile.bash examples/profile
session:
bash examples/compile.bash examples/session

View File

@ -260,7 +260,7 @@ var (
{"Comment", `(?:#)[^\n]*`}, {"Comment", `(?:#)[^\n]*`},
{"Ident", `^[A-Z]+`}, {"Ident", `^[A-Z]+`},
{"Size", `[0-9]+`}, {"Size", `[0-9]+`},
{"Sym", `[a-zA-Z_\*][a-zA-Z0-9_]*`}, {"Sym", `[a-zA-Z_\*\.][a-zA-Z0-9_]*`},
{"Whitespace", `[ \t]+`}, {"Whitespace", `[ \t]+`},
{"EOL", `[\n\r]+`}, {"EOL", `[\n\r]+`},
{"Quote", `["']`}, {"Quote", `["']`},

View File

@ -22,12 +22,8 @@ func main() {
fmt.Fprintf(os.Stderr, "starting session at symbol '%s' using resource dir: %s\n", root, dir) fmt.Fprintf(os.Stderr, "starting session at symbol '%s' using resource dir: %s\n", root, dir)
ctx := context.Background() ctx := context.Background()
en, err := engine.NewSizedEngine(dir, uint32(size)) en := engine.NewSizedEngine(dir, uint32(size))
if err != nil { err := engine.Loop(&en, os.Stdin, os.Stdout, ctx)
fmt.Fprintf(os.Stderr, "engine create fail: %v\n", err)
os.Exit(1)
}
err = engine.Loop(&en, os.Stdin, os.Stdout, ctx)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err) fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err)
os.Exit(1) os.Exit(1)

View File

@ -9,7 +9,7 @@ import (
) )
// NewDefaultEngine is a convenience function to instantiate a filesystem-backed engine with no output constraints. // 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) st := state.NewState(0)
rs := resource.NewFsResource(dir) rs := resource.NewFsResource(dir)
ca := cache.NewCache() 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. // 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) st := state.NewState(0)
rs := resource.NewFsResource(dir) rs := resource.NewFsResource(dir)
ca := cache.NewCache() ca := cache.NewCache()

View File

@ -33,7 +33,7 @@ type Engine struct {
} }
// NewEngine creates a new Engine // 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 var szr *render.Sizer
if cfg.OutputSize > 0 { if cfg.OutputSize > 0 {
szr = render.NewSizer(cfg.OutputSize) 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), vm: vm.NewVm(st, rs, ca, szr),
} }
engine.root = cfg.Root engine.root = cfg.Root
return engine, nil
return engine
} }
// Init must be explicitly called before using the Engine instance. // Init must be explicitly called before using the Engine instance.
// //
// It loads and executes code for the start node. // 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 { if en.initd {
log.Printf("already initialized") log.Printf("already initialized")
return nil return nil
} }
sym := en.root
if sym == "" { if sym == "" {
return fmt.Errorf("start sym empty") 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) { func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
var err error var err error
if en.st.Moves == 0 { if en.st.Moves == 0 {
err = en.Init(en.root, ctx) err = en.Init(ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -165,6 +167,7 @@ func(en *Engine) WriteResult(w io.Writer, ctx context.Context) (int, error) {
return io.WriteString(w, r) 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 { func(en *Engine) reset(ctx context.Context) error {
var err error var err error
var isTop bool var isTop bool
@ -181,5 +184,5 @@ func(en *Engine) reset(ctx context.Context) error {
} }
en.st.Restart() en.st.Restart()
en.initd = false en.initd = false
return en.Init(en.root, ctx) return en.Init(ctx)
} }

View File

@ -81,14 +81,11 @@ func TestEngineInit(t *testing.T) {
rs := NewFsWrapper(dataDir, &st) rs := NewFsWrapper(dataDir, &st)
ca := cache.NewCache().WithCacheSize(1024) ca := cache.NewCache().WithCacheSize(1024)
en, err := NewEngine(Config{ en := NewEngine(Config{
Root: "root", Root: "root",
}, &st, &rs, ca, ctx) }, &st, &rs, ca, ctx)
if err != nil {
t.Fatal(err)
}
err = en.Init("root", ctx) err = en.Init(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -141,13 +138,10 @@ func TestEngineExecInvalidInput(t *testing.T) {
ca := cache.NewCache().WithCacheSize(1024) ca := cache.NewCache().WithCacheSize(1024)
en, err := NewEngine(Config{ en := NewEngine(Config{
Root: "root", Root: "root",
}, &st, &rs, ca, ctx) }, &st, &rs, ca, ctx)
if err != nil { err := en.Init(ctx)
t.Fatal(err)
}
err = en.Init("root", ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -164,13 +158,10 @@ func TestEngineResumeTerminated(t *testing.T) {
rs := NewFsWrapper(dataDir, &st) rs := NewFsWrapper(dataDir, &st)
ca := cache.NewCache().WithCacheSize(1024) ca := cache.NewCache().WithCacheSize(1024)
en, err := NewEngine(Config{ en := NewEngine(Config{
Root: "root", Root: "root",
}, &st, &rs, ca, ctx) }, &st, &rs, ca, ctx)
if err != nil { err := en.Init(ctx)
t.Fatal(err)
}
err = en.Init("root", ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -23,11 +23,8 @@ func TestLoopTop(t *testing.T) {
cfg := Config{ cfg := Config{
Root: "root", Root: "root",
} }
en, err := NewEngine(cfg, &st, &rs, ca, ctx) en := NewEngine(cfg, &st, &rs, ca, ctx)
if err != nil { err := en.Init(ctx)
t.Fatal(err)
}
err = en.Init("root", ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -62,11 +59,8 @@ func TestLoopBackForth(t *testing.T) {
cfg := Config{ cfg := Config{
Root: "root", Root: "root",
} }
en, err := NewEngine(cfg, &st, &rs, ca, ctx) en := NewEngine(cfg, &st, &rs, ca, ctx)
if err != nil { err := en.Init(ctx)
t.Fatal(err)
}
err = en.Init("root", ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -99,11 +93,8 @@ func TestLoopBrowse(t *testing.T) {
OutputSize: 68, OutputSize: 68,
Root: "root", Root: "root",
} }
en, err := NewEngine(cfg, &st, &rs, ca, ctx) en := NewEngine(cfg, &st, &rs, ca, ctx)
if err != nil { err := en.Init(ctx)
t.Fatal(err)
}
err = en.Init("root", ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -24,10 +24,7 @@ func RunPersisted(cfg Config, rs resource.Resource, pr persist.Persister, input
return err return err
} }
en, err := NewEngine(cfg, pr.GetState(), rs, pr.GetMemory(), ctx) en := NewEngine(cfg, pr.GetState(), rs, pr.GetMemory(), ctx)
if err != nil {
return err
}
c, err := en.WriteResult(w, ctx) c, err := en.WriteResult(w, ctx)
if err != nil { if err != nil {

View File

@ -122,9 +122,10 @@ func main() {
OutputSize: uint32(size), OutputSize: uint32(size),
} }
ctx := context.Background() 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 { 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) os.Exit(1)
} }

View File

2
examples/session/input Normal file
View File

@ -0,0 +1,2 @@
hey hey hey
your data is {{.do_save}}

View File

@ -0,0 +1,4 @@
MAP do_save
HALT
RELOAD do_save
INCMP * .

85
examples/session/main.go Normal file
View File

@ -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)
}
}

View File

@ -0,0 +1,2 @@
LOAD do_save 0
MOVE input

View File

@ -1,11 +1,12 @@
package state package state
const ( const (
FLAG_READIN = 1 FLAG_READIN = iota
FLAG_INMATCH = 2 FLAG_INMATCH
FLAG_TERMINATE = 3 FLAG_TERMINATE
FLAG_DIRTY = 4 FLAG_DIRTY
FLAG_LOADFAIL = 5 FLAG_WAIT
FLAG_LOADFAIL
) )
func IsWriteableFlag(flag uint32) bool { func IsWriteableFlag(flag uint32) bool {
@ -17,3 +18,6 @@ func IsWriteableFlag(flag uint32) bool {
//} //}
return false return false
} }
type FlagDebugger struct {
}

View File

@ -208,6 +208,10 @@ func(st *State) Next() (uint16, error) {
return st.SizeIdx, nil return st.SizeIdx, nil
} }
func(st *State) Same() {
st.Moves += 1
}
// Previous moves to the next sink page index. // Previous moves to the next sink page index.
// //
// Fails if try to move beyond index 0. // Fails if try to move beyond index 0.
@ -338,7 +342,7 @@ func(st *State) Restart() error {
// String implements String interface // String implements String interface
func(st State) String() string { 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. // initializes all flags not in control of client.

View File

@ -12,7 +12,7 @@ import (
var ( var (
inputRegexStr = "^[a-zA-Z0-9].*$" inputRegexStr = "^[a-zA-Z0-9].*$"
inputRegex = regexp.MustCompile(inputRegexStr) inputRegex = regexp.MustCompile(inputRegexStr)
ctrlRegexStr = "^[><_^]$" ctrlRegexStr = "^[><_^.]$"
ctrlRegex = regexp.MustCompile(ctrlRegexStr) ctrlRegex = regexp.MustCompile(ctrlRegexStr)
symRegexStr = "^[a-zA-Z0-9][a-zA-Z0-9_]+$" symRegexStr = "^[a-zA-Z0-9][a-zA-Z0-9_]+$"
symRegex = regexp.MustCompile(symRegexStr) symRegex = regexp.MustCompile(symRegexStr)
@ -137,6 +137,10 @@ func applyTarget(target []byte, st *state.State, ca cache.Memory, ctx context.Co
return sym, idx, err return sym, idx, err
} }
} }
case '.':
st.Same()
location, idx := st.Where()
return location, idx, nil
default: default:
sym = string(target) sym = string(target)
err := st.Down(sym) err := st.Down(sym)

View File

@ -31,6 +31,7 @@ func NewVm(st *state.State, rs resource.Resource, ca cache.Memory, sizer *render
sizer: sizer, sizer: sizer,
} }
vmi.Reset() vmi.Reset()
log.Printf("vm created with state: %v", st)
return vmi return vmi
} }
@ -65,6 +66,19 @@ func(vm *Vm) Run(b []byte, ctx context.Context) ([]byte, error) {
if err != nil { if err != nil {
panic(err) 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) _, err = vm.st.SetFlag(state.FLAG_DIRTY)
if err != nil { if err != nil {
panic(err) panic(err)
@ -295,12 +309,7 @@ func(vm *Vm) RunInCmp(b []byte, ctx context.Context) ([]byte, error) {
panic(err) panic(err)
} }
if have { if have {
if !reading { if reading {
_, err = vm.st.ResetFlag(state.FLAG_INMATCH)
if err != nil {
panic(err)
}
} else {
log.Printf("ignoring input %s, already have match", sym) log.Printf("ignoring input %s, already have match", sym)
return b, nil 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) log.Printf("input wildcard match ('%s'), target '%s'", input, target)
} else { } else {
if sym != string(input) { if sym != string(input) {
log.Printf("foo")
return b, nil return b, nil
} }
log.Printf("input match for '%s', target '%s'", input, target) log.Printf("input match for '%s', target '%s'", input, target)
} }
_, err = vm.st.SetFlag(state.FLAG_INMATCH) _, err = vm.st.SetFlag(state.FLAG_INMATCH)
if err != nil { if err != nil {
panic(err) panic(err)
@ -334,13 +343,10 @@ func(vm *Vm) RunInCmp(b []byte, ctx context.Context) ([]byte, error) {
panic(err) 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) _, ok := err.(*state.IndexError)
if ok { if ok {
_, err = vm.st.ResetFlag(state.FLAG_INMATCH)
if err != nil {
panic(err)
}
_, err = vm.st.SetFlag(state.FLAG_READIN) _, err = vm.st.SetFlag(state.FLAG_READIN)
if err != nil { if err != nil {
panic(err) panic(err)
@ -349,12 +355,16 @@ func(vm *Vm) RunInCmp(b []byte, ctx context.Context) ([]byte, error) {
} else if err != nil { } else if err != nil {
return b, err return b, err
} }
target = newTarget
vm.Reset() vm.Reset()
code, err := vm.rs.GetCode(target) code, err := vm.rs.GetCode(target)
if err != nil { if err != nil {
return b, err return b, err
} }
log.Printf("bar")
log.Printf("loaded additional code for target '%s': %x", target, code) log.Printf("loaded additional code for target '%s': %x", target, code)
b = append(b, code...) b = append(b, code...)
return b, err return b, err
@ -368,7 +378,12 @@ func(vm *Vm) RunHalt(b []byte, ctx context.Context) ([]byte, error) {
return b, err return b, err
} }
log.Printf("found HALT, stopping") 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 // 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() input, _ := vm.st.GetInput()
r, err := fn(key, input, ctx) r, err := fn(key, input, ctx)
if err != nil { if err != nil {
log.Printf("loadfail %v", err)
var perr error var perr error
_, perr = vm.st.SetFlag(state.FLAG_LOADFAIL) _, perr = vm.st.SetFlag(state.FLAG_LOADFAIL)
if perr != nil { if perr != nil {

View File

@ -541,6 +541,7 @@ func TestInputIgnore(t *testing.T) {
b := NewLine(nil, INCMP, []string{"foo", "one"}, nil, nil) b := NewLine(nil, INCMP, []string{"foo", "one"}, nil, nil)
b = NewLine(b, INCMP, []string{"bar", "two"}, nil, nil) b = NewLine(b, INCMP, []string{"bar", "two"}, nil, nil)
b = NewLine(b, HALT, nil, nil, nil)
ctx := context.TODO() ctx := context.TODO()