# Layer 2: Instructions

This chapter looks at the lowest level of the WIR, which implements a simply assembly-like instruction language for manipulating the stack that determines the graph control flow.

The edges of the graph themselves are defined in the previous chapter, and the chapter before that talks about the toplevel of the WIR representation tying the edges together.

We use the same conventions as in the previous chapters. We recommend you read them before continuing to understand what we wrote down.

## EdgeInstrs

A `Linear`

-edge may be annotated with zero or more `EdgeInstr`

uctions, which are assembly-like instructions that manipulate a workflow-wide stack. Some `Edge`

s manipulate the stack too, most often reading from it, but most work is done by explicitly stating the instructions.

As discussed in the `introduction chapter`

, the instructions essentially implement a second layer of computation. Where the edges mostly related to "on-graph" computation, the edge instructions can be thought of as "off-graph" computation that typically matters less for reasoners.

As far as the specification goes, the the following fields are shared by all instructions:

`kind`

(string): Denotes which variant of the`EdgeInstr`

this object describes. The identifiers used are given below in each subsections.

A convenient index of all instructions:

`Cast`

: Casts a value to another type.`Pop`

: Discards the top value on the stack.`PopMarker`

: Pushes an invisible value that is used by...`DynamicPop`

: Pops values off the stack until the invisible value pushed by`PopMarker`

is popped.`Branch`

: Conditionally jumps to another instruction within the same stream.`BranchNot`

: Conditionally jumps to another instruction within the same stream but negated.`Not`

: Performs logical negation.`Neg`

: Performs arithmetic negation.`And`

: Performs logical conjunction.`Or`

: Performs logical disjunction.`Add`

: Performs arithmetic addition / string concatenation.`Sub`

: Performs arithmetic subtraction.`Mul`

: Performs arithmetic multiplication.`Div`

: Performs arithmetic division.`Mod`

: Performs arithmetic modulo.`Eq`

: Compares two values.`Ne`

: Compares two values but negated.`Lt`

: Compares two numerical values in a less-than fashion.`Le`

: Compares two numerical values in a less-than-or-equal-to fashion.`Gt`

: Compares two numerical values in a greater-than fashion.`Ge`

: Compares two numerical values in a greater-than-or-equal-to fashion.`Array`

: Pushes an array literal onto the stack (or rather, creates one out of existing values).`ArrayIndex`

: Takes an array and an index and pushes the element of the array at that index.`Instance`

: Pushes a new instance of a class onto the stack by popping existing values.`Proj`

: Projects a field on an instance to get that field's value.`VarDec`

: Declares the existance of a variable.`VarUndec`

: Releases the resources of a variable.`VarGet`

: Gets the value of a variable.`VarSet`

: Sets the value of a variable.`Boolean`

: Pushes a boolean constant.`Integer`

: Pushes a integer constant.`Real`

: Pushes a floating-point constant.`String`

: Pushes a string constant.`Function`

: Pushes a function handle on top of the stack.

### Cast

*Identifier: "cst"*

The `Cast`

takes the top value off the stack and attempts to perform a type conversion on it. Then, the result is pushed back on top of the stack.

Specification-wise, the `Cast`

needs one additional field:

`t`

(`DataType`

): Defines the datatype the top value on the stack to.

Stack-wise, the `Cast`

manipulates the stack in the following way:

*pops*`DataType::Any`

from the stack; and*pushes*An object of type`t`

on top of the stack representing the same value but as another type.

Note that this conversion may fail, since not all types can be converted into other types. Specifically, the following convertions are defined, where `T`

and `U`

represent arbitrary types and `Ts`

and `Us`

represents lists of arbitrary, possibly heterogenously typed values:

`bool`

to`int`

creates a 1 if the input value is true or a 0 otherwise.`bool`

to`str`

creates a "true" if the input value is true, or a "false" if the input value is false.`int`

to`bool`

creates true if the input value is non-zero, or false otherwise.`int`

to`real`

creates an equivalent floating-point number.`int`

to`str`

writes the integer as a serialized number.`real`

