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 {
Instructions []*Instruction `@@*`
}
// Arg holds all parsed argument elements of a single line of assembly code.
type Arg struct {
Sym *string `(@Sym 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)
}
// String implements the String interface.
func (a Arg) String() string {
s := "[Arg]"
if a.Sym != nil {
@ -235,12 +238,14 @@ func (a Arg) String() string {
return fmt.Sprintf(s)
}
// Instruction represents one full line of assembly code.
type Instruction struct {
OpCode string `@Ident`
OpArg Arg `(Whitespace @@)?`
Comment string `Comment? EOL`
}
// String implement the String interface.
func (i Instruction) String() string {
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:])
}
// Batcher handles assembly commands that generates multiple instructions, such as menu navigation commands.
type Batcher struct {
menuProcessor MenuProcessor
inMenu bool
}
// NewBatcher creates a new Batcher objcet.
func NewBatcher(mp MenuProcessor) Batcher {
return Batcher{
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) {
if !bt.inMenu {
return 0, nil
@ -323,6 +331,7 @@ func(bt *Batcher) MenuExit(w io.Writer) (int, error) {
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) {
bt.inMenu = true
var selector string
@ -343,10 +352,12 @@ func(bt *Batcher) MenuAdd(w io.Writer, code string, arg Arg) (int, error) {
return 0, err
}
// Exit is a synonym for MenuExit
func(bt *Batcher) Exit(w io.Writer) (int, error) {
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) {
rd := strings.NewReader(s)
ast, err := asmParser.Parse("file", rd)

View File

@ -6,6 +6,7 @@ import (
"git.defalsify.org/festive/vm"
)
// BatchCode defines quasi-opcodes that expand to mulitple individual vm instructions.
type BatchCode uint16
const (
@ -32,15 +33,22 @@ type menuItem struct {
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 {
items []menuItem
size uint32
}
// NewMenuProcessor creates a new MenuProcessor object.
func NewMenuProcessor() 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 {
bopCode := batchCode[bop]
if bopCode == 0 {
@ -59,6 +67,7 @@ func(mp *MenuProcessor) Add(bop string, choice string, display string, target st
return nil
}
// ToLines returns the generated bytecode from the added menu batch instructions.
func (mp *MenuProcessor) ToLines() []byte {
preLines := []byte{}
postLines := []byte{}

11
go/cache/cache.go vendored
View File

@ -5,14 +5,12 @@ import (
"log"
)
// Cache stores loaded content, enforcing size limits and keeping track of size usage.
type Cache struct {
CacheSize uint32 // Total allowed cumulative size of 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
//CacheMap map[string]string // Mapped
//outputSize uint32 // Max size of output
sizes map[string]uint16 // Size limits for all loaded symbols.
//sink *string
}
// NewCache creates a new ready-to-use cache object
@ -21,7 +19,6 @@ func NewCache() *Cache {
Cache: []map[string]string{make(map[string]string)},
sizes: make(map[string]uint16),
}
//ca.resetCurrent()
return ca
}
@ -97,9 +94,6 @@ func(ca *Cache) Update(key string, value string) error {
r := ca.Cache[checkFrame][key]
l := uint32(len(r))
ca.Cache[checkFrame][key] = ""
//if ca.CacheMap[key] != "" {
// ca.CacheMap[key] = value
//}
ca.CacheUseSize -= l
sz := ca.checkCapacity(value)
if sz == 0 {
@ -113,6 +107,9 @@ func(ca *Cache) Update(key string, value string) error {
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) {
i := ca.frameOf(key)
r, ok := ca.Cache[i][key]

1
go/cache/memory.go vendored
View File

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

View File

@ -6,6 +6,7 @@ import (
"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 {
st := state.NewState(0)
rs := resource.NewFsResource(dir)
@ -13,6 +14,7 @@ func NewDefaultEngine(dir string) Engine {
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 {
st := state.NewState(0)
rs := resource.NewFsResource(dir)

View File

@ -13,13 +13,12 @@ import (
"git.defalsify.org/festive/vm"
)
// Config globally defines behavior of all components driven by the engine.
type Config struct {
OutputSize uint32
// FlagCount uint32
// CacheSize uint32
OutputSize uint32 // Maximum size of output from a single rendered page
}
// 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 {
st *state.State
rs resource.Resource

View File

@ -2,7 +2,6 @@ package engine
import (
"bufio"
// "bytes"
"context"
"fmt"
"io"
@ -10,17 +9,26 @@ import (
"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 {
err := en.Init(startSym, ctx)
if err != nil {
return fmt.Errorf("cannot init: %v\n", err)
}
//b := bytes.NewBuffer(nil)
//en.WriteResult(b, ctx)
en.WriteResult(writer, ctx)
err = en.WriteResult(writer, ctx)
if err != nil {
return err
}
writer.Write([]byte{0x0a})
//fmt.Println(b.String())
running := true
bufReader := bufio.NewReader(reader)

View File

@ -4,11 +4,13 @@ import (
"fmt"
)
// BrowseError is raised when browsing outside the page range of a rendered node.
type BrowseError struct {
Idx uint16
PageCount uint16
}
// Error implements the Error interface.
func(err *BrowseError) Error() string {
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 {
menu [][2]string
browse BrowseConfig
pageCount uint16
canNext bool
canPrevious bool
outputSize uint16
menu [][2]string // selector and title for menu items.
browse BrowseConfig // browse definitions.
pageCount uint16 // number of pages the menu should represent.
canNext bool // availability flag for the "next" browse option.
canPrevious bool // availability flag for the "previous" browse option.
outputSize uint16 // maximum size constraint for the menu.
}
// 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
}
// 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.
//
// 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
}
// prepare menu object for re-use.
func(m *Menu) reset() {
if m.browse.NextAvailable {
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"
)
// Page exectues output rendering into pages constrained by size.
type Page struct {
cacheMap map[string]string // Mapped
cache cache.Memory
resource resource.Resource
menu *Menu
sink *string
sinkSize uint16
sizer *Sizer
cacheMap map[string]string // Mapped content symbols
cache cache.Memory // Content store.
resource resource.Resource // Symbol resolver.
menu *Menu // Menu rendererer.
sink *string // Content symbol rendered by dynamic size.
sizer *Sizer // Process size constraints.
}
// NewPage creates a new Page object.
func NewPage(cache cache.Memory, rs resource.Resource) *Page {
return &Page{
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 {
pg.menu = menu
if pg.sizer != nil {
@ -37,6 +39,7 @@ func(pg *Page) WithMenu(menu *Menu) *Page {
return pg
}
// WithSizer sets a size constraints definition for the page.
func(pg *Page) WithSizer(sizer *Sizer) *Page {
pg.sizer = sizer
if pg.menu != nil {
@ -45,7 +48,7 @@ func(pg *Page) WithSizer(sizer *Sizer) *Page {
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) {
var l int
var c uint16
@ -97,6 +100,8 @@ func(pg *Page) Map(key string) error {
return nil
}
// Val gets the mapped content for the given symbol.
//
// Fails if key is not mapped.
func(pg *Page) Val(key string) (string, error) {
r := pg.cacheMap[key]
@ -106,7 +111,7 @@ func(pg *Page) Val(key string) (string, error) {
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) {
sizes := make(map[string]uint16)
var haveSink bool
@ -121,12 +126,11 @@ func(pg *Page) Sizes() (map[string]uint16, error) {
}
haveSink = true
}
pg.sinkSize = l
}
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) {
tpl, err := pg.resource.GetTemplate(sym)
if err != nil {
@ -156,6 +160,26 @@ func(pg *Page) RenderTemplate(sym string, values map[string]string, idx uint16)
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
// TODO: Function too long, split up
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
}
// 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) {
var ok bool
r := ""
@ -309,19 +334,3 @@ func(pg *Page) render(sym string, values map[string]string, idx uint16) (string,
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"
)
// Sizer splits dynamic contents into individual segments for browseable pages.
type Sizer struct {
outputSize uint32
menuSize uint16
memberSizes map[string]uint16
totalMemberSize uint32
crsrs []uint32
sink string
outputSize uint32 // maximum output for a single page.
menuSize uint16 // actual menu size for the dynamic page being sized
memberSizes map[string]uint16 // individual byte sizes of all content to be rendered by template.
totalMemberSize uint32 // total byte size of all content to be rendered by template (sum of memberSizes)
crsrs []uint32 // byte offsets in the sink content for browseable pages indices.
sink string // sink symbol.
}
// NewSizer creates a new Sizer object with the given output size constraint.
func NewSizer(outputSize uint32) *Sizer {
return &Sizer{
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 {
szr.menuSize = menuSize
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 {
szr.memberSizes[key] = size
if size == 0 {
@ -37,6 +41,7 @@ func(szr *Sizer) Set(key string, size uint16) error {
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) {
l := uint32(len(s))
if szr.outputSize > 0 {
@ -49,6 +54,7 @@ func(szr *Sizer) Check(s string) (uint32, bool) {
return l, true
}
// String implements the String interface.
func(szr *Sizer) String() string {
var diff uint32
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)
}
// 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) {
r, ok := szr.memberSizes[s]
if !ok {
@ -65,15 +74,20 @@ func(szr *Sizer) Size(s string) (uint16, error) {
return r, nil
}
// Menusize returns the currently defined menu size.
func(szr *Sizer) MenuSize() uint16 {
return szr.menuSize
}
// AddCursor adds a pagination cursor for the paged sink content.
func(szr *Sizer) AddCursor(c uint32) {
log.Printf("added cursor: %v", 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) {
if szr.sink == "" {
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{"1", "baz"}, 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["inky"] = "one"
@ -164,6 +163,9 @@ func generate() error {
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) {
dir, err := ioutil.TempDir("", "festive_testdata_")
if err != nil {
@ -175,6 +177,12 @@ func Generate() (string, error) {
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 {
if dirLock {
return fmt.Errorf("directory already overridden")

View File

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