Files
fz-lang/docs/en/internals/stacks.md

2.6 KiB

Virtual machine stacks

While running, the FZLang virtual machine relies on three stacks: the data stack, the environment stack, and the call stack. Each stack is initialized as an array of 256 values and then zeroed. All three share the same basic property: the most recently added value is at the top and is the first one removed, decrementing the stack-top pointer.

Data stack

The data stack is an array of 256 fzl_value_t elements. The fzl_value_t structure is a container for values used while a program runs.

typedef struct
{
  fzl_type_t type;

  union
  {
    void *obj;

    struct
    {
      const char *value;
      size_t length;
    } s;

    char c;
    bool b;

    int8_t i8;
    uint8_t u8;

    int16_t i16;
    uint16_t u16;

    int32_t i32;
    uint32_t u32;

    int64_t i64;
    uint64_t u64;

    intptr_t i;
    uintptr_t u;
  } as;
} fzl_value_t;

The VM uses this stack to store and retrieve intermediate values while the program runs. The stack should be empty when execution finishes.

Environment stack

The environment stack tracks the lexical environment in which evaluation and instructions are currently taking place. It is an array of 256 fzl_vm_env_t elements.

typedef struct
{
  size_t size;
  size_t remainder;

  fzl_vm_env_entry_t *table;

  fzl_vm_object_t **owned_objects;
  size_t owned_objects_remainder;
  size_t owned_objects_count;
} fzl_vm_env_t;

Each stack element stores a table of local names (fzl_vm_env_entry_t) and the objects owned by the current environment. A local name is a container that wraps an fzl_value_t and gives it a name:

typedef struct
{
  char *name;
  fzl_value_t value;
} fzl_vm_env_entry_t;

An object is an array of its fields. The number of fields is set when the object is allocated by the FZL_OPCODE_ALLOC_OBJECT N opcode (see the opcode reference for details).

typedef struct
{
  fzl_value_t *values;
  size_t values_count;
} fzl_vm_object_t;

An environment is created or removed by the FZL_OPCODE_MK_ENVTABLE and FZL_OPCODE_RM_ENVTABLE opcodes, respectively. If the compiler sees a value of type FZL_TYPE_OBJECT being returned from a function, it inserts FZL_OPCODE_ESCAPE_OBJECT before FZL_OPCODE_RET and FZL_OPCODE_RM_ENVTABLE. This moves ownership of the object to the parent environment.

Call stack

The call stack stores instruction addresses when entering a function through the FZL_OPCODE_CALL opcode. FZL_OPCODE_RET pops an address from the call stack and jumps to it.

The stack itself is an array of 256 size_t elements.