vise/go/engine/engine.go

142 lines
3.3 KiB
Go
Raw Normal View History

2023-04-01 11:58:02 +02:00
package engine
import (
"context"
2023-04-01 15:47:03 +02:00
"fmt"
2023-04-01 11:58:02 +02:00
"io"
"log"
2023-04-06 13:41:36 +02:00
"regexp"
2023-04-01 11:58:02 +02:00
"git.defalsify.org/festive/resource"
"git.defalsify.org/festive/state"
"git.defalsify.org/festive/vm"
)
2023-04-06 13:41:36 +02:00
var (
2023-04-06 13:47:09 +02:00
inputRegexStr = "^[a-zA-Z0-9].*$"
inputRegex = regexp.MustCompile(inputRegexStr)
2023-04-06 13:41:36 +02:00
)
2023-04-01 11:58:02 +02:00
//type Config struct {
// FlagCount uint32
// CacheSize uint32
//}
2023-04-01 16:04:38 +02:00
// Engine is an execution engine that handles top-level errors when running user inputs against currently exposed bytecode.
2023-04-01 11:58:02 +02:00
type Engine struct {
2023-04-01 15:47:03 +02:00
st *state.State
2023-04-01 11:58:02 +02:00
rs resource.Resource
}
2023-04-01 16:04:38 +02:00
// NewEngine creates a new Engine
2023-04-01 15:47:03 +02:00
func NewEngine(st *state.State, rs resource.Resource) Engine {
2023-04-01 11:58:02 +02:00
engine := Engine{st, rs}
return engine
}
2023-04-01 16:04:38 +02:00
// Init must be explicitly called before using the Engine instance.
//
2023-04-06 13:08:30 +02:00
// It loads and executes code for the start node.
2023-04-02 10:07:53 +02:00
func(en *Engine) Init(sym string, ctx context.Context) error {
err := en.st.SetInput([]byte{})
if err != nil {
return err
}
b := vm.NewLine(nil, vm.MOVE, []string{sym}, nil, nil)
b, err = vm.Run(b, en.st, en.rs, ctx)
2023-04-01 15:47:03 +02:00
if err != nil {
return err
}
en.st.SetCode(b)
return nil
2023-04-01 15:47:03 +02:00
}
2023-04-06 13:47:09 +02:00
// return descriptive error if client input is invalid
2023-04-06 13:41:36 +02:00
func checkInput(input []byte) error {
if !inputRegex.Match(input) {
2023-04-06 13:47:09 +02:00
return fmt.Errorf("Input '%s' does not match format /%s/", input, inputRegexStr)
2023-04-06 13:41:36 +02:00
}
return nil
}
2023-04-01 16:04:38 +02:00
// Exec processes user input against the current state of the virtual machine environment.
//
2023-04-06 13:08:30 +02:00
// If successfully executed, output of the last execution is available using the WriteResult call.
//
// A bool return valus of false indicates that execution should be terminated. Calling Exec again has undefined effects.
2023-04-01 16:04:38 +02:00
//
// Fails if:
2023-04-06 13:08:30 +02:00
// - input is formally invalid (too long etc)
2023-04-01 16:04:38 +02:00
// - no current bytecode is available
// - input processing against bytcode failed
2023-04-06 11:08:40 +02:00
func (en *Engine) Exec(input []byte, ctx context.Context) (bool, error) {
2023-04-06 13:41:36 +02:00
err := checkInput(input)
if err != nil {
2023-04-06 13:47:09 +02:00
return true, err
2023-04-06 13:41:36 +02:00
}
err = en.st.SetInput(input)
if err != nil {
2023-04-06 11:08:40 +02:00
return false, err
2023-04-01 15:47:03 +02:00
}
2023-04-06 13:47:09 +02:00
log.Printf("new execution with input '%s' (0x%x)", input, input)
2023-04-01 15:47:03 +02:00
code, err := en.st.GetCode()
if err != nil {
2023-04-06 11:08:40 +02:00
return false, err
2023-04-01 15:47:03 +02:00
}
if len(code) == 0 {
2023-04-06 11:08:40 +02:00
return false, fmt.Errorf("no code to execute")
2023-04-01 15:47:03 +02:00
}
code, err = vm.Run(code, en.st, en.rs, ctx)
2023-04-06 11:08:40 +02:00
if err != nil {
return false, err
}
v, err := en.st.MatchFlag(state.FLAG_TERMINATE, false)
if err != nil {
return false, err
}
if v {
if len(code) > 0 {
log.Printf("terminated with code remaining: %x", code)
}
return false, nil
}
2023-04-01 15:47:03 +02:00
en.st.SetCode(code)
2023-04-06 13:08:30 +02:00
if len(code) == 0 {
log.Printf("runner finished with no remaining code")
return false, nil
}
2023-04-06 11:08:40 +02:00
return true, nil
2023-04-01 11:58:02 +02:00
}
2023-04-01 16:04:38 +02:00
// WriteResult writes the output of the last vm execution to the given writer.
//
// Fails if
// - required data inputs to the template are not available.
// - the template for the given node point is note available for retrieval using the resource.Resource implementer.
// - the supplied writer fails to process the writes.
2023-04-01 11:58:02 +02:00
func(en *Engine) WriteResult(w io.Writer) error {
location, idx := en.st.Where()
2023-04-01 11:58:02 +02:00
v, err := en.st.Get()
if err != nil {
return err
}
r, err := en.rs.RenderTemplate(location, v, idx, nil)
2023-04-01 11:58:02 +02:00
if err != nil {
return err
}
m, err := en.rs.RenderMenu()
if err != nil {
return err
}
if len(m) > 0 {
r += "\n" + m
}
2023-04-01 11:58:02 +02:00
c, err := io.WriteString(w, r)
log.Printf("%v bytes written as result for %v", c, location)
return err
}