What is a Virtual Machine?
A virtual machine is an emulation of a computer system. This basically means that we're creating a facsimile of physical hardware operations in software. DM's language interpreter is fundamentally a Virtual Machine.
Basic Interpreter Structure
A simplified interpreter will have the following properties:
- block_ptr: A pointer to a memory region that contains a series of instructions.
- offset: The interpreter's current offset, or which instruction in the block_ptr is currently being read.
- memory_ptr: A block of memory allocated for the interpreter's current longterm storage state.
- stack_ptr A block of memory representing the interpreter's current shortterm storage state.
- stack_len A value indicating how large the stack currently was at the depth increase.
- stored_jump A value indicating a pending offset to jump to.
A few points of interest here, the block pointer references a linear sequence of commands. Commands are operations. Operations may have operands. Operands are like values that an operation uses to mutate data.
The memory pointer stores pointers to values sequentially. The compiler may be given the names of the memory locations, but the interpreter does not need them. Instead, they are boiled down to numeric offsets starting at 0.
The stack pointer stores pointers to values sequentially. Stack structure can be generalized as:
<return_value> <break_offset> <this> <local memory> <storage>
Standard Operations
key:
<label> - remove value from stack and use it
<(label)> - use value from stack without removing it
[label] - peek into memory
(+X) - the next instruction from block_ptr following this one
X:Y - Y of X.
Stack Management:
- RTN return: pop the last value from stack, set <return_value>,
and decrease stack depth. - PEK peek: add the last value in the stack to the stack again
- POP pop: remove the last value in the stack
- PSH (+1) push: add a constant value to the stack using the next instruction in the code block as the operand
- IVK invoke: (operands: POP POP POP) increase stack depth, PUSH null (return_value), PUSH offset+1 (break_point), PUSH operand1 (this), PUSH operand2 (local memory), set offset to operand3
Memory region 0 = global
Memory region 1 = local
General Memory Management:
- ACC (+1) (+2) access: PUSH memory region operand1's:operand2.
- ASG (+1) (+2) assign: Set memory region operand1:operand2 to PEEK.
Local memory is flexible and can expand and shrink as needed.
Local Memory Management:
- DCL (+1)declare: Add a number of values to local memory
- UDC (-1) undeclare: Remove a number of values from local memory
Valuation:
- GET (+1)get: Get property POP:operand2
- SET (+1) set: Set property operand1:POP to PEEK.
- IDX index: PUSH POP[POP]
Abstract patterns like goto, if/else, while, for, switch, break, and continue are all compilation patterns that utilize jumping. Jumping moves the current offset by a positive or negative number.
Jumps:
- JMP (+1) jump: add operand1 to offset.
- CND (+1)conditional jump: POP operand2. If operand2 false, add operand 1 to offset.
- CSJ (+1) set jump point: set interpreter's stored jump point to offset + operand1
- CSE (+1) case equality jump: PEEK operand2. If operand2 is equal to operand 1, POP, then go to jump point stored by last CSJ
- CSR (+1) (+1) case range jump: PEEK operand3. If operand3 is greater or equal to operand 1,
and less or equal to operand 2, POP, then go to jump point stored by last CSJ.
All single-operand operators POP, perform their operation, then PUSH.
Single Operand:
- OP_BNT operator binary not: ~operand1
- OP_LNT operator logical not: !operand1
- OP_NEG operator negation: -operand1
- OP_INC operator increment: operand1+1
- OP_DEC operator decrement: operand1+-1
All dual-operand operators POP, POP, then perform their operation, then PUSH
Dual Operand:
- OP_EXP operator exponent: operand2 ** operand1
- OP_MUL operator multiply: operand2 * operand1
- OP_DIV operator divide: operand2 / operand1
- OP_MOD operator modulo: operand2 % operand1
- OP_ADD operator add: operand2 + operand1
- OP_SUB operator subtract: operand2 - operand1
- OP_EQ operator equality: operand2 == operand1
- OP_NEQ operator inequality: operand2 != operand1
- OP_LT operator less than: operand2 < operand1
- OP_GT operator greater than: operand2 > operand1
- OP_LEQ operator less or equal: operand2 <= operand1
- OP_GEQ operator greater or equal: operand2 >= operand1
- OP_RSH operator right shift: operand2 >> operand1
- OP_LSH operator left shift: operand2 << operand1
- OP_BA operator binary and: operand2&operand1
- OP_BO operator binary or: operand2|operand1
- OP_BX operator binary xor: operand2^operand1
- OP_LA operator logical and: operand2&&operand1
- OP_LO operator logical or: operand2||operand1
Abstract Pattern Examples
key:
[SOMETHING] - Whatever bytecode has compiled from SOMETHING region in the code snippet.
<position> - a reference to a jump point. Compiles down as whatever offset is required to jump the instruction offset to where the matching <position> is in the pattern example.</<
if patterns: