vancode/interpreter/chunk

This module defines the Chunk type, which represents a chunk of bytecode. It also defines the Script type, which represents a script being executed by the interpreter.

This module provides utilities for emitting bytecode into a chunk, as well as for managing line information for error reporting

Types

Chunk {.acyclic.} = ref object
  file*: string
  code*: seq[uint8]          ## the raw bytecode
  ln*: int = 1               ## the current line number, used when emitting ## bytecode
  col*: int                  ## the current column number, used when emitting bytecode
  strings*: seq[string] = [] ## seq of strings used in this chunk (for string literals, global names, etc.)
A chunk of bytecode
LineInfo = tuple[file: string, ln, col: int, runLength: int]
Line information.
Opcode {..} = enum
  opcNoop = "noop", opcPushTrue = "pushTrue", opcPushFalse = "pushFalse",
  opcPushNil = "pushNil", opcPushJNil = "pushJNil", ## push a JSON nil
  opcPushI = "pushI",       ## push int
  opcPushF = "pushF",       ## push float
  opcPushS = "pushS",       ## push string
  opcPushG = "pushG",       ## push global
  opcPopG = "popG",         ## pop global
  opcPushL = "pushL",       ## push local
  opcPopL = "popL",         ## pop local
  opcFFIGetProc = "ffiGetProc", ## get a symbol from a dynamic library
  opcPushPointer = "pushPointer", opcPopPointer = "popPointer", opcConstrObj = "constrObj", ## construct object
  opcGetF = "getF",         ## push field
  opcSetF = "setF",         ## pop field
  opcConstrArray = "constrArray", ## construct array
  opcGetI = "getI",         ## get array item
  opcSetI = "setI",         ## set array item
  opcDiscard = "discard",   ## discard values
  opcGetJ = "getJ",         ## get JSON value
  opcSetJ = "setJ",         ## set JSON value
  opcNegI = "negI",         ## negate int
  opcAddI = "addI",         ## add ints
  opcSubI = "subI",         ## subtract ints
  opcMultI = "multI",       ## multiply ints
  opcDivI = "divI",         ## divide ints
  opcNegF = "negF",         ## negate float
  opcAddF = "addF",         ## add floats
  opcSubF = "subF",         ## subtract floats
  opcMultF = "multF",       ## multiply floats
  opcDivF = "divF",         ## divide floats
  opcInvB = "invB",         ## invert bool
  opcEqB = "eqB",           ## equal bools
  opcEqI = "eqI",           ## equal ints
  opcLessI = "lessI",       ## int less than
  opcGreaterI = "greaterI", ## int greater than
  opcEqF = "eqF",           ## equal floats
  opcLessF = "lessF",       ## float less than
  opcGreaterF = "greaterF", ## float greater than
  opcJumpFwd = "jumpFwd",   ## jump forward
  opcJumpFwdT = "jumpFwdT", ## jump forward if true
  opcJumpFwdF = "jumpFwdF", ## jump forward if false
  opcJumpBack = "jumpBack", ## jump backward
  opcCallD = "callD",       ## call direct
  opcCallI = "callI",       ## call indirect
  opcReturnVal = "returnVal", ## return value from proc
  opcReturnVoid = "returnVoid", ## return void from proc
  opcImportModule = "importModule", ## import a module
  opcImportModuleAlias = "importModuleAlias", ## import a module with an alias
  opcImportFromModule = "importFromModule", ## import a symbol from a module
  opcHalt = "halt"           ## halt the VM
An opcode, used for execution.
Proc {.acyclic.} = ref object
  name*: string
  case kind*: ProcKind
  of pkNative:
    chunk*: Chunk            ## the chunk of bytecode of this procedure
  of pkForeign:
    foreign*: ForeignProc    ## the foreign implementation of this procedure
  paramCount*: int           ## the number of parameters this procedure takes
  hasResult*: bool           ## flag signifying whether the proc returns a value
A runtime procedure.
ProcKind = enum
  pkNative,                 ## a native (bytecode) proc
  pkForeign                  ## a foreign (Nim) proc defined at compile-time
The kind of a procedure. This is used to determine how the procedure should be called.
Script {.acyclic.} = ref object
  stdpos*: int
  libs*: Table[string, LibHandle] ## a table of dynamic libraries loaded by this script
  procs*: seq[Proc]          ## all procs declared in this script
  procsExport*: seq[Proc]    ## the public procs declared in this script
                             ## these are the procs that can be called from
                             ## other scripts
  mainChunk*: Chunk          ## the main chunk of this script
  scripts*: Table[string, Script] ## a table of scripts, basically reprresenting the
                                  ## modules the script imports
  jsOutput*: string
  typeCount*: int            ## the number of types in this script.
                             ## used for compilation

