go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

commit c7e3ffa8d8070a2a4aa77bbb7368fc38bfe5d7a8
parent bb6a692840a0ace15a90f40a617e8892108ad0a9
Author: lash <dev@holbrook.no>
Date:   Mon,  1 May 2023 20:31:00 +0100

Add description of resource implementation

Diffstat:
Mdoc/texinfo/cache.texi | 1+
Mdoc/texinfo/dev.texi | 123++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Ddoc/texinfo/engine.texi | 64----------------------------------------------------------------
Mdoc/texinfo/index.texi | 1-
Mresource/fs.go | 13++++++++-----
5 files changed, 131 insertions(+), 71 deletions(-)

diff --git a/doc/texinfo/cache.texi b/doc/texinfo/cache.texi @@ -12,6 +12,7 @@ It can be used to: @end itemize +@anchor{load_handler} @section The @code{LOAD} handler In the @emph{golang} code, the handler of the @code{LOAD} instruction is an implementation of the @code{resource.Resource} interface. diff --git a/doc/texinfo/dev.texi b/doc/texinfo/dev.texi @@ -1,5 +1,5 @@ @node dev -@chapter Development +@chapter Developing with vise @section Code repository structure @@ -28,6 +28,127 @@ Defines instructions, and applies transformations according to the instructions. @end table +@section Interacting with @code{vise} + +Implementers of @code{vise} should interface with the system using the @code{engine} module. + +The engine comes in two implementations, one volatile base implemetnation and a subclass that includes persistent state. + + +@subsection Modes of operation + +The @code{engine} module provides three different modes of operation for the engine implementations. + + +@subsubsection Manual operation + +Directly interaction with an @code{engine.Engine} instance. + +The engine is manually initialized, and execution must be explicitly triggered with input every time the VM yields control. + +Output flushing must also be operated manually. + +The interface is the same for both persistent and volatile operation. + + +@subsubsection Synchronous loop + +Receives input from a reader and writes into to a writer, and executes the underlying @code{engine.Engine} with given inputs until execution is terminated. + +The loop may be either persistent or volatile. + +This mode drives the interactive driver execution tool. + + +@subsubsection Asynchronous one-shot + +Compatible with e.g. a network socket or HTTP frontend. The @code{engine.RunPersisted} method restores a persisted state and runs one single input until VM yield after which the new state is persisted. + +This mode of operation can only be used with persistent state. + + +@subsection Configuration + +The engine configuration defines the top-level parameters for the execution environment, including maximum output size, default language, execution entry point and more. + +Please refer to @code{engine.Config} for details. + + +@subsection Sessions + +The @code{engine.Config.SessionId} is used to disambiguate the end-user that is interacting with the engine. + +For example, in a @abbr{USSD} context, the @code{SessionId} may be the @emph{phone number} of the end-user. + + +@anchor{execution_context} +@subsection Execution context + +The engine stores the @code{SessionId} aswell as the current chosen @code{lang.Language} in the execution context. This is passed through to the VM operation, and is available for client code, specifically: + +@itemize +@item When resolving symbols with @code{LOAD}. (@code{resource.EntryFunc}). +@item When resolving menu symbols (@code{resource.Resource.GetMenu}). +@item When retrieving node templates (@code{resource.Resource.GetTemplate}). +@end itemize + + +@section Resolving resources + +The core of implementation code is defined by implementing the @code{resource.Resource} interface. This is also described in the @ref{load_handler, LOAD handler} section. + +In addition to resolving external code symbols, @code{resource.Resource} implementations also translate @emph{menu labels} and @emph{templates} based on the current language context, and retrieves bytecode for execution nodes. + +@subsection Memory resource implementation + +One of two reference implementations of @code{resource.Resource} is the @code{resource.MemResource} class. It enables the client to register all node and symbol resolutions at runtime, using its functions prefixed with @code{Add...}. + +The @code{resource.MemResource} implementation is primarily useful for use in tests. + + +@subsection Filesystem resource implementation + +The Filesystem based resource implemementation is used by the @code{dev/interactive} tool, aswell as the executable examples in @file{examples/} directory. + +It is instantiated with a base directory location relative to which all resources are read. + + +@subsubsection Bytecode (@code{resource.Resource.GetCode}) + +Read from @file{basedir/<node>.bin}. + + +@subsubsection Templates (@code{resource.Resource.GetTemplate}) + +If language has been set, the template will be read from @file{basedir/<node>_<lang>}. For example, the @emph{norwegian} template for the node @code{root} will be read from @file{basedir/root_nor}. + +If reading the language specific template fails (or if no language has been set), template will be read from @file{basedir/<node>}. + +A missing template file will result in load failure and program termination. + + +@subsubsection Menus (@code{resource.Resource.GetMenu}) + +If language has been set, the template will be read from @file{basedir/<label>_<lang>_menu}. For example, the @emph{norwegian} template for the menu label @code{foo} will be read from @file{basedir/foo_nor_menu}. + +If reading the language specific menu label fails (or if no language has been set), label will be read from @file{basedir/<label>_menu}. + +If this also fails, the implementation returns the original label used for lookup. + + +@subsubsection External symbols (@code{resource.Resource.FuncFor}) + +The implementation allows setting resolver functions for symbols at runtime, using the @code{resource.FsResource.AddLocalFunc} method. This registers an @code{resource.FsResource.EntryFunc} with the lookup symbol as key. Note that the @code{EntryFunc} receives the language setting through the execution context. + +If no function has been registered for the requested symbol, it will be looked up in the filesystem on @file{basedir/<symbol>_<lang>.txt}. For example, the @emph{norwegian} entry for the symbol @code{foo} will be read from @file{basedir/foo_nor.txt}. + +If reading the language specific entry fails (or if no language has been set), entry will be read from @file{basedir/<symbol>.txt}. + +A missing entry will result in load failure and program termination. + +The implementation contains no built-in handling of the @code{SessionId} supplied by the context. + + @section Logging Loglevels are set at compile-time using the following build tags: diff --git a/doc/texinfo/engine.texi b/doc/texinfo/engine.texi @@ -1,64 +0,0 @@ -@node engine -@chapter Using vise - -Implementers of @code{vise} should interface with the system using the @code{engine} module. - -The engine comes in two implementations, one volatile base implemetnation and a subclass that includes persistent state. - - -@section Modes of operation - -The @code{engine} module provides three different modes of operation for the engine implementations. - - -@subsection Manual operation - -Directly interaction with an @code{engine.Engine} instance. - -The engine is manually initialized, and execution must be explicitly triggered with input every time the VM yields control. - -Output flushing must also be operated manually. - -The interface is the same for both persistent and volatile operation. - - -@subsection Synchronous loop - -Receives input from a reader and writes into to a writer, and executes the underlying @code{engine.Engine} with given inputs until execution is terminated. - -The loop may be either persistent or volatile. - -This mode drives the interactive driver execution tool. - - -@subsection Asynchronous one-shot - -Compatible with e.g. a network socket or HTTP frontend. The @code{engine.RunPersisted} method restores a persisted state and runs one single input until VM yield after which the new state is persisted. - -This mode of operation can only be used with persistent state. - - -@section Configuration - -The engine configuration defines the top-level parameters for the execution environment, including maximum output size, default language, execution entry point and more. - -Please refer to @code{engine.Config} for details. - - -@section Sessions - -The @code{engine.Config.SessionId} is used to disambiguate the end-user that is interacting with the engine. - -For example, in a @abbr{USSD} context, the @code{SessionId} may be the @emph{phone number} of the end-user. - - -@anchor{execution_context} -@section Execution context - -The engine stores the @code{SessionId} aswell as the current chosen @code{lang.Language} in the execution context. This is passed through to the VM operation, and is available for client code, specifically: - -@itemize -@item When resolving symbols with @code{LOAD}. (@code{resource.EntryFunc}). -@item When resolving menu symbols (@code{resource.Resource.GetMenu}). -@item When retrieving node templates (@code{resource.Resource.GetTemplate}). -@end itemize diff --git a/doc/texinfo/index.texi b/doc/texinfo/index.texi @@ -30,7 +30,6 @@ Released 2023 under AGPL3 @include navigation.texi @include cache.texi @include render.texi -@include engine.texi @include language.texi @include cookbook.texi @include dev.texi diff --git a/resource/fs.go b/resource/fs.go @@ -127,14 +127,17 @@ func(fsr FsResource) getFunc(ctx context.Context, sym string, input []byte) (Res } func(fsr FsResource) getFuncNoCtx(sym string, input []byte, language *lang.Language) (Result, error) { - fb := sym + ".txt" - fp := path.Join(fsr.Path, fb) - fpl := fp + fb := sym + fbl := fb if language != nil { - fpl += "_" + language.Code + fbl += "_" + language.Code } + fb += ".txt" + fbl += ".txt" + fp := path.Join(fsr.Path, fb) + fpl := path.Join(fsr.Path, fbl) Logg.Debugf("getfunc search dir", "dir", fsr.Path, "path", fp, "lang_path", fpl, "sym", sym, "language", language) - r, err := ioutil.ReadFile(fp) + r, err := ioutil.ReadFile(fpl) if err != nil { if errors.Is(err, os.ErrNotExist) { if fpl != fp {