to`int`

writes the floating-point value rounded down to the nearest integer.`real`

to`str`

writes the floating-point number as a serialized number.`arr<T>`

to`str`

casts the elements in the array from`T`

to`str`

and then serializes them within square brackets (`[]`

), separated by comma's (`,`

) (e.g.,`"[ 42, 43, 44 ]"`

).`arr<T>`

to`arr<U>`

casts every element in the array from`T`

to`U`

.`func(Ts) -> T`

to`func(Us) -> U`

is a no-op but*only*if`Ts == Us`

and`T == U`

.`func(Ts) -> T`

to`str`

casts the values in`Ts`

to`str`

and`T`

to`str`

and then writes the name of the function followed by the arguments in parenthesis (`()`

) followed by`->`

and the return type (e.g.,`"foo(int, real) -> str"`

). If the function is a class method, then the class name and`::`

are prefixed to the function name (e.g.,`Foo::foo(Foo, int, real) -> str`

).`func(Ts) -> T`

to`clss`

is a no-op, even keeping the same type, but*only*if the function is a method and belongs to the class it is casted to. This can be used to assert that a method is part of a class(?).`clss<Ts>`

to`str`

casts the values in`Ts`

to`str`

and then serializes it as the class name followed by the values in the class serialized as name`:=`

value separated by comma's in between curly brackets (`{}`

) (e.g.,`Foo { foo := 42, bar := "test" }`

).`Data`

to`str`

writes`Data`

, and then the name of the data in triangular brackets (`<>`

) (e.g.,`"Data<Foo>"`

).`Data`

to`IntermediateResult`

is a no-op.`IntermediateResult`

to`str`

writes`IntermediateResult`

, and then the name of the data in triangular brackets (`<>`

) (e.g.,`"IntermediateResult<Foo>"`

).`T`

to`T`

performs a no-op.`T`

to`Any`

performs a no-op and just changes the type.

Any convertion not mentioned here is defined to be illegal.

As such, the `Cast`

can throw the following errors:

`Empty stack`

when popping;`Stack overflow`

when pushing; or`Illegal cast`

when the value cannot be casted to type`t`

.

The following is an example `Cast`

-instruction:

```
{
"kind": "cst",
"t": "str"
}
```

### Pop

*Identifier: "pop"*

Pops the top value off the stack and discards it.

This instruction does not require any additional fields.

Stack-wise, the `Pop`

does the following:

*pops*`DataType::Any`

from the stack.

It may throw the following error:

`Empty stack`

when popping.

An example:

```
{
"kind": "pop"
}
```

### PopMarker

*Identifier: "mpp"*

Pushes a so-called *pop marker* onto the stack, which can then be popped using the `DynamicPop`

-instruction. This combination can be used when popping an unknown number of values off the stack.

This instruction does not require any additional fields.

Stack-wise, the `PopMarker`

does the following:

*pushes*a special value onto the stack that is invisible to most operations except`DynamicPop`

.

It may throw the following error:

`Stack overflow`

when pushing.

An example:

```
{
"kind": "mpp"
}
```

### DynamicPop

*Identifier: "dpp"*

*Dynamically pops* the stack until a `PopMarker`

is popped. This combination can be used when popping an unknown number of values off the stack.

This instruction does not require any additional fields.

Stack-wise, the `PopMarker`

does the following:

*pops*a dynamic amount of values off the stack until`PopMarker`

's special value is popped.

Doing so may make it throw the following error:

`Empty stack`

when popping.

An example:

```
{
"kind": "dpp"
}
```

### Branch

*Identifier: "brc"*

Not to be confused with the `Branch`

-edge, this instruction implements a branch in the instruction stream only. This is only allowed when it's possible to do this within the same linear edge, implying the branch does not influence directly which nodes are executed.

The branch is taken when the top value on the stack is true; otherwise, it is ignored and implements a no-op.

The `Branch`

defines the following fields:

`n`

(number): The relative offset to jump to. This can be thought of as "number of instructions to skip", where a value of -1 points to the previous instruction, 0 points to the`Branch`

-instruction itself and 1 points to the next instruction.

Stack-wise, the `Branch`

does the following:

*pops*`DataType::Boolean`

from the stack.

As such, it can throw the following errors:

`Empty stack`

when popping; or`Type error`

when the popped value is not a`bool`

.

Note that skipping outside of the sequence of instructions belonging to a `Linear`

-edge means the VM simply stops executing.

An example `Branch`

-instruction:

```
{
"kind": "brc",
"n": 5
}
```

### BranchNot

*Identifier: "brn"*

Counterpart to the `Branch`

-instruction that behaves the same except that it branches when the value on the stack is false instead of true.

The `BranchNot`

defines the following fields:

`n`

(number): The relative offset to jump to. This can be thought of as "number of instructions to skip", where a value of -1 points to the previous instruction, 0 points to the`BranchNot`

-instruction itself and 1 points to the next instruction.

Stack-wise, the `Branch`

does the following:

*pops*`DataType::Boolean`

from the stack.

As such, it can throw the following errors:

`Empty stack`

when popping; or`Type error`

when the popped value is not a`bool`

.

Note that skipping outside of the sequence of instructions belonging to a `Linear`

-edge means the VM simply stops executing.

An example `Branch`

-instruction:

```
{
"kind": "brn",
"n": 5
}
```

### Not

*Identifier: "not"*

Implements a logical negation on the top value on the stack.

The `Not`

does not need additional fields to do this.

Stack-wise, the `Not`

does the following:

*pops*`DataType::Boolean`

from the stack; and*pushes*`DataType::Boolean`

on top of the stack.

As such, it can throw the following errors:

`Empty stack`

when popping;`Type error`

when the popped value is not a`bool`

; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "not"
}
```

### Neg

*Identifier: "neg"*

Implements a arithmetic negation on the top value on the stack.

The `Neg`

does not need additional fields to do this.

Stack-wise, the `Neg`

does the following:

*pops*`DataType::Numeric`

from the stack; and*pushes*`DataType::Numeric`

on top of the stack.

As such, it can throw the following errors:

`Empty stack`

when popping;`Type error`

when the popped value is not an`int`

or`real`

; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "neg"
}
```

### And

*Identifier: "and"*

Performs logical conjunction on the top two values on the stack.

No additional fields are needed to do this.

Stack-wise, the `And`

does the following:

*pops*`DataType::Boolean`

for the righthand-side;*pops*`DataType::Boolean`

for the lefthand-side; and*pushes*`DataType::Boolean`

that is the conjunction of the LHS and RHS.

The following errors can occur during this process:

`Empty stack`

when popping;`Type error`

when the popped values are not a`bool`

; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "and"
}
```

### Or

*Identifier: "or"*

Performs logical disjunction on the top two values on the stack.

No additional fields are needed to do this.

Stack-wise, the `Or`

does the following:

*pops*`DataType::Boolean`

for the righthand-side;*pops*`DataType::Boolean`

for the lefthand-side; and*pushes*`DataType::Boolean`

that is the disjunction of the LHS and RHS.

The following errors can occur during this process:

`Empty stack`

when popping;`Type error`

when the popped values are not a`bool`

; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "or"
}
```

### Add

*Identifier: "add"*

Performs arithmetic addition *or* string concatenation on the top two values on the stack. Which of the two depends on the types of the popped values.

The `Add`

does not introduce additional fields.

Stack-wise, the `Add`

does the following:

*pops*`DataType::Integer`

,`DataType::Real`

or`DataType::String`

for the righthand-side;*pops*`DataType::Integer`

,`DataType::Real`

or`DataType::String`

for the lefthand-side; and*pushes*`DataType::Integer`

,`DataType::Real`

or`DataType::String`

depending on the input types:- If both arguments are
`DataType::Integer`

, then a new integer is pushed that is the arithmetic addition of the LHS and RHS; - If both arguments are
`DataType::Real`

, then a new real is pushed that is the arithmetic addition of both the LHS and RHS; and - If both arguments are
`DataType::String`

, then a new string is pushed that is the concatenation of the LHS and then the RHS.

- If both arguments are

The following errors may occur when processing an `Add`

:

`Empty stack`

when popping;`Type error`

when the popped values do not match any of the three cases above;`Overflow error`

when the addition results in integer/real addition; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "add"
}
```

### Sub

*Identifier: "sub"*

Performs arithmetic subtraction on the top two values on the stack.

The `Sub`

does not introduce additional fields.

Stack-wise, the `Sub`

does the following:

*pops*`DataType::Integer`

or`DataType::Real`

for the righthand-side;*pops*`DataType::Integer`

or`DataType::Real`

for the lefthand-side; and*pushes*`DataType::Integer`

or`DataType::Real`

depending on the input types:- If both arguments are
`DataType::Integer`

, then a new integer is pushed that is the arithmetic subtraction of the LHS and RHS; and - If both arguments are
`DataType::Real`

, then a new real is pushed that is the arithmetic subtraction of both the LHS and RHS.

- If both arguments are

The following errors may occur when processing an `Add`

:

`Empty stack`

when popping;`Type error`

when the popped values do not match any of the two cases above;`Overflow error`

when the subtraction results in integer/real underflow; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "sub"
}
```

### Mul

*Identifier: "mul"*

Performs arithmetic multiplication on the top two values on the stack.

The `Mul`

does not introduce additional fields.

Stack-wise, the `Mul`

does the following:

*pops*`DataType::Integer`

or`DataType::Real`

for the righthand-side;*pops*`DataType::Integer`

or`DataType::Real`

for the lefthand-side; and*pushes*`DataType::Integer`

or`DataType::Real`

depending on the input types:- If both arguments are
`DataType::Integer`

, then a new integer is pushed that is the arithmetic multiplication of the LHS and RHS; and - If both arguments are
`DataType::Real`

, then a new real is pushed that is the arithmetic multiplication of both the LHS and RHS.

- If both arguments are

The following errors may occur when processing an `Add`

:

`Empty stack`

when popping;`Type error`

when the popped values do not match any of the two cases above;`Overflow error`

when the multiplication results in integer/real overflow; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "mul"
}
```

### Div

*Identifier: "div"*

Performs arithmetic division on the top two values on the stack.

The `Div`

does not introduce additional fields.

Stack-wise, the `Div`

does the following:

*pops*`DataType::Integer`

or`DataType::Real`

for the righthand-side;*pops*`DataType::Integer`

or`DataType::Real`

for the lefthand-side; and*pushes*`DataType::Integer`

or`DataType::Real`

depending on the input types:- If both arguments are
`DataType::Integer`

, then a new integer is pushed that is the*integer*division of the LHS and RHS (i.e., rounded down to the nearest integer); and - If both arguments are
`DataType::Real`

, then a new real is pushed that is the*floating-point*division of both the LHS and RHS.

- If both arguments are

The following errors may occur when processing an `Add`

:

`Empty stack`

when popping;`Type error`

when the popped values do not match any of the two cases above;`Overflow error`

when the division results in real underflow; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "div"
}
```

### Mod

*Identifier: "mod"*

Computes the remainder of dividing one value on top of the stack with another.

The `Mod`

does not introduce additional fields to do so.

Stack-wise, the `Mod`

does the following:

*pops*`DataType::Integer`

for the righthand-side;*pops*`DataType::Integer`

for the lefthand-side; and*pushes*`DataType::Integer`

that is the remainder of dividing the LHS by the RHS.

The following errors may occur when processing a `Mod`

:

`Empty stack`

when popping;`Type error`

when the popped values are not`int`

s; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "mod"
}
```

### Eq

*Identifier: "eq"*

Compares the top two values on the stack for equality. This is first type-wise (their types must be equal), and then value-wise.

No additional fields are introduced to do so.

Stack-wise, the `Eq`

does the following:

*pops*`DataType::Any`

for the righthand-side;*pops*`DataType::Any`

for the lefthand-side; and*pushes*`DataType::Boolean`

with true if the LHS equals the RHS, or false otherwise.

The following errors may occur when processing an `Eq`

:

`Empty stack`

when popping; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "eq"
}
```

