Add code comment documentation

This commit is contained in:
lash 2023-04-12 18:04:36 +01:00
parent 91ee0568ca
commit e340210d8f
Signed by untrusted user who does not match committer: lash
GPG Key ID: 21D2E7BB88C2A746
13 changed files with 134 additions and 70 deletions

View File

@ -17,10 +17,12 @@ import (
) )
// Asm assembles bytecode from the festive assembly mini-language.
type Asm struct { type Asm struct {
Instructions []*Instruction `@@*` Instructions []*Instruction `@@*`
} }
// Arg holds all parsed argument elements of a single line of assembly code.
type Arg struct { type Arg struct {
Sym *string `(@Sym Whitespace?)?` Sym *string `(@Sym Whitespace?)?`
Size *uint32 `(@Size Whitespace?)?` Size *uint32 `(@Size Whitespace?)?`
@ -214,6 +216,7 @@ func parseOne(op vm.Opcode, instruction *Instruction, w io.Writer) (int, error)
return flush(b, w) return flush(b, w)
} }
// String implements the String interface.
func (a Arg) String() string { func (a Arg) String() string {
s := "[Arg]" s := "[Arg]"
if a.Sym != nil { if a.Sym != nil {
@ -235,12 +238,14 @@ func (a Arg) String() string {
return fmt.Sprintf(s) return fmt.Sprintf(s)
} }
// Instruction represents one full line of assembly code.
type Instruction struct { type Instruction struct {
OpCode string `@Ident` OpCode string `@Ident`
OpArg Arg `(Whitespace @@)?` OpArg Arg `(Whitespace @@)?`
Comment string `Comment? EOL` Comment string `Comment? EOL`
} }
// String implement the String interface.
func (i Instruction) String() string { func (i Instruction) String() string {
return fmt.Sprintf("%s %s", i.OpCode, i.OpArg) return fmt.Sprintf("%s %s", i.OpCode, i.OpArg)
} }
@ -303,17 +308,20 @@ func writeSize(w *bytes.Buffer, n uint32) (int, error) {
return w.Write(bn[c:]) return w.Write(bn[c:])
} }
// Batcher handles assembly commands that generates multiple instructions, such as menu navigation commands.
type Batcher struct { type Batcher struct {
menuProcessor MenuProcessor menuProcessor MenuProcessor
inMenu bool inMenu bool
} }
// NewBatcher creates a new Batcher objcet.
func NewBatcher(mp MenuProcessor) Batcher { func NewBatcher(mp MenuProcessor) Batcher {
return Batcher{ return Batcher{
menuProcessor: NewMenuProcessor(), menuProcessor: NewMenuProcessor(),
} }
} }
// MenuExit generates the instructions for the batch and writes them to the given io.Writer.
func(bt *Batcher) MenuExit(w io.Writer) (int, error) { func(bt *Batcher) MenuExit(w io.Writer) (int, error) {
if !bt.inMenu { if !bt.inMenu {
return 0, nil return 0, nil
@ -323,6 +331,7 @@ func(bt *Batcher) MenuExit(w io.Writer) (int, error) {
return w.Write(b) return w.Write(b)
} }
// MenuAdd adds a new menu instruction to the batcher.
func(bt *Batcher) MenuAdd(w io.Writer, code string, arg Arg) (int, error) { func(bt *Batcher) MenuAdd(w io.Writer, code string, arg Arg) (int, error) {
bt.inMenu = true bt.inMenu = true
var selector string var selector string
@ -343,10 +352,12 @@ func(bt *Batcher) MenuAdd(w io.Writer, code string, arg Arg) (int, error) {
return 0, err return 0, err
} }
// Exit is a synonym for MenuExit
func(bt *Batcher) Exit(w io.Writer) (int, error) { func(bt *Batcher) Exit(w io.Writer) (int, error) {
return bt.MenuExit(w) return bt.MenuExit(w)
} }
// Parse one or more lines of assembly code, and write assembled bytecode to the provided writer.
func Parse(s string, w io.Writer) (int, error) { func Parse(s string, w io.Writer) (int, error) {
rd := strings.NewReader(s) rd := strings.NewReader(s)
ast, err := asmParser.Parse("file", rd) ast, err := asmParser.Parse("file", rd)

View File

@ -6,6 +6,7 @@ import (
"git.defalsify.org/festive/vm" "git.defalsify.org/festive/vm"
) )
// BatchCode defines quasi-opcodes that expand to mulitple individual vm instructions.
type BatchCode uint16 type BatchCode uint16
const ( const (
@ -32,15 +33,22 @@ type menuItem struct {
target string target string
} }
// MenuProcessor handles code lines with BatchCode quasi-opcodes that control menu generation.
//
// It creates vm instructions for display of menu and handling of input on either size of a vm.HALT instruction.
type MenuProcessor struct { type MenuProcessor struct {
items []menuItem items []menuItem
size uint32 size uint32
} }
// NewMenuProcessor creates a new MenuProcessor object.
func NewMenuProcessor() MenuProcessor { func NewMenuProcessor() MenuProcessor {
return MenuProcessor{} return MenuProcessor{}
} }
// Add a menu batch instruction to be processed.
//
// Instructions will be rendered in the order in which they have been added.
func(mp *MenuProcessor) Add(bop string, choice string, display string, target string) error { func(mp *MenuProcessor) Add(bop string, choice string, display string, target string) error {
bopCode := batchCode[bop] bopCode := batchCode[bop]
if bopCode == 0 { if bopCode == 0 {
@ -59,6 +67,7 @@ func(mp *MenuProcessor) Add(bop string, choice string, display string, target st
return nil return nil
} }
// ToLines returns the generated bytecode from the added menu batch instructions.
func (mp *MenuProcessor) ToLines() []byte { func (mp *MenuProcessor) ToLines() []byte {
preLines := []byte{} preLines := []byte{}
postLines := []byte{} postLines := []byte{}

11
go/cache/cache.go vendored
View File

@ -5,14 +5,12 @@ import (
"log" "log"
) )
// Cache stores loaded content, enforcing size limits and keeping track of size usage.
type Cache struct { type Cache struct {
CacheSize uint32 // Total allowed cumulative size of values (not code) in cache CacheSize uint32 // Total allowed cumulative size of values (not code) in cache
CacheUseSize uint32 // Currently used bytes by all values (not code) in cache CacheUseSize uint32 // Currently used bytes by all values (not code) in cache
Cache []map[string]string // All loaded cache items Cache []map[string]string // All loaded cache items
//CacheMap map[string]string // Mapped
//outputSize uint32 // Max size of output
sizes map[string]uint16 // Size limits for all loaded symbols. sizes map[string]uint16 // Size limits for all loaded symbols.
//sink *string
} }
// NewCache creates a new ready-to-use cache object // NewCache creates a new ready-to-use cache object
@ -21,7 +19,6 @@ func NewCache() *Cache {
Cache: []map[string]string{make(map[string]string)}, Cache: []map[string]string{make(map[string]string)},
sizes: make(map[string]uint16), sizes: make(map[string]uint16),
} }
//ca.resetCurrent()
return ca return ca
} }
@ -97,9 +94,6 @@ func(ca *Cache) Update(key string, value string) error {
r := ca.Cache[checkFrame][key] r := ca.Cache[checkFrame][key]
l := uint32(len(r)) l := uint32(len(r))
ca.Cache[checkFrame][key] = "" ca.Cache[checkFrame][key] = ""
//if ca.CacheMap[key] != "" {
// ca.CacheMap[key] = value
//}
ca.CacheUseSize -= l ca.CacheUseSize -= l
sz := ca.checkCapacity(value) sz := ca.checkCapacity(value)
if sz == 0 { if sz == 0 {
@ -113,6 +107,9 @@ func(ca *Cache) Update(key string, value string) error {
return nil return nil
} }
// Get the content currently loaded for a single key, loaded at any level.
//
// Fails if key has not been loaded.
func(ca *Cache) Get(key string) (string, error) { func(ca *Cache) Get(key string) (string, error) {
i := ca.frameOf(key) i := ca.frameOf(key)
r, ok := ca.Cache[i][key] r, ok := ca.Cache[i][key]

1
go/cache/memory.go vendored
View File

@ -1,5 +1,6 @@
package cache package cache
// Memory defines the interface for store of a symbol mapped content store.
type Memory interface { type Memory interface {
Add(key string, val string, sizeLimit uint16) error Add(key string, val string, sizeLimit uint16) error
Update(key string, val string) error Update(key string, val string) error

View File

@ -6,6 +6,7 @@ import (
"git.defalsify.org/festive/state" "git.defalsify.org/festive/state"
) )
// NewDefaultEngine is a convenience function to instantiate a filesystem-backed engine with no output constraints.
func NewDefaultEngine(dir string) Engine { func NewDefaultEngine(dir string) Engine {
st := state.NewState(0) st := state.NewState(0)
rs := resource.NewFsResource(dir) rs := resource.NewFsResource(dir)
@ -13,6 +14,7 @@ func NewDefaultEngine(dir string) Engine {
return NewEngine(Config{}, &st, &rs, ca) return NewEngine(Config{}, &st, &rs, ca)
} }
// NewSizedEngine is a convenience function to instantiate a filesystem-backed engine with a specified output constraint.
func NewSizedEngine(dir string, size uint32) Engine { func NewSizedEngine(dir string, size uint32) Engine {
st := state.NewState(0) st := state.NewState(0)
rs := resource.NewFsResource(dir) rs := resource.NewFsResource(dir)

View File

@ -13,13 +13,12 @@ import (
"git.defalsify.org/festive/vm" "git.defalsify.org/festive/vm"
) )
// Config globally defines behavior of all components driven by the engine.
type Config struct { type Config struct {
OutputSize uint32 OutputSize uint32 // Maximum size of output from a single rendered page
// FlagCount uint32
// CacheSize uint32
} }
// Engine is an execution engine that handles top-level errors when running user inputs against currently exposed bytecode. // Engine is an execution engine that handles top-level errors when running client inputs against code in the bytecode buffer.
type Engine struct { type Engine struct {
st *state.State st *state.State
rs resource.Resource rs resource.Resource

View File

@ -2,7 +2,6 @@ package engine
import ( import (
"bufio" "bufio"
// "bytes"
"context" "context"
"fmt" "fmt"
"io" "io"
@ -10,17 +9,26 @@ import (
"strings" "strings"
) )
// Loop starts an engine execution loop with the given symbol as the starting node.
//
// The root reads inputs from the provided reader, one line at a time.
//
// It will execute until running out of bytecode in the buffer.
//
// Any error not handled by the engine will terminate the oop and return an error.
//
// Rendered output is written to the provided writer.
func Loop(en *Engine, startSym string, ctx context.Context, reader io.Reader, writer io.Writer) error { func Loop(en *Engine, startSym string, ctx context.Context, reader io.Reader, writer io.Writer) error {
err := en.Init(startSym, ctx) err := en.Init(startSym, ctx)
if err != nil { if err != nil {
return fmt.Errorf("cannot init: %v\n", err) return fmt.Errorf("cannot init: %v\n", err)
} }
//b := bytes.NewBuffer(nil) err = en.WriteResult(writer, ctx)
//en.WriteResult(b, ctx) if err != nil {
en.WriteResult(writer, ctx) return err
}
writer.Write([]byte{0x0a}) writer.Write([]byte{0x0a})
//fmt.Println(b.String())
running := true running := true
bufReader := bufio.NewReader(reader) bufReader := bufio.NewReader(reader)

View File

@ -4,11 +4,13 @@ import (
"fmt" "fmt"
) )
// BrowseError is raised when browsing outside the page range of a rendered node.
type BrowseError struct { type BrowseError struct {
Idx uint16 Idx uint16
PageCount uint16 PageCount uint16
} }
// Error implements the Error interface.
func(err *BrowseError) Error() string { func(err *BrowseError) Error() string {
return fmt.Sprintf("index is out of bounds: %v", err.Idx) return fmt.Sprintf("index is out of bounds: %v", err.Idx)
} }
@ -35,13 +37,14 @@ func DefaultBrowseConfig() BrowseConfig {
} }
} }
// Menu renders menus. May be included in a Page object to render menus for pages.
type Menu struct { type Menu struct {
menu [][2]string menu [][2]string // selector and title for menu items.
browse BrowseConfig browse BrowseConfig // browse definitions.
pageCount uint16 pageCount uint16 // number of pages the menu should represent.
canNext bool canNext bool // availability flag for the "next" browse option.
canPrevious bool canPrevious bool // availability flag for the "previous" browse option.
outputSize uint16 outputSize uint16 // maximum size constraint for the menu.
} }
// NewMenu creates a new Menu with an explicit page count. // NewMenu creates a new Menu with an explicit page count.
@ -83,6 +86,11 @@ func(m *Menu) Put(selector string, title string) error {
return nil return nil
} }
// ReservedSize returns the maximum render byte size of the menu.
func(m *Menu) ReservedSize() uint16 {
return m.outputSize
}
// Render returns the full current state of the menu as a string. // Render returns the full current state of the menu as a string.
// //
// After this has been executed, the state of the menu will be empty. // After this has been executed, the state of the menu will be empty.
@ -160,6 +168,7 @@ func(m *Menu) shiftMenu() (string, string, error) {
return r[0], r[1], nil return r[0], r[1], nil
} }
// prepare menu object for re-use.
func(m *Menu) reset() { func(m *Menu) reset() {
if m.browse.NextAvailable { if m.browse.NextAvailable {
m.canNext = true m.canNext = true
@ -170,6 +179,3 @@ func(m *Menu) reset() {
} }
func(m *Menu) ReservedSize() uint16 {
return m.outputSize
}

View File

@ -11,16 +11,17 @@ import (
"git.defalsify.org/festive/resource" "git.defalsify.org/festive/resource"
) )
// Page exectues output rendering into pages constrained by size.
type Page struct { type Page struct {
cacheMap map[string]string // Mapped cacheMap map[string]string // Mapped content symbols
cache cache.Memory cache cache.Memory // Content store.
resource resource.Resource resource resource.Resource // Symbol resolver.
menu *Menu menu *Menu // Menu rendererer.
sink *string sink *string // Content symbol rendered by dynamic size.
sinkSize uint16 sizer *Sizer // Process size constraints.
sizer *Sizer
} }
// NewPage creates a new Page object.
func NewPage(cache cache.Memory, rs resource.Resource) *Page { func NewPage(cache cache.Memory, rs resource.Resource) *Page {
return &Page{ return &Page{
cache: cache, cache: cache,
@ -29,6 +30,7 @@ func NewPage(cache cache.Memory, rs resource.Resource) *Page {
} }
} }
// WithMenu sets a menu renderer for the page.
func(pg *Page) WithMenu(menu *Menu) *Page { func(pg *Page) WithMenu(menu *Menu) *Page {
pg.menu = menu pg.menu = menu
if pg.sizer != nil { if pg.sizer != nil {
@ -37,6 +39,7 @@ func(pg *Page) WithMenu(menu *Menu) *Page {
return pg return pg
} }
// WithSizer sets a size constraints definition for the page.
func(pg *Page) WithSizer(sizer *Sizer) *Page { func(pg *Page) WithSizer(sizer *Sizer) *Page {
pg.sizer = sizer pg.sizer = sizer
if pg.menu != nil { if pg.menu != nil {
@ -45,7 +48,7 @@ func(pg *Page) WithSizer(sizer *Sizer) *Page {
return pg return pg
} }
// Size returns size used by values and menu, and remaining size available // Usage returns size used by values and menu, and remaining size available
func(pg *Page) Usage() (uint32, uint32, error) { func(pg *Page) Usage() (uint32, uint32, error) {
var l int var l int
var c uint16 var c uint16
@ -97,6 +100,8 @@ func(pg *Page) Map(key string) error {
return nil return nil
} }
// Val gets the mapped content for the given symbol.
//
// Fails if key is not mapped. // Fails if key is not mapped.
func(pg *Page) Val(key string) (string, error) { func(pg *Page) Val(key string) (string, error) {
r := pg.cacheMap[key] r := pg.cacheMap[key]
@ -106,7 +111,7 @@ func(pg *Page) Val(key string) (string, error) {
return r, nil return r, nil
} }
// Moved from cache, MAP should hook to this object // Sizes returned the actual used bytes by each mapped symbol.
func(pg *Page) Sizes() (map[string]uint16, error) { func(pg *Page) Sizes() (map[string]uint16, error) {
sizes := make(map[string]uint16) sizes := make(map[string]uint16)
var haveSink bool var haveSink bool
@ -121,12 +126,11 @@ func(pg *Page) Sizes() (map[string]uint16, error) {
} }
haveSink = true haveSink = true
} }
pg.sinkSize = l
} }
return sizes, nil return sizes, nil
} }
// DefaultRenderTemplate is an adapter to implement the builtin golang text template renderer as resource.RenderTemplate. // RenderTemplate is an adapter to implement the builtin golang text template renderer as resource.RenderTemplate.
func(pg *Page) RenderTemplate(sym string, values map[string]string, idx uint16) (string, error) { func(pg *Page) RenderTemplate(sym string, values map[string]string, idx uint16) (string, error) {
tpl, err := pg.resource.GetTemplate(sym) tpl, err := pg.resource.GetTemplate(sym)
if err != nil { if err != nil {
@ -156,6 +160,26 @@ func(pg *Page) RenderTemplate(sym string, values map[string]string, idx uint16)
return b.String(), err return b.String(), err
} }
// Render renders the current mapped content and menu state against the template associated with the symbol.
func(pg *Page) Render(sym string, idx uint16) (string, error) {
var err error
values, err := pg.prepare(sym, pg.cacheMap, idx)
if err != nil {
return "", err
}
return pg.render(sym, values, idx)
}
// Reset prepared the Page object for re-use.
//
// It clears mappings and removes the sink definition.
func(pg *Page) Reset() {
pg.sink = nil
pg.cacheMap = make(map[string]string)
}
// render menu and all syms except sink, split sink into display chunks // render menu and all syms except sink, split sink into display chunks
// TODO: Function too long, split up // TODO: Function too long, split up
func(pg *Page) prepare(sym string, values map[string]string, idx uint16) (map[string]string, error) { func(pg *Page) prepare(sym string, values map[string]string, idx uint16) (map[string]string, error) {
@ -279,6 +303,7 @@ func(pg *Page) prepare(sym string, values map[string]string, idx uint16) (map[st
return noSinkValues, nil return noSinkValues, nil
} }
// render template, menu (if it exists), and audit size constraint (if it exists).
func(pg *Page) render(sym string, values map[string]string, idx uint16) (string, error) { func(pg *Page) render(sym string, values map[string]string, idx uint16) (string, error) {
var ok bool var ok bool
r := "" r := ""
@ -309,19 +334,3 @@ func(pg *Page) render(sym string, values map[string]string, idx uint16) (string,
return r, nil return r, nil
} }
func(pg *Page) Render(sym string, idx uint16) (string, error) {
var err error
values, err := pg.prepare(sym, pg.cacheMap, idx)
if err != nil {
return "", err
}
return pg.render(sym, values, idx)
}
func(pg *Page) Reset() {
pg.sink = nil
pg.sinkSize = 0
pg.cacheMap = make(map[string]string)
}

View File

@ -1 +0,0 @@
package render

View File

@ -7,15 +7,17 @@ import (
"strings" "strings"
) )
// Sizer splits dynamic contents into individual segments for browseable pages.
type Sizer struct { type Sizer struct {
outputSize uint32 outputSize uint32 // maximum output for a single page.
menuSize uint16 menuSize uint16 // actual menu size for the dynamic page being sized
memberSizes map[string]uint16 memberSizes map[string]uint16 // individual byte sizes of all content to be rendered by template.
totalMemberSize uint32 totalMemberSize uint32 // total byte size of all content to be rendered by template (sum of memberSizes)
crsrs []uint32 crsrs []uint32 // byte offsets in the sink content for browseable pages indices.
sink string sink string // sink symbol.
} }
// NewSizer creates a new Sizer object with the given output size constraint.
func NewSizer(outputSize uint32) *Sizer { func NewSizer(outputSize uint32) *Sizer {
return &Sizer{ return &Sizer{
outputSize: outputSize, outputSize: outputSize,
@ -23,11 +25,13 @@ func NewSizer(outputSize uint32) *Sizer {
} }
} }
// WithMenuSize sets the size of the menu being used in the rendering context.
func(szr *Sizer) WithMenuSize(menuSize uint16) *Sizer { func(szr *Sizer) WithMenuSize(menuSize uint16) *Sizer {
szr.menuSize = menuSize szr.menuSize = menuSize
return szr return szr
} }
// Set adds a content symbol in the state it will be used by the renderer.
func(szr *Sizer) Set(key string, size uint16) error { func(szr *Sizer) Set(key string, size uint16) error {
szr.memberSizes[key] = size szr.memberSizes[key] = size
if size == 0 { if size == 0 {
@ -37,6 +41,7 @@ func(szr *Sizer) Set(key string, size uint16) error {
return nil return nil
} }
// Check audits whether the rendered string is within the output size constraint of the sizer.
func(szr *Sizer) Check(s string) (uint32, bool) { func(szr *Sizer) Check(s string) (uint32, bool) {
l := uint32(len(s)) l := uint32(len(s))
if szr.outputSize > 0 { if szr.outputSize > 0 {
@ -49,6 +54,7 @@ func(szr *Sizer) Check(s string) (uint32, bool) {
return l, true return l, true
} }
// String implements the String interface.
func(szr *Sizer) String() string { func(szr *Sizer) String() string {
var diff uint32 var diff uint32
if szr.outputSize > 0 { if szr.outputSize > 0 {
@ -57,6 +63,9 @@ func(szr *Sizer) String() string {
return fmt.Sprintf("output: %v, member: %v, menu: %v, diff: %v", szr.outputSize, szr.totalMemberSize, szr.menuSize, diff) return fmt.Sprintf("output: %v, member: %v, menu: %v, diff: %v", szr.outputSize, szr.totalMemberSize, szr.menuSize, diff)
} }
// Size gives the byte size of content for a single symbol.
//
// Fails if the symbol has not been registered using Set
func(szr *Sizer) Size(s string) (uint16, error) { func(szr *Sizer) Size(s string) (uint16, error) {
r, ok := szr.memberSizes[s] r, ok := szr.memberSizes[s]
if !ok { if !ok {
@ -65,15 +74,20 @@ func(szr *Sizer) Size(s string) (uint16, error) {
return r, nil return r, nil
} }
// Menusize returns the currently defined menu size.
func(szr *Sizer) MenuSize() uint16 { func(szr *Sizer) MenuSize() uint16 {
return szr.menuSize return szr.menuSize
} }
// AddCursor adds a pagination cursor for the paged sink content.
func(szr *Sizer) AddCursor(c uint32) { func(szr *Sizer) AddCursor(c uint32) {
log.Printf("added cursor: %v", c) log.Printf("added cursor: %v", c)
szr.crsrs = append(szr.crsrs, c) szr.crsrs = append(szr.crsrs, c)
} }
// GetAt the paged symbols for the current page index.
//
// Fails if index requested is out of range.
func(szr *Sizer) GetAt(values map[string]string, idx uint16) (map[string]string, error) { func(szr *Sizer) GetAt(values map[string]string, idx uint16) (map[string]string, error) {
if szr.sink == "" { if szr.sink == "" {
return values, nil return values, nil

View File

@ -72,7 +72,6 @@ func foo() error {
b = vm.NewLine(b, vm.INCMP, []string{"0", "_"}, nil, nil) b = vm.NewLine(b, vm.INCMP, []string{"0", "_"}, nil, nil)
b = vm.NewLine(b, vm.INCMP, []string{"1", "baz"}, nil, nil) b = vm.NewLine(b, vm.INCMP, []string{"1", "baz"}, nil, nil)
b = vm.NewLine(b, vm.INCMP, []string{"2", "long"}, nil, nil) b = vm.NewLine(b, vm.INCMP, []string{"2", "long"}, nil, nil)
//b = vm.NewLine(b, vm.CATCH, []string{"_catch"}, []byte{1}, []uint8{1})
data := make(map[string]string) data := make(map[string]string)
data["inky"] = "one" data["inky"] = "one"
@ -164,6 +163,9 @@ func generate() error {
return nil return nil
} }
// Generate outputs bytecode, templates and content symbols to a temporary directory.
//
// This directory can in turn be used as data source for the the resource.FsResource object.
func Generate() (string, error) { func Generate() (string, error) {
dir, err := ioutil.TempDir("", "festive_testdata_") dir, err := ioutil.TempDir("", "festive_testdata_")
if err != nil { if err != nil {
@ -175,6 +177,12 @@ func Generate() (string, error) {
return dir, err return dir, err
} }
// Generate outputs bytecode, templates and content symbols to a specified directory.
//
// The directory must exist, and must not have been used already in the same code execution.
//
// This directory can in turn be used as data source for the the resource.FsResource object.
func GenerateTo(dir string) error { func GenerateTo(dir string) error {
if dirLock { if dirLock {
return fmt.Errorf("directory already overridden") return fmt.Errorf("directory already overridden")

View File

@ -11,16 +11,17 @@ import (
"git.defalsify.org/festive/state" "git.defalsify.org/festive/state"
) )
// Vm holds sub-components mutated by the vm execution.
type Vm struct { type Vm struct {
st *state.State st *state.State // Navigation and error states.
rs resource.Resource rs resource.Resource // Retrieves content, code, and templates for symbols.
pg *render.Page ca cache.Memory // Loaded content.
ca cache.Memory mn *render.Menu // Menu component of page.
mn *render.Menu sizer *render.Sizer // Apply size constraints to output.
sizer *render.Sizer pg *render.Page // Render outputs with menues to size constraints.
} }
// NewVm creates a new Vm.
func NewVm(st *state.State, rs resource.Resource, ca cache.Memory, sizer *render.Sizer) *Vm { func NewVm(st *state.State, rs resource.Resource, ca cache.Memory, sizer *render.Sizer) *Vm {
vmi := &Vm{ vmi := &Vm{
st: st, st: st,
@ -33,17 +34,16 @@ func NewVm(st *state.State, rs resource.Resource, ca cache.Memory, sizer *render
return vmi return vmi
} }
// Reset re-initializes sub-components for output rendering.
func(vmi *Vm) Reset() { func(vmi *Vm) Reset() {
vmi.mn = render.NewMenu() vmi.mn = render.NewMenu()
vmi.pg.Reset() vmi.pg.Reset()
vmi.pg = vmi.pg.WithMenu(vmi.mn) //render.NewPage(vmi.ca, vmi.rs).WithMenu(vmi.mn) vmi.pg = vmi.pg.WithMenu(vmi.mn)
if vmi.sizer != nil { if vmi.sizer != nil {
vmi.pg = vmi.pg.WithSizer(vmi.sizer) vmi.pg = vmi.pg.WithSizer(vmi.sizer)
} }
} }
//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. // Run extracts individual op codes and arguments and executes them.
// //
// Each step may update the state. // Each step may update the state.
@ -394,6 +394,7 @@ func(vm *Vm) RunMPrev(b []byte, ctx context.Context) ([]byte, error) {
return b, nil return b, nil
} }
// Render wraps output rendering, and handles error when attempting to browse beyond the rendered page count.
func(vm *Vm) Render(ctx context.Context) (string, error) { func(vm *Vm) Render(ctx context.Context) (string, error) {
changed, err := vm.st.ResetFlag(state.FLAG_DIRTY) changed, err := vm.st.ResetFlag(state.FLAG_DIRTY)
if err != nil { if err != nil {