dev.texi (10543B)
1 @node dev 2 @chapter Developing with vise 3 4 5 @section Code repository structure 6 7 @table @code 8 @item asm 9 Assembly parser and compiler. 10 @item cache 11 Holds and manages all loaded content. 12 @item engine 13 Outermost interface. Orchestrates execution of bytecode against input. 14 @item lang 15 Validation and specification of language context. 16 @item logging 17 Logging interface and build tags for loglevels. 18 @item persist 19 Interface and reference implementation of `state` and `cache` persistence across asynchronous vm executions. 20 @item render 21 Renders menu and templates, and enforces output size constraints. 22 @item resource 23 Retrieves data and bytecode from external symbols, and retrieves templates. 24 @item state 25 Holds the bytecode buffer, error states and navigation states. 26 @item vm 27 Defines instructions, and applies transformations according to the instructions. 28 @end table 29 30 31 @section Interacting with @code{vise} 32 33 Implementers of @code{vise} should interface with the system using the @code{engine} module. 34 35 The engine comes in two implementations, one volatile base implemetnation and a subclass that includes persistent state. 36 37 38 @subsection Modes of operation 39 40 The @code{engine} module provides three different modes of operation for the engine implementations. 41 42 43 @subsubsection Manual operation 44 45 Directly interaction with an @code{engine.Engine} instance. 46 47 The engine is manually initialized, and execution must be explicitly triggered with input every time the VM yields control. 48 49 Output flushing must also be operated manually. 50 51 The interface is the same for both persistent and volatile operation. 52 53 54 @subsubsection Synchronous loop 55 56 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. 57 58 The loop may be either persistent or volatile. 59 60 This mode drives the interactive driver execution tool. 61 62 63 @subsubsection Asynchronous one-shot 64 65 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. 66 67 This mode of operation can only be used with persistent state. 68 69 70 @subsection Configuration 71 72 The engine configuration defines the top-level parameters for the execution environment, including maximum output size, default language, execution entry point and more. 73 74 Please refer to @code{engine.Config} for details. 75 76 77 @subsection Sessions 78 79 The @code{engine.Config.SessionId} is used to disambiguate the end-user that is interacting with the engine. 80 81 For example, in a @abbr{USSD} context, the @code{SessionId} may be the @emph{phone number} of the end-user. 82 83 84 @anchor{execution_context} 85 @subsection Execution context 86 87 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: 88 89 @itemize 90 @item When resolving symbols with @code{LOAD}. (@code{resource.EntryFunc}). 91 @item When resolving menu symbols (@code{resource.Resource.GetMenu}). 92 @item When retrieving node templates (@code{resource.Resource.GetTemplate}). 93 @end itemize 94 95 96 @section Resolving resources 97 98 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. 99 100 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. 101 102 @subsection Memory resource implementation 103 104 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...}. 105 106 The @code{resource.MemResource} implementation is primarily useful for use in tests. 107 108 109 @subsection Filesystem resource implementation 110 111 The Filesystem based resource implemementation is used by the @code{dev/interactive} tool, aswell as the executable examples in @file{examples/} directory. 112 113 It is instantiated with a base directory location relative to which all resources are read. 114 115 116 @subsubsection Bytecode (@code{resource.Resource.GetCode}) 117 118 Read from @file{basedir/<node>.bin}. 119 120 121 @subsubsection Templates (@code{resource.Resource.GetTemplate}) 122 123 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}. 124 125 If reading the language specific template fails (or if no language has been set), template will be read from @file{basedir/<node>}. 126 127 A missing template file will result in load failure and program termination. 128 129 130 @subsubsection Menus (@code{resource.Resource.GetMenu}) 131 132 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}. 133 134 If reading the language specific menu label fails (or if no language has been set), label will be read from @file{basedir/<label>_menu}. 135 136 If this also fails, the implementation returns the original label used for lookup. 137 138 139 @subsubsection External symbols (@code{resource.Resource.FuncFor}) 140 141 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. 142 143 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}. 144 145 If reading the language specific entry fails (or if no language has been set), entry will be read from @file{basedir/<symbol>.txt}. 146 147 A missing entry will result in load failure and program termination. 148 149 The implementation contains no built-in handling of the @code{SessionId} supplied by the context. 150 151 152 @section Logging 153 154 Loglevels are set at compile-time using the following build tags: 155 156 @itemize 157 @item @code{lognone} 158 @item @code{logerror} 159 @item @code{logwarn} 160 @item @code{loginfo} 161 @item @code{logdebug} 162 @item @code{logtrace} 163 @end itemize 164 165 Only use @strong{ONE} of these tags. 166 167 The default tag is @code{lognone} which disables logging completely. 168 169 @code{logging.Logger} defines the logging interface. It is faintly inspired by the experimental @url{https://pkg.go.dev/golang.org/x/exp/slog) package, in that it differentiates explicit context logging, slog}. 170 171 172 @section Tools 173 174 Located in the @file{dev/} directory of the source code repository. 175 176 177 @subsection Test data generation 178 179 @example 180 go run ./dev/gendata/ <directory> 181 @end example 182 183 Outputs bytecodes and templates for test data scenarios used in `engine` unit tests. 184 185 186 @subsection Interactive runner 187 188 @example 189 go run ./dev/interactive [-d <data_directory>] [--root <root_symbol>] [--session-id <session_id>] [--persist] 190 @end example 191 192 Creates a new interactive session using @code{engine.DefaultEngine}, starting execution at symbol @code{root_symbol} 193 194 @code{data_directory} points to a directory where templates and bytecode is to be found (in the same format as generated by @file{dev/gendata}). 195 196 If @code{data_directory} is not set, current directory will be used. 197 198 if @code{root_symbol} is not set, the symbol @code{root} will be used. 199 200 if @code{session_id} is set, mutable data will be stored and retrieved keyed by the given identifer (if implemented). 201 202 If @code{persist} is set, the execution state will be persisted across sessions. 203 204 205 @subsection Assembler 206 207 @example 208 go run ./dev/asm <assembly_file> 209 @end example 210 211 Will output bytecode on STDOUT generated from a valid assembly file. 212 213 214 @subsection Disassembler 215 216 @example 217 go run ./dev/disasm/ <binary_file> 218 @end example 219 220 Will list all the instructions on STDOUT from a valid binary file. 221 222 223 @subsection Interactive case examples 224 225 Found in @file{examples/}. 226 227 Be sure to @code{make examples} before running them. 228 229 Can be run with: 230 231 @example 232 go run ./examples/<case> [...] 233 @end example 234 235 except helloworld which is run as 236 237 @example 238 go run ./dev/interactive -d ./examples/helloworld [...] 239 @end example 240 241 The available options are the same as for the @file{dev/interactive} tool. 242 243 Contents of the case directory: 244 245 @table @file 246 @item *.vis 247 assembly code. 248 @item *.bin 249 bytecode for each node symbol (only available after make). 250 @item *.txt.orig 251 default contents of a single data entry. 252 @item *.txt 253 current contents of a single data entry (only available after make). 254 @end table 255 256 257 @section Assembly examples 258 259 See @file{testdata/*.vis} 260 261 262 @section Bytecode example 263 264 Currently the following rules apply for encoding in version @code{0}: 265 266 @itemize 267 @item A code instruction is a @emph{big-endian} 2-byte value. See @file{vm/opcodes.go} for valid opcode values. 268 @item @code{symbol} value is encoded as @emph{one byte} of string length, after which the byte-value of the string follows. 269 @item @code{size} value is encoded as @emph{one byte} of numeric length, after which the @emph{big-endian} byte-value of the integer follows. 270 @item @code{signal} value is encoded as @emph{one byte} of byte length, after which a byte-array representing the defined signal follows. 271 @end itemize 272 273 274 @subsection Example 275 276 (Minimal, WIP) 277 278 @verbatim 279 000a 03666f6f 05746f666f6f # MOUT tofoo foo - display a menu entry for choice "foo", described by "to foo" 280 0008 03666f6f 03626172 # INCMP bar foo - move to node "bar" if input is "FOO" 281 0001 0461696565 01 01 # CATCH aiee 1 1 - move to node "aiee" (and immediately halt) if input match flag (1) is not set (1) 282 0003 04616263 020104 # LOAD abc 260 - execute code symbol "abc" with a result size limit of 260 (2 byte BE integer, 0x0104) 283 0003 04646566 00 # LOAD def 0 - execute code symbol "abc" with no size limit (sink) 284 0005 04616263 # MAP abc - make "abc" available for renderer 285 0007 # HALT - stop execution (require new input to continue) 286 0006 0461313233 # MOVE a123 - move to node "a123" (regardless of input) 287 0007 # HALT - stop execution 288 @end verbatim