### Ne

*Identifier: "ne"*

Compares the top two values on the stack for inequality. This is first type-wise (their types must be unequal), and then value-wise.

No additional fields are introduced to do so.

Stack-wise, the `Ne`

does the following:

*pops*`DataType::Any`

for the righthand-side;*pops*`DataType::Any`

for the lefthand-side; and*pushes*`DataType::Boolean`

with true if the LHS does*not*equal the RHS, or false otherwise.

The following errors may occur when processing an `Eq`

:

`Empty stack`

when popping; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "ne"
}
```

### Lt

*Identifier: "lt"*

Compares the top two values on the stack for order, specifically less-than. This can only be done for numerical values.

No additional fields are introduced to do so.

Stack-wise, the `Lt`

does the following:

*pops*`DataType::Numeric`

for the righthand-side;*pops*`DataType::Numeric`

for the lefthand-side; and*pushes*`DataType::Boolean`

with true if the LHS is stricly less than the RHS, or false otherwise.

The following errors may occur when processing an `Lt`

:

`Empty stack`

when popping;`Type error`

when either argument is not a`num`

*or*they are not of the same type (i.e., cannot compare`int`

with`real`

); or`Stack overflow`

when pushing.

Example:

```
{
"kind": "lt"
}
```

### Le

*Identifier: "le"*

Compares the top two values on the stack for order, specifically less-than-or-equal-to. This can only be done for numerical values.

No additional fields are introduced to do so.

Stack-wise, the `Le`

does the following:

*pops*`DataType::Numeric`

for the righthand-side;*pops*`DataType::Numeric`

for the lefthand-side; and*pushes*`DataType::Boolean`

with true if the LHS is less than or equal to the RHS, or false otherwise.

The following errors may occur when processing an `Le`

:

`Empty stack`

when popping;`Type error`

when either argument is not a`num`

*or*they are not of the same type (i.e., cannot compare`int`

with`real`

); or`Stack overflow`

when pushing.

Example:

```
{
"kind": "le"
}
```

### Gt

*Identifier: "gt"*

Compares the top two values on the stack for order, specifically greater-than. This can only be done for numerical values.

No additional fields are introduced to do so.

Stack-wise, the `Gt`

does the following:

*pops*`DataType::Numeric`

for the righthand-side;*pops*`DataType::Numeric`

for the lefthand-side; and*pushes*`DataType::Boolean`

with true if the LHS is strictly greater than to the RHS, or false otherwise.

The following errors may occur when processing an `Gt`

:

`Empty stack`

when popping;`Type error`

when either argument is not a`num`

*or*they are not of the same type (i.e., cannot compare`int`

with`real`

); or`Stack overflow`

when pushing.

Example:

```
{
"kind": "gt"
}
```

### Ge

*Identifier: "ge"*

Compares the top two values on the stack for order, specifically greater-than-or-equal-to. This can only be done for numerical values.

No additional fields are introduced to do so.

Stack-wise, the `Ge`

does the following:

*pops*`DataType::Numeric`

for the righthand-side;*pops*`DataType::Numeric`

for the lefthand-side; and*pushes*`DataType::Boolean`

with true if the LHS is greater than or equal to the RHS, or false otherwise.

The following errors may occur when processing an `Ge`

:

`Empty stack`

when popping;`Type error`

when either argument is not a`num`

*or*they are not of the same type (i.e., cannot compare`int`

with`real`

); or`Stack overflow`

when pushing.

Example:

```
{
"kind": "ge"
}
```

### Array

*Identifier: "arr"*

Consumes a number of values from the top off the stack and creates a new Array with them.

The `Array`

specifies the following additional fields:

`l`

(number): The number of elements to pop, i.e., the length of the array.`t`

(`DataType`

): The data type of the array. Note that this includes the`arr`

datatype and its element type.

Stack-wise, the `Array`

does the following:

*pops*`l`

values of type`t`

; and*pushes*`DataType::Array`

with`l`

elements of type`t`

. Note that the order of elements is reversed (i.e., the first element popped is the last element of the array).

Doing so may trigger the following errors:

`Empty stack`

when popping;`Type error`

when a popped value does not have type`t`

; or`Stack overflow`

when pushing.

Example:

```
// Represents an array literal of length 5, element type `str`
{
"kind": "arr",
"l": 5,
"t": { "kind": "arr", "t": { "kind": "str" } }
}
```

### ArrayIndex

*Identifier: "arx"*

Indexes an array value on top of the stack with some index and outputs the element at that index.

The `ArrayIndex`

specifies the following additional fields:

`t`

(`DataType`

): The data type of the element. This is the type of the pushed value.

Stack-wise, the `ArrayIndex`

does the following:

*pops*`DataType::Integer`

for the index;*pops*`DataType::Array`

for the array to index, which must have element type`t`

; and*pushes*a value of type`t`

that is the element at the specified index.

Doing so may trigger the following errors:

`Empty stack`

when popping;`Type error`

when any of the popped values are incorrectly typed;`Array out-of-bounds`

when the index is too large for the given array, or if it's negative; or`Stack overflow`

when pushing.

Example:

```
/// Indexes an array of string elements
{
"kind": "arx",
"t": { "kind": "str" }
}
```

### Instance

*Identifier: "ins"*

Consumes a number of values on top of the stack in order to construct a new instance of a class.

To do so, the `Instance`

defines additional fields:

`d`

(number): Identifier of the type which we are constructing. This definition is given in the parent symbol table.

Stack-wise, the `Instance`

does the following:

*pops*`N`

values of varying types from the top off the stack, where`N`

is the number of fields (which may be 0) and the types are those matching to the fields. Note that they are popped in*reverse alphabetical order*, e.g., field`a`

is below field`z`

on the stack; and*pushes*a value of the type referred to by`d`

.

Doing so may trigger the following errors:

`Unknown definition`

if`d`

is not known in the symbol tables;`Empty stack`

when popping;`Type error`

when any of the popped values are incorrectly typed; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "ins",
"d": 42
}
```

### Proj

*Identifier: "prj"*

The `Proj`

instruction takes an instance and retrieves the value of the given field from it.

The field is embedded in the instruction. As such, it adds the following specification fields:

`f`

(string): The name of the field to project.

Stack-wise, the `Proj`

does the following:

*pops*an instance value; and*pushes*the value of field`f`

in that instance.

The projection ducktypes the instance, and as such can trigger the following errors:

`Empty stack`

when popping;`Unknown field`

when the popped instance has no field by the name described in`f`

; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "prj",
"f": "foo"
}
```

### VarDec

*Identifier: "vrd"*

Declares a new variable, giving an opportunity to the underlying execution context to reserve space for it.

The following fields are added by a `VarDec`

:

`d`

(number): The identifier of the variable definition in the parent symbol table of the variable we're declaring. Note that, because definitions are scoped to functions, this is a unique identifier for specific variable instances the current scope.

Stack-wise, the `VarDec`

doesn't do anything, as it fully acts on the underlying variable system.

The following errors may occur when working with the `VarDec`

:

`Unknown definition`

if`d`

is not known in the symbol tables; or`Variable error`

if the underlying context finds another reason to crash.

Example:

```
{
"kind": "vrd",
"d": 42
}
```

### VarUndec

*Identifier: "vru"*

Explicitly undeclares a new variable, giving an opportunity to the underlying execution context to claim back space for it.

The following fields are added by a `VarUndec`

:

`d`

(number): The identifier of the variable definition in the parent symbol table of the variable we're undeclaring.Note that, because definitions are scoped to functions, this is a unique identifier for specific variable instances the current scope.

Stack-wise, the `VarUndec`

doesn't do anything, as it fully acts on the underlying variable system.

The following errors may occur when working with the `VarUndec`

:

`Unknown definition`

if`d`

is not known in the symbol tables; or`Variable error`

if the underlying context finds another reason to crash.

Example:

```
{
"kind": "vru",
"d": 42
}
```

### VarGet

*Identifier: "vrg"*

Retrieves the value of a variable which was previously `VarSet`

.

The following fields are added by a `VarGet`

:

`d`

(number): The identifier of the variable definition in the parent symbol table of the variable we're undeclaring.Note that, because definitions are scoped to functions, this is a unique identifier for specific variable instances the current scope.

Stack-wise, the `VarGet`

does the following:

*pushes*the value stored in the variable on top of the stack.

The following errors may occur when working with the `VarGet`

:

`Unknown definition`

if`d`

is not known in the symbol tables;`Variable error`

if the underlying context finds another reason to crash; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "vrs",
"d": 42
}
```

