vise/go/vm/runner.go

245 lines
5.6 KiB
Go
Raw Normal View History

2023-04-02 13:12:06 +02:00
package vm
import (
"context"
"fmt"
"log"
"git.defalsify.org/festive/resource"
"git.defalsify.org/festive/state"
)
//type Runner func(instruction []byte, st state.State, rs resource.Resource, ctx context.Context) (state.State, []byte, error)
// Run extracts individual op codes and arguments and executes them.
//
// Each step may update the state.
//
// On error, the remaining instructions will be returned. State will not be rolled back.
func Run(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
running := true
for running {
log.Printf("execute code %x", b)
op, bb, err := opSplit(b)
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
b = bb
2023-04-02 13:12:06 +02:00
switch op {
case CATCH:
b, err = RunCatch(b, st, rs, ctx)
2023-04-02 13:12:06 +02:00
case CROAK:
b, err = RunCroak(b, st, rs, ctx)
2023-04-02 13:12:06 +02:00
case LOAD:
b, err = RunLoad(b, st, rs, ctx)
2023-04-02 13:12:06 +02:00
case RELOAD:
b, err = RunReload(b, st, rs, ctx)
2023-04-02 13:12:06 +02:00
case MAP:
b, err = RunMap(b, st, rs, ctx)
2023-04-02 13:12:06 +02:00
case MOVE:
b, err = RunMove(b, st, rs, ctx)
2023-04-02 13:12:06 +02:00
case INCMP:
b, err = RunInCmp(b, st, rs, ctx)
case MSIZE:
b, err = RunMSize(b, st, rs, ctx)
case MOUT:
b, err = RunMOut(b, st, rs, ctx)
2023-04-03 10:11:44 +02:00
case MNEXT:
b, err = RunMNext(b, st, rs, ctx)
case MPREV:
b, err = RunMPrev(b, st, rs, ctx)
2023-04-02 13:12:06 +02:00
case HALT:
b, err = RunHalt(b, st, rs, ctx)
return b, err
2023-04-02 13:12:06 +02:00
default:
err = fmt.Errorf("Unhandled state: %v", op)
}
if err != nil {
return b, err
}
if len(b) == 0 {
return []byte{}, nil
2023-04-02 13:12:06 +02:00
}
}
return b, nil
2023-04-02 13:12:06 +02:00
}
// RunMap executes the MAP opcode
func RunMap(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
sym, b, err := ParseMap(b)
err = st.Map(sym)
return b, err
2023-04-02 13:12:06 +02:00
}
// RunMap executes the CATCH opcode
func RunCatch(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
sym, sig, mode, b, err := ParseCatch(b)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
r, err := matchFlag(st, sig, mode)
if err != nil {
return b, err
}
if r {
log.Printf("catch at flag %v, moving to %v", sig, sym) //bitField, d)
st.Down(sym)
b = []byte{}
2023-04-02 13:12:06 +02:00
}
return b, nil
2023-04-02 13:12:06 +02:00
}
// RunMap executes the CROAK opcode
func RunCroak(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
sig, mode, b, err := ParseCroak(b)
if err != nil {
return b, err
}
r, err := matchFlag(st, sig, mode)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
}
if r {
log.Printf("croak at flag %v, purging and moving to top", sig)
st.Reset()
b = []byte{}
2023-04-02 13:12:06 +02:00
}
return []byte{}, nil
}
// RunLoad executes the LOAD opcode
func RunLoad(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
sym, sz, b, err := ParseLoad(b)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
r, err := refresh(sym, rs, ctx)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
err = st.Add(sym, r, uint16(sz))
return b, err
2023-04-02 13:12:06 +02:00
}
// RunLoad executes the RELOAD opcode
func RunReload(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
sym, b, err := ParseReload(b)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
r, err := refresh(sym, rs, ctx)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
st.Update(sym, r)
return b, nil
2023-04-02 13:12:06 +02:00
}
// RunLoad executes the MOVE opcode
func RunMove(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
sym, b, err := ParseMove(b)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
st.Down(sym)
code, err := rs.GetCode(sym)
if err != nil {
return b, err
}
log.Printf("loaded additional code: %x", code)
b = append(b, code...)
return b, nil
2023-04-02 13:12:06 +02:00
}
// RunIncmp executes the INCMP opcode
func RunInCmp(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
sym, target, b, err := ParseInCmp(b)
2023-04-02 13:12:06 +02:00
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
v, err := st.GetFlag(state.FLAG_INMATCH)
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
if v {
return b, nil
2023-04-02 13:12:06 +02:00
}
input, err := st.GetInput()
if err != nil {
return b, err
2023-04-02 13:12:06 +02:00
}
if sym == string(input) {
2023-04-02 13:12:06 +02:00
log.Printf("input match for '%s'", input)
_, err = st.SetFlag(state.FLAG_INMATCH)
st.Down(target)
2023-04-02 13:12:06 +02:00
}
code, err := rs.GetCode(target)
if err != nil {
return b, err
}
log.Printf("loaded additional code: %x", code)
b = append(b, code...)
return b, err
2023-04-02 13:12:06 +02:00
}
// RunHalt executes the HALT opcode
func RunHalt(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
var err error
b, err = ParseHalt(b)
if err != nil {
return b, err
}
2023-04-02 13:12:06 +02:00
log.Printf("found HALT, stopping")
_, err = st.ResetFlag(state.FLAG_INMATCH)
return b, err
2023-04-02 13:12:06 +02:00
}
// RunMSize
func RunMSize(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
2023-04-03 10:11:44 +02:00
log.Printf("WARNING MSIZE not yet implemented")
return b, nil
}
2023-04-03 10:11:44 +02:00
// RunMSize
func RunMNext(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
selector, display, b, err := ParseMNext(b)
if err != nil {
return b, err
}
err = rs.SetMenuBrowse(selector, display, false)
return b, err
}
// RunMSize
func RunMPrev(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
selector, display, b, err := ParseMPrev(b)
if err != nil {
return b, err
}
err = rs.SetMenuBrowse(selector, display, false)
return b, err
}
func RunMOut(b []byte, st *state.State, rs resource.Resource, ctx context.Context) ([]byte, error) {
choice, title, b, err := ParseMOut(b)
if err != nil {
return b, err
}
err = rs.PutMenu(choice, title)
return b, err
}
2023-04-02 13:12:06 +02:00
// retrieve data for key
func refresh(key string, rs resource.Resource, ctx context.Context) (string, error) {
fn, err := rs.FuncFor(key)
if err != nil {
return "", err
}
if fn == nil {
return "", fmt.Errorf("no retrieve function for external symbol %v", key)
}
return fn(ctx)
}