Retro 12 is built around the Nga virtual machine. At the lowest level Nga implements a lightweight MISC-based processor. Above this is an interface layer adding I/O and a means of interacting with the VM. Nga machine code is stored in an image file. This is a raw memory dump. The interface layer is aware of how to find certain items within memory and make use of them. A few addresses in the image file are considered to be special. The image will start with a few stub instructions, then a pointer to the most recent dictionary entry and a pointer to the next free address in memory. Input is processed on a token by token basis. A token is read into an input buffer. A pointer to this buffer is put on the stack, then the `interpret` function is called. Note: The `interpet` function is looked up for each token processed. This has a performance hit, but makes it possible to replace it. The `interpret` function does the following: - checks to see if the first character has a matching prefix handler - yes: pass the rest of the string to the prefix handler for processing - no: lookup in the dictionary - found: pass xt of word to the corresponding class handler - not found: call `err:notfound` If a token is a single character, `interpret` will skip the prefix check. Prefixes are single character modifiers at the start of a token that tell Retro how to treat the token. They are defined as normal words. E.g., :prefix:( drop ; immediate This defines a new prefix, `(`, which ignores the rest of the token. Prefixes are used heavily. Major prefixes provided by Retro are: : create a new word # parse as number $ parse as ASCII character & parse as pointer to named item ( treat as a comment ` compile as a raw Nga bytecode ' parse as a string Word classes tell Retro how to treat functions. These are functions which take a pointer and do something with it based on the Retro system state. Typically they will handle `Compiler` state based actions. An example: :class:immediate (a-) call ; This defines a class handler that always calls a function using it. Pointers to the appropriate word class are stored in the dictionary and used by `interpret` as part of the normal execution flow. You can create new classes at any time to extend the runtime and compile time behavior of Retro. The dictionary is a linked list. A variable, `Dictionary`, points to the most recent entry. It contains: 0 A pointer to the prior entry d:link 1 A pointer to the start of the function d:xt 2 A pointer to the class handler for the function d:class 3 A string with the name of the function d:name Note: The last column above contains the accessor words for each field. Use these, not the raw offsets, as the exact structure may change over time. Quotes are another big design element. These are anonoymous, nestable blocks of code that can be passed between functions. Example: #10 #2 [ #33 * ] dip Words operating on quotes are called combinators. Retro uses these for a variety of stack and logic flow control. Some examples: #33 eq? [ 'true ] if #33 eq? [ 'true ] [ 'fase ] choose #0 #10 [ dup n:inc ] times #1 #2 [ #2 * ] sip Naming of functions follows a few general rules. Names should be prefixed by a namespace string. The following are the initial namespaces: ASCII: constants for special ASCII characters buffer: linear buffers c: characters class: word class handlers compile: compiler words d: dictionary err: error handlers n: numbers prefix: prefix handlers s: strings v: variables Constants should be named in UPPERCASE. Variables should use Title-Case. Regular words should be lowercase. Compund names should be separated by a single dash. E.g., dup-pair Readability counts. Although almost any symbol can be used, it's best to have readable names. A ? suffix implies returning a boolean flag. A - prefix implies the inverse of a word without. E.g., n:zero? n:-zero?