### VarSet

*Identifier: "vrs"*

Moves the value on top of the stack to a variable so it may be `VarGet`

.

The following fields are added by a `VarSet`

:

`d`

(number): The identifier of the variable definition in the parent symbol table of the variable we're undeclaring.Note that, because definitions are scoped to functions, this is a unique identifier for specific variable instances the current scope.

Stack-wise the `VarSet`

does the following:

*pops*`DataType::Any`

from the stack to put in the variable.

The following errors may occur when working with the `VarGet`

:

`Unknown definition`

if`d`

is not known in the symbol tables;`Variable error`

if the underlying context finds another reason to crash;`Empty stack`

when popping; or`Type error`

when the type of the popped value does not match the type of the variable.

Example:

```
{
"kind": "vrs",
"d": 42
}
```

### Boolean

*Identifier: "bol"*

Pushes a boolean constant on top of the stack.

The following fields are added by a `Boolean`

:

`v`

(bool): The boolean value to push.

Stack-wise, the `Boolean`

does the following:

*pushes*`DataType::Boolean`

on top of the stack.

The following errors may occur when executing a `Boolean`

:

`Stack overflow`

when pushing.

Example:

```
{
"kind": "bol",
"v": true
}
```

### Integer

*Identifier: "int"*

Pushes an integer constant on top of the stack.

The following fields are added by a `Integer`

:

`v`

(number): The integer value to push.

Stack-wise, the `Integer`

does the following:

*pushes*`DataType::Integer`

on top of the stack.

The following errors may occur when executing a `Integer`

:

`Stack overflow`

when pushing.

Example:

```
{
"kind": "int",
"v": -84
}
```

### Real

*Identifier: "rel"*

Pushes a floating-point constant on top of the stack.

The following fields are added by a `Real`

:

`v`

(number): The floating-point value to push.

Stack-wise, the `Real`

does the following:

*pushes*`DataType::Real`

on top of the stack.

The following errors may occur when executing a `Real`

:

`Stack overflow`

when pushing.

Example:

```
{
"kind": "rel",
"v": 0.42
}
```

### String

*Identifier: "str"*

Pushes a string constant on top of the stack.

The following fields are added by a `String`

:

`v`

(string): The string value to push.

Stack-wise, the `String`

does the following:

*pushes*`DataType::String`

on top of the stack.

The following errors may occur when executing a `String`

:

`Stack overflow`

when pushing.

Example:

```
{
"kind": "str",
"v": "Hello, world!"
}
```

### Function

*Identifier: "fnc"*

Pushes a function handle on top of the stack so it may be called.

The following fields are added by a `Function`

:

`d`

(number): The ID of the function definition which to push on top of the stack.

Stack-wise, the `Function`

does the following:

*pushes*`DataType::Function`

on top of the stack.

The following errors may occur when executing a `Function`

:

`Unknown definition`

if`d`

is not known in the symbol tables; or`Stack overflow`

when pushing.

Example:

```
{
"kind": "fnc",
"v": 42
}
```

## Next

This chapter concludes the specification of the WIR.

If you are intending to discover the WIR bottom-up, continue to the previous chapter to see how the graph overlaying the instructions is represented.

Otherwise, you can learn other aspects of the framework or the specification by selecting a different topic in the sidebar on the left.