Add hello world example

This commit is contained in:
lash 2023-04-17 06:35:36 +01:00
parent 100f7f3b48
commit afb3ff3a36
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
11 changed files with 48 additions and 29 deletions

View File

@ -1,4 +1,4 @@
examples: profile session examples: profile session helloworld
.PHONY: examples .PHONY: examples
@ -7,3 +7,6 @@ profile:
session: session:
bash examples/compile.bash examples/session bash examples/compile.bash examples/session
helloworld:
bash examples/compile.bash examples/helloworld

View File

@ -38,6 +38,7 @@ Original motivation was to create a simple templating renderer for USSD clients,
* Breakpoints. * Breakpoints.
* Key/value database reference example. * Key/value database reference example.
* Same-page catch with dedicated error string to prepend to template
## Opcodes ## Opcodes

View File

@ -23,11 +23,20 @@ func main() {
ctx := context.Background() ctx := context.Background()
en := engine.NewSizedEngine(dir, uint32(size)) en := engine.NewSizedEngine(dir, uint32(size))
err := en.Init(ctx) cont, err := en.Init(ctx)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "engine init exited with error: %v\n", err) fmt.Fprintf(os.Stderr, "engine init exited with error: %v\n", err)
os.Exit(1) os.Exit(1)
} }
if !cont {
_, err = en.WriteResult(os.Stdout, ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "dead init write error: %v\n", err)
os.Exit(1)
}
os.Stdout.Write([]byte{0x0a})
os.Exit(0)
}
err = engine.Loop(&en, os.Stdin, os.Stdout, ctx) 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)

View File

@ -53,37 +53,34 @@ func NewEngine(cfg Config, st *state.State, rs resource.Resource, ca cache.Memor
// 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(ctx context.Context) error { func(en *Engine) Init(ctx context.Context) (bool, error) {
if en.initd { if en.initd {
log.Printf("already initialized") log.Printf("already initialized")
return nil return true, nil
} }
sym := en.root sym := en.root
if sym == "" { if sym == "" {
return fmt.Errorf("start sym empty") return false, fmt.Errorf("start sym empty")
} }
inSave, _ := en.st.GetInput() inSave, _ := en.st.GetInput()
err := en.st.SetInput([]byte{}) err := en.st.SetInput([]byte{})
if err != nil { if err != nil {
return err return false, err
} }
b := vm.NewLine(nil, vm.MOVE, []string{sym}, nil, nil) b := vm.NewLine(nil, vm.MOVE, []string{sym}, nil, nil)
log.Printf("start new init VM run with code %x", b) log.Printf("start new init VM run with code %x", b)
b, err = en.vm.Run(b, ctx) b, err = en.vm.Run(b, ctx)
if err != nil { if err != nil {
return err return false, err
}
if len(b) == 0 {
return fmt.Errorf("no code left after init, that's just useless and sad")
} }
log.Printf("ended init VM run with code %x", b) log.Printf("ended init VM run with code %x", b)
en.st.SetCode(b) en.st.SetCode(b)
err = en.st.SetInput(inSave) err = en.st.SetInput(inSave)
if err != nil { if err != nil {
return err return false, err
} }
en.initd = true return len(b) > 0, nil
return nil
} }
// Exec processes user input against the current state of the virtual machine environment. // Exec processes user input against the current state of the virtual machine environment.
@ -99,13 +96,11 @@ func(en *Engine) Init(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(ctx) cont, err := en.Init(ctx)
if err != nil { if err != nil {
return false, err return false, err
} }
if len(input) == 0 { return cont, nil
return true, nil
}
} }
err = vm.ValidInput(input) err = vm.ValidInput(input)
if err != nil { if err != nil {
@ -146,7 +141,7 @@ func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
en.st.SetCode(code) en.st.SetCode(code)
if len(code) == 0 { if len(code) == 0 {
log.Printf("runner finished with no remaining code") log.Printf("runner finished with no remaining code")
err = en.reset(ctx) _, err = en.reset(ctx)
return false, err return false, err
} }
@ -168,17 +163,17 @@ func(en *Engine) WriteResult(w io.Writer, ctx context.Context) (int, error) {
} }
// start execution over at top node while keeping current state of client error flags. // 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) (bool, error) {
var err error var err error
var isTop bool var isTop bool
for !isTop { for !isTop {
isTop, err = en.st.Top() isTop, err = en.st.Top()
if err != nil { if err != nil {
return err return false, err
} }
_, err = en.st.Up() _, err = en.st.Up()
if err != nil { if err != nil {
return err return false, err
} }
en.ca.Pop() en.ca.Pop()
} }

View File

@ -85,7 +85,7 @@ func TestEngineInit(t *testing.T) {
Root: "root", Root: "root",
}, &st, &rs, ca, ctx) }, &st, &rs, ca, ctx)
err = en.Init(ctx) _, err = en.Init(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -141,7 +141,8 @@ func TestEngineExecInvalidInput(t *testing.T) {
en := NewEngine(Config{ en := NewEngine(Config{
Root: "root", Root: "root",
}, &st, &rs, ca, ctx) }, &st, &rs, ca, ctx)
err := en.Init(ctx) var err error
_, err = en.Init(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -161,7 +162,8 @@ func TestEngineResumeTerminated(t *testing.T) {
en := NewEngine(Config{ en := NewEngine(Config{
Root: "root", Root: "root",
}, &st, &rs, ca, ctx) }, &st, &rs, ca, ctx)
err := en.Init(ctx) var err error
_, err = en.Init(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -24,7 +24,8 @@ func TestLoopTop(t *testing.T) {
Root: "root", Root: "root",
} }
en := NewEngine(cfg, &st, &rs, ca, ctx) en := NewEngine(cfg, &st, &rs, ca, ctx)
err := en.Init(ctx) var err error
_, err = en.Init(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -60,7 +61,8 @@ func TestLoopBackForth(t *testing.T) {
Root: "root", Root: "root",
} }
en := NewEngine(cfg, &st, &rs, ca, ctx) en := NewEngine(cfg, &st, &rs, ca, ctx)
err := en.Init(ctx) var err error
_, err = en.Init(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -94,7 +96,8 @@ func TestLoopBrowse(t *testing.T) {
Root: "root", Root: "root",
} }
en := NewEngine(cfg, &st, &rs, ca, ctx) en := NewEngine(cfg, &st, &rs, ca, ctx)
err := en.Init(ctx) var err error
_, err = en.Init(ctx)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -0,0 +1 @@
world

1
examples/helloworld/root Normal file
View File

@ -0,0 +1 @@
hello, {{.que}}!

View File

@ -0,0 +1,2 @@
LOAD que 5
MAP que

View File

@ -124,7 +124,8 @@ func main() {
} }
ctx := context.Background() ctx := context.Background()
en := engine.NewEngine(cfg, &st, rs, ca, ctx) en := engine.NewEngine(cfg, &st, rs, ca, ctx)
err := en.Init(ctx) var err error
_, err = en.Init(ctx)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err) fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err)
os.Exit(1) os.Exit(1)

View File

@ -72,7 +72,8 @@ func main() {
ctx := context.Background() ctx := context.Background()
ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "SessionId", sessionId)
en := engine.NewEngine(cfg, &st, rs, ca, ctx) en := engine.NewEngine(cfg, &st, rs, ca, ctx)
err := en.Init(ctx) var err error
_, err = en.Init(ctx)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err) fmt.Fprintf(os.Stderr, "engine init fail: %v\n", err)
os.Exit(1) os.Exit(1)