dev.texi (12854B)
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 db 13 Provides interface and implementations for data storage and retrieval backends. 14 @item engine 15 Outermost interface. Orchestrates execution of bytecode against input. 16 @item lang 17 Validation and specification of language context. 18 @item logging 19 Logging interface and build tags for loglevels. 20 @item persist 21 Provides `state` and `cache` persistence across asynchronous vm executions. 22 @item render 23 Renders menu and templates, and enforces output size constraints. 24 @item resource 25 Resolves bytecode, translations, templates and menu symbols from external symbols. 26 @item state 27 Holds the bytecode buffer, error states and navigation states. 28 @item vm 29 Defines instructions, and applies transformations according to the instructions. 30 @end table 31 32 33 @section Interacting with @code{vise} 34 35 Implementers of @code{vise} should interface with the system using the @code{engine} module. 36 37 The engine comes in two implementations, one volatile base implemetnation and a subclass that includes persistent state. 38 39 40 @subsection Modes of operation 41 42 The @code{engine} module provides three different modes of operation for the engine implementations. 43 44 45 @subsubsection Manual operation 46 47 Directly interaction with an @code{engine.Engine} instance. 48 49 The engine is manually initialized, and execution must be explicitly triggered with input every time the VM yields control. 50 51 Output flushing must also be operated manually. 52 53 The interface is the same for both persistent and volatile operation. 54 55 56 @subsubsection Synchronous loop 57 58 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. 59 60 The loop may be either persistent or volatile. 61 62 This mode drives the interactive driver execution tool. 63 64 65 @subsubsection Asynchronous one-shot 66 67 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. 68 69 This mode of operation can only be used with persistent state. 70 71 72 @subsection Configuration 73 74 The engine configuration defines the top-level parameters for the execution environment, including maximum output size, default language, execution entry point and more. 75 76 Please refer to @code{engine.Config} for details. 77 78 79 @anchor{sessions} 80 @subsection Sessions 81 82 The @code{engine.Config.SessionId} is used to disambiguate the end-user that is interacting with the engine. 83 84 For example, in a @abbr{USSD} context, the @code{SessionId} may be the @emph{phone number} of the end-user. 85 86 87 @anchor{execution_context} 88 @subsection Execution context 89 90 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: 91 92 @itemize 93 @item When resolving symbols with @code{LOAD}. (@code{resource.EntryFunc}). 94 @item When resolving menu symbols (@code{resource.Resource.GetMenu}). 95 @item When retrieving node templates (@code{resource.Resource.GetTemplate}). 96 @end itemize 97 98 99 @subsection Blocking execution 100 101 Using the @code{engine.SetFirst()} method, a function may be defined that executes before the pending bytecode in the VM state. 102 103 The function uses the same signature as the external functions executed by @code{resource} for @code{LOAD} instructions. 104 105 This can be for example be used to prevent access to execution for a blocked user account, or as an override while doing maintenance. 106 107 To prevent VM execution from the pre-VM check, the flag @code{TERMINATE} should be set in the @code{resource.Result.FlagSet} array. 108 109 110 @section Resolving resources 111 112 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. 113 114 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. 115 116 @subsection Memory resource implementation 117 118 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...}. 119 120 The @code{resource.MemResource} implementation is primarily useful for use in tests. 121 122 123 @subsection Filesystem resource implementation 124 125 The Filesystem based resource implemementation is used by the @code{dev/interactive} tool, aswell as the executable examples in @file{examples/} directory. 126 127 It is instantiated with a base directory location relative to which all resources are read. 128 129 130 @subsubsection Bytecode (@code{resource.Resource.GetCode}) 131 132 Read from @file{basedir/<node>.bin}. 133 134 135 @subsubsection Templates (@code{resource.Resource.GetTemplate}) 136 137 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}. 138 139 If reading the language specific template fails (or if no language has been set), template will be read from @file{basedir/<node>}. 140 141 A missing template file will result in load failure and program termination. 142 143 144 @subsubsection Menus (@code{resource.Resource.GetMenu}) 145 146 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}. 147 148 If reading the language specific menu label fails (or if no language has been set), label will be read from @file{basedir/<label>_menu}. 149 150 If this also fails, the implementation returns the original label used for lookup. 151 152 153 @subsubsection External symbols (@code{resource.Resource.FuncFor}) 154 155 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. 156 157 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}. 158 159 If reading the language specific entry fails (or if no language has been set), entry will be read from @file{basedir/<symbol>.txt}. 160 161 A missing entry will result in load failure and program termination. 162 163 The implementation contains no built-in handling of the @code{SessionId} supplied by the context. 164 165 166 @section Data provider 167 168 The @code{db.Db} interface provides methods to get and set data to key-value stores. 169 170 The storage keys are partitioned according to the @ref{sessions, session} context, aswell as what type of data is being stored or retrieved. 171 172 The interface and the data types are defined in @code{db/db.go}. 173 174 The included implementations are: 175 176 @table @code 177 @item MemDb 178 An volatile, in-process store. Used in most tests. 179 @item FsDb 180 A filesystem-backed store using subdirectories to separate sessions. 181 @item GdbmDb 182 A @url{https://www.gnu.org/software/gdbm/gdbm,gdbm} backed store. 183 @item PgDb 184 A @url{https://www.postgresql.org/,Postgres} backed store, using a single table with two @code{BYTEA} columns and a connection pool. 185 @end table 186 187 188 @subsection Uses 189 190 @code{db.Db} may fulfill all local data requirements in @code{vise}, including: 191 192 @itemize 193 @item Resource retrieval 194 @item State and cache persistence 195 @item Application data 196 @end itemize 197 198 199 @subsection Using data provider with resources 200 201 The @code{resource.dbGetter} assists in using a @code{db.Db} implementation. 202 203 Its functions may be assigned individually to a @code{resource.MenuResource}, allowing for co-existence of @code{db.Db} backed resources, aswell as from other sources. 204 205 206 @subsection State persistence 207 208 Any asynchronous or consecutive synchronous operation of the @code{engine.Engine} requires persistence of the associated @code{state.State} and @code{cache.Memory}. This is achieved using @code{persist.Persister}, instantiated with a @code{db.Db} implementation. 209 210 The @code{db.Db} used for persistence does not need to be the same as e.g. used for retrieval of resources, or even for application data. 211 212 213 @section Logging 214 215 Loglevels are set at compile-time using the following build tags: 216 217 @itemize 218 @item @code{lognone} 219 @item @code{logerror} 220 @item @code{logwarn} 221 @item @code{loginfo} 222 @item @code{logdebug} 223 @item @code{logtrace} 224 @end itemize 225 226 Only use @strong{ONE} of these tags. 227 228 The default tag is @code{lognone} which disables logging completely. 229 230 @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}. 231 232 233 @section Tools 234 235 Located in the @file{dev/} directory of the source code repository. 236 237 238 @subsection Test data generation 239 240 @example 241 go run ./dev/gendata/ <directory> 242 @end example 243 244 Outputs bytecodes and templates for test data scenarios used in `engine` unit tests. 245 246 247 @subsection Interactive runner 248 249 @example 250 go run ./dev/interactive [-d <data_directory>] [--root <root_symbol>] [--session-id <session_id>] [--persist] 251 @end example 252 253 Creates a new interactive session using @code{engine.DefaultEngine}, starting execution at symbol @code{root_symbol} 254 255 @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}). 256 257 If @code{data_directory} is not set, current directory will be used. 258 259 if @code{root_symbol} is not set, the symbol @code{root} will be used. 260 261 if @code{session_id} is set, mutable data will be stored and retrieved keyed by the given identifer (if implemented). 262 263 If @code{persist} is set, the execution state will be persisted across sessions. 264 265 266 @subsection Assembler 267 268 @example 269 go run ./dev/asm <assembly_file> 270 @end example 271 272 Will output bytecode on STDOUT generated from a valid assembly file. 273 274 275 @subsection Disassembler 276 277 @example 278 go run ./dev/disasm/ <binary_file> 279 @end example 280 281 Will list all the instructions on STDOUT from a valid binary file. 282 283 284 @subsection Interactive case examples 285 286 Found in @file{examples/}. 287 288 Be sure to @code{make examples} before running them. 289 290 Can be run with: 291 292 @example 293 go run ./examples/<case> [...] 294 @end example 295 296 except helloworld which is run as 297 298 @example 299 go run ./dev/interactive -d ./examples/helloworld [...] 300 @end example 301 302 The available options are the same as for the @file{dev/interactive} tool. 303 304 Contents of the case directory: 305 306 @table @file 307 @item *.vis 308 assembly code. 309 @item *.bin 310 bytecode for each node symbol (only available after make). 311 @item *.txt.orig 312 default contents of a single data entry. 313 @item *.txt 314 current contents of a single data entry (only available after make). 315 @end table 316 317 318 @section Assembly examples 319 320 See @file{testdata/*.vis} 321 322 323 @section Bytecode example 324 325 Currently the following rules apply for encoding in version @code{0}: 326 327 @itemize 328 @item A code instruction is a @emph{big-endian} 2-byte value. See @file{vm/opcodes.go} for valid opcode values. 329 @item @code{symbol} value is encoded as @emph{one byte} of string length, after which the byte-value of the string follows. 330 @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. 331 @item @code{signal} value is encoded as @emph{one byte} of byte length, after which a byte-array representing the defined signal follows. 332 @end itemize 333 334 335 @subsection Example 336 337 (Minimal, WIP) 338 339 @verbatim 340 000a 03666f6f 05746f666f6f # MOUT tofoo foo - display a menu entry for choice "foo", described by "to foo" 341 0008 03666f6f 03626172 # INCMP bar foo - move to node "bar" if input is "FOO" 342 0001 0461696565 01 01 # CATCH aiee 1 1 - move to node "aiee" (and immediately halt) if input match flag (1) is set (1) 343 0003 04616263 020104 # LOAD abc 260 - execute code symbol "abc" with a result size limit of 260 (2 byte BE integer, 0x0104) 344 0003 04646566 00 # LOAD def 0 - execute code symbol "abc" with no size limit (sink) 345 0005 04616263 # MAP abc - make "abc" available for renderer 346 0007 # HALT - stop execution (require new input to continue) 347 0006 0461313233 # MOVE a123 - move to node "a123" (regardless of input) 348 0007 # HALT - stop execution 349 @end verbatim