Procs

proc `$`(c: Chunk): string {....raises: [ValueError], tags: [], forbids: [].}
Convert a Chunk to a string (for debugging).
proc `$`(s: Script): string {....raises: [ValueError], tags: [], forbids: [].}
Convert a Script to a string (for debugging).
proc `==`(a, b: Chunk): bool {....raises: [], tags: [], forbids: [].}
Compares two Chunks by address
proc addLineInfo(chunk: var Chunk; n: int) {....raises: [], tags: [], forbids: [].}
Add n line info entries to the chunk.
proc emit(chunk: var Chunk; opc: Opcode) {....raises: [], tags: [], forbids: [].}
Emit an opcode. This ignores noop opcodes.
proc emit(chunk: var Chunk; u8: uint8) {....raises: [], tags: [], forbids: [].}
Emit a uint8.
proc emit(chunk: var Chunk; u16: uint16) {....raises: [], tags: [], forbids: [].}
Emit a uint16.
proc emit(chunk: var Chunk; val: float64) {....raises: [], tags: [], forbids: [].}
Emit a float.
proc emit(chunk: var Chunk; val: int64) {....raises: [], tags: [], forbids: [].}
Emit an int.
proc emit(chunk: var Chunk; xptr: pointer) {....raises: [], tags: [], forbids: [].}
Emit a pointer.
proc emitHole(chunk: var Chunk; size: int): int {....raises: [], tags: [],
    forbids: [].}
Emit a hole, to be filled later by fillHole.
proc fillHole(chunk: var Chunk; hole: int; val: uint8) {....raises: [], tags: [],
    forbids: [].}
Fill a hole with an 8-bit value.
proc fillHole(chunk: var Chunk; hole: int; val: uint16) {....raises: [], tags: [],
    forbids: [].}
Fill a hole with a 16-bit value.
proc getFloat(chunk: Chunk; i: int): float64 {....raises: [], tags: [], forbids: [].}
Get a constant float at position i.
proc getInt(chunk: Chunk; i: int): int64 {....raises: [], tags: [], forbids: [].}
Get a constant int at position i.
proc getLineInfo(chunk: Chunk; i: int): LineInfo {....raises: [], tags: [],
    forbids: [].}
Get the line info at position i. Warning: This is very slow, because it has to walk the entire chunk decoding the run length encoded line info!
proc getLineInfoTable(chunk: Chunk): seq[LineInfo] {....raises: [], tags: [],
    forbids: [].}
Get the line info table for this chunk, expanding the run-length encoding.
proc getOpcode(chunk: Chunk; i: int): Opcode {....raises: [], tags: [], forbids: [].}
Get the opcode at position i.
proc getString(chunk: var Chunk; str: sink string): uint16 {....raises: [KeyError],
    tags: [], forbids: [].}
O(1) string interning with a hash index; returns existing id or inserts. Guards against uint16 overflow and avoids extra string copy via move.
proc getU8(chunk: Chunk; i: int): uint8 {....raises: [], tags: [], forbids: [].}
Gets the uint8 at position i.
proc getU16(chunk: Chunk; i: int): uint16 {....raises: [], tags: [], forbids: [].}
Get the uint16 at position i.
proc hash(x: Chunk): Hash {....raises: [], tags: [], forbids: [].}
Hashes a Chunk by its address
proc hash(x: Script): Hash {....raises: [], tags: [], forbids: [].}
Hashes a Script by its address
proc newChunk(file: string): Chunk {....raises: [], tags: [], forbids: [].}
Create a new chunk.
proc newScript(main: Chunk): Script {....raises: [], tags: [], forbids: [].}
Create a new script, with the given main chunk.
proc patchHole(chunk: var Chunk; hole: int) {....raises: [], tags: [], forbids: [].}
Fill a 16-bit hole with the current chunk's length + 1.
proc rebuildStringIds(chunk: var Chunk) {....raises: [], tags: [], forbids: [].}
Rebuild the string ID table from the strings sequence. This is necessary after deserializing a chunk, because the string IDs are not stored in the serialized form of the chunk.
proc setLineInfoTable(chunk: var Chunk; info: seq[LineInfo]) {....raises: [],
    tags: [], forbids: [].}
Set the line info table for this chunk, compressing it with run-length encoding.