2.4. Evaluation

This chapter specifies the operational semantics of abstract instructions within an abstract stack machine, to indirectly give semantics of a Kink program. Abstract instructions are transformed from a Kink program through desugaring and translation steps.

abstract stack machine

An abstract machine which evaluates abstract instructions.

abstract instruction

An instruction which is evaluated by abstract stack machine.

Multiple abstract stack machines can exist in a single runtime, and they can evaluate abstract instructions in parallel. They share the same set of vals, and other resources within the runtime.

The abstract stack machine and abstract instructions are defined just to show how Kink programs should behave. Thus, an implementation of runtime need not implement the abstract stack machine literally, however, the observable behavior must obey the definition.

2.4.1. Conventions

2.4.1.1. Abstract instruction

An abstract instruction is parenthesized like the following:

(varref "op_store")

Here, varref is the opcode of the instruction, and "op_store" is the operand of the instruction.

Every instruction has a location as an implicit operand.

2.4.1.2. Data types

The following data types are used as operands of abstract instructions:

  • val : a Kink val.

  • str : an immutable, finite length sequence of unicode codepoints, which can be represented by a str val.

  • num : a combination of (Mantissa, Scale), which can be represented by a num val.

  • location : a combination of (Program_name, Program_text, Pos), which can be represented by a location val.

  • a sequence of instructions.

2.4.1.3. Sequence of instructions

A sequence of instructions is represented by just placing instructions in order like the following:

(emptyvec) (binding) (load "X") (add)

A sequence of no instruction is represented as follows:

2.4.1.4. Stack

An abstract stack machine operates on its stack.

A stack consists of a sequence of instructions, and the sequence of instructions is updated every time a reduction rule is applied.

A stack is represented like the following:

^ (str "Foo") (dup) (remove) $

Here, ^ is the mark of the beginning of the stack, and $ is the mark of the end of the stack.

2.4.1.5. Reduction rules

A reduction rule is written like the following:

(val V) (dup) → (val V) (val V)

It means that if there is a sub-sequence of instructions (val V) (dup) in the stack, the reduction rule is applied, and the sub-sequence is updated to (val V) (val V). Note that V is a metavariable, and it represents a Kink val.

Additional conditions of a reduction rule can be given using comment lines which start with #.

If there are multiple reduction rules which can be applied for a stack, the leftmost rule is applied.

2.4.1.6. Metavariable

In this chapter, metavariables of the following formats are used to represent an abstract instruction, a sequence of abstruct instructions, and other data.

_snake_case_with_underscore_as_prefix_and_suffix_

A sequence of instructions.

Capitalized_snake_case

An abstract instructions, a val, a str, a num, or a location.

2.4.2. Initial state

Evaluation of an abstract stack machine starts invoking a fun with no args. When evaluation is started with a fun val Fun, the initial state of the stack is:

^ (val Fun) (nada) (emptyvec) (call "..root..") $

2.4.3. Terminal state

When the stack is in the following states, no more reduction rule can be applied. These states are terminal states.

^ (val V) $

This terminal state means that the abstract stack machine terminates returining a val.

^ (uncaught Exc) $

This terminal state means the abstract stack machine terminates with an uncaught exception.

2.4.4. Abstract instructions

2.4.4.1. val instruction

val instruction places an arbitrary type of val on the stack.

syntax

(val V)

There is no reduction rule for val instruction.

A single val instruction can be a terminal state of the abstract stack machine.

2.4.4.2. uncaught instruction

uncaught instruction means an exception which is not caught within the abstract stack machine.

syntax

(uncaught Exc)

Exc is an exception val.

reduction rule

(uncaught Exc) Any → (uncaught Exc)
  • Any is any instruction

This rule means that the uncaught exception is propagated until the terminal state.

2.4.4.3. num instruction

num instruction produces a num val.

syntax

(num Num)

Num parameter is a num.

reduction rule

(num Num)

→ (val Num_val)
# where `Num_val` is a num val converted from `Num`.

2.4.4.4. str instruction

str instruction produces a str val.

syntax

(str Str)

Str parameter is a str.

reduction rule

(str Str)

→ (val Str_val)
# where `Str_val` is a str val converted from `Str`

2.4.4.5. nada instruction

nada instruction produces nada val.

syntax

(nada)

reduction rule

(nada)

→ (val Nada_val)
# where `Nada_val` is `nada` val

2.4.4.6. fun instruction

fun instruction produces a function val.

syntax

(fun _insns_)

_insns_ is a sequence of instructions.

reduction rule

(val Enclosing_binding) (fun _insns_)
# where
# • `Enclosing_binding` is a binding val.

→ (val Fun)
# where
# • `Fun` is a fun val with instructions copied from `_insns_`,
#   replacing every occurrence of (enclosingbinding) by (val Enclosing_binding).
function

A value which holds an immutable sequence of abstract instructions. Function can be abbreviated to fun. The companion module of functions is kink/FUN.

2.4.4.7. emptyvec instruction

emptyvec instruction produces a newly created vec val with no element.

syntax

(emptyvec)

reduction rule

(emptyvec)

→ (val Empty_vec)
# where `Empty_vec` is a newly created vec val with no element

2.4.4.8. add instruction

add instruction produces a newly created vec val adding an element to the back of an existing vec.

syntax

(add)

reduction rule

(val Vec) (val Elem) (add)
# where `Vec` is a vec val

→ (val New_vec)
# where `New_vec` is a newly created vec val containing the elements of `Vec`
# at the front, and `Elem` at the back.

2.4.4.9. concat instruction

concat instruction sets up doconcat instruction.

syntax

(concat)

reduction rules

(concat)

→ (doconcat) (trace "" Location)
# where
# • `Location` is the location of the implicit location of the `concat` instruction.

concat and doconcat are separated, so that the trace of the location of spreading can be added to the stack overflow exception.

2.4.4.10. doconcat instruction

doconcat instruction produces a newly created vec val concatenating two vec vals.

syntax

(doconcat)

reduction rules

(val X) (val Y) (doconcat)
# where
# • `X` is a vec val
# • `Y` is a vec val

→ (val New_vec)
# where `New_vec` is a newly created vec val
# concatenating `X` at the front, and `Y` at the back.
(val X) (val Non_vec) (doconcat)
# where
# • `X` is a vec val
# • `Non_vec` is not a vec val

→ (str Message) (traces) (exception) (raise)
# where
# • `Message` is an error message.

2.4.4.11. dup instruction

dup instruction duplicates the preceding val on the stack.

syntax

(dup)

reduction rule

(val V) (dup) → (val V) (val V)

2.4.4.12. flip instruction

flip instruction swaps the preceding two vals on the stack.

syntax

(flip)

reduction rule

(val A) (val B) (flip) → (val B) (val A)

2.4.4.13. remove instruction

remove instruction removes the preceding val from the stack.

syntax

(remove)

reduction rule

(val V) (remove) → ∅

2.4.4.14. varref instruction

varref instruction produces a varref val.

syntax

(varref Symbol)

Symbol is a str.

reduction rule

(val Owner) (varref Symbol)

→ (val Varref)
# where `Varref` is a varref val whose owner is `Owner`, and whose symbol is `Symbol`.

2.4.4.15. load instruction

load instruction does variable load operation for a variable.

syntax

(load Symbol)

Symbol is a a str.

reduction rules

(val Owner) (load Symbol)
# where the content of variable (`Owner`, `Symbol`) is not empty

→ (val Content)
# where `Content` is the content of the variable (`Owner`, `Symbol`)
(val Owner) (load Symbol)
# where the content of variable (`Owner`, `Symbol`) is empty

→ (str Message) (traces) (exception) (raise) (trace "" Location)
# where
# • `Message` is an error message.
# • `Location` is the implicit location of the `load` instruction.

2.4.4.16. checkfun instruction

checkfun instruction ensures that the preceding val is a fun val.

syntax

(checkfun)

reduction rules

(val Fun) (checkfun)
# where
# • `Not_fun` is a fun val.

→ ∅
(val Not_fun) (checkfun)
# where
# • `Not_fun` is not a fun val.

→ (str Message) (traces) (exception) (raise) (trace "" Location)
# where
# • `Message` is an error message.
# • `Location` is the implicit location of the `checkfun` instruction.

2.4.4.17. call instruction

call instruction sets up invocation of a fun.

syntax

(call Symbol)

Symbol is a str.

reduction rules

(val Fun) (val Recv) (val Args) (call Symbol) (tail)
# where
# • `Fun` is a fun val.
# • `Args` is a vec val.

→ (val Fun) (val Recv) (val Args) (tailcall) (tailtrace Symbol Location)
# where
# • `Location` is the implicit location of the `call` instruction.
(val Fun) (val Recv) (val Args) (call Symbol) Non_tail
# where
# • `Non_tail` is an instruction which is not `tail`

→ (val Fun) (val Recv) (val Args) (docall) (trace Symbol Location) Non_tail
# where
# • `Location` is the implicit location of the `call` instruction.

2.4.4.18. tailcall instruction

tailcall instruction sets up tail traces and invocation of a fun.

syntax

(tailcall)

reduction rule

(tailcall) _tail_traces_
# where
# • `_tail_traces_` is a sequence of 0 or more `tailtrace` instructions.

→ (docall) _snipped_tail_traces_
# where
# • `_snipped_tail_traces_` is a sequence of `tailtrace` instructions,
#   snipped from `_tail_traces_` following the the rule below.

tail trace snipping rule

The specification requires that successive tail calls do not cause stack overflow. Such requirement is called tail call elimination. To guarantee it, the sequence of tailtrace instructions is snipped as follows:

  • Each tailtrace instruction can be remvoed.

  • Each tailtrace instruction can be converted to (tailtrace "" (program name="", program text="", pos index in the program text=0))

  • The length of the sequence must be kept less than or equal to the constant maximum number which the runtime defines.

tail call

The function call done by tailcall instruction.

tail call elimination

The requirement that successive tail calls do not cause stack overflow.

2.4.4.19. docall instruction

docall instruction invokes a fun.

syntax

(docall)

reduction rule

(val Fun) (val Recv) (val Args) (docall)
# where
# • `Fun` is a fun val.
# • `Args` is a vec val.

→ (val Recv) (val Args_copy) _fun_insns_ (tail)
# where
# • `_fun_insns_` is the insns within `Fun`.
# • `Args_copy` is a newly created vec val copying the elements of `Args`.

2.4.4.20. traces instruction

traces instruction makes a vec of trace vals from trace and tailtrace instructions on the stack.

syntax

(traces)

reduction rules

(traces) _insns_ $
# where `_insns_` is a sequence of instructions.

→ (val Traces) _insns_ $
# where
# • `Traces` is a newly created vec val of trace vals,
#   converted from `trace`, `tailtrace`, and `delimit` instructions in `_insns_` as follows,
#   reverting the order.
#   • From a `trace` instruction, a non-tail trace val is produced.
#   • From a `tailtrace` instruction, a tail trace val is produced.
#   • From a `delimit` instruction, a non-tail trace val is produced with symbol="..kont tag..",
#     and location=("", "", 0).

2.4.4.21. exception instruction

exception instruction produces an exception value.

exception

A value which consists of a message string, a vec of traces, and optionally the next exception in the chain. The companion module of exceptions is kink/EXCEPTION.

syntax

(exception)

reduction rule

(val Message) (val Traces) (exception)
# where
# • `Message` is a str val
# • `Traces` is a vec of trace vals

→ (val Exc)
# where `Exc` is an exception val made from `Message` and `Traces`

2.4.4.22. raise instruction

raise instruction raises an exception.

syntax

(raise)

reduction rules

(val Exc) (raise) _no_jump_tag_ (val Escape_tag) (delimit)
# where
# • `Escape_tag` is the escape tag (see kink/KONT_TAG.escape_tag).
# • `_no_jump_tag_` is a sequence of instructions
#   which does not contain (delimit) directly preceded by (val Escape_tag).

→ (val Fun) (val Escape_tag) (delimit)
# where `Fun` is a fun val like {(:cont_raised :cont_jump) cont_raised(Exc) }
(val Exc) (raise) _no_jump_tag_ $
# where
# • `_no_jump_tag_` is a sequence of instructions
#   which does not contain (delimit) directly preceded by (val Escape_tag).

→ (uncaught Exc) _no_jump_tag_ $

2.4.4.23. binding instruction

binding instruction is the place-holder of the current binding.

syntax

(binding)

2.4.4.24. enclosingbinding instruction

enclosingbinding instruction is the place-holder of the enclosing binding.

syntax

(enclosingbinding)

2.4.4.25. clonebinding instruction

clonebinding instruction makes a new binding from the enclosing binding.

syntax

(clonebinding)

reduction rule

(val Enclosing_binding) (clonebinding)
# where
# • `Enclosing_binding` is a binding val.

→ (val Binding)
# where
# • `Binding` is a newly created binding val,
#   copying all the vars of `Enclosing_binding`.

2.4.4.26. setbinding instruction

setbinding instruction sets the current binding.

syntax

(setbinding)

reduction rule

(val Binding) (setbinding) _until_tail_ (tail)
# where
# • `Binding` is a binding val.
# • `_until_tail_` is a sequence of instructions
#   which does not contain `tail` instruction._converted_until_tail_ (tail)
# where
# • `_converted_until_tail_` is a sequence of instructions
#   copied from `_converted_until_tail_`,
#   replacing every occurrence of (binding) by (val Binding).

2.4.4.27. storerecvargs instruction

storerecvargs instruction sets _Recv and _Args vars of the current binding.

syntax

(storerecvargs)

reduction rule

(val Recv) (val Args) (val Binding) (storerecvargs)
# where
# • `Args` is a vec val.
# • `Binding` is a binding val.

→ ∅

When this reduction rule is applied, Recv is assigined to _Recv variable of Binding, and Args is assigined to _Args variable of Binding.

2.4.4.28. tail instruction

tail instruction is the marker of tail context.

syntax

(tail)

reduction rule

(tail) → ∅

This rule means that tail instruction acts as NOP.

2.4.4.29. hostcall instruction

hostcall instruction invokes a host procedure.

host procedure

Information to specify a procedure within the host system, such as a function address, closure, object reference. A host procedure should take a single value as the argument, and return a single value as the result.

syntax

(hostcall Host_proc)

Host_proc is a host procedure.

reduction rule

(val Arg) (call Host_proc)

→ (val Result)
# where
# • `Result` is the result of `Host_proc` with the argument `Arg`.

2.4.4.30. canshift instruction

canshift instruction tests whether the current continuation is delimited by a continuation tag.

syntax

(canshift)

reduction rules

(val Kont_tag) (canshift) _insns_ (val Kont_tag) (delimit)
# where
# • `Kont_tag` is a continuation tag.
→ (val True) _insns_ (val Kont_tag) (delimit)
# where
# • `True` is true val.

(val Kont_tag) (canshift) _no_delimit_ $
# where
# • `Kont_tag` is a continuation tag.
# • `_no_delimit_` is a sequence of instructions
#   which does not contain (delimit) directly preceded by (val Kont_tag).
→ (val False) _no_delimit_ $
# where
# • `False` is false val.

2.4.4.31. kont instruction

kont instruction extracts a delimited continuation function from the stack.

delimited continuation

A function created by kont instruction. Delimited continuation can be abbreviated to kont. See kink/KONT_TAG for creation and usage in the real world.

syntax

(kont)

reduction rule

(val Kont_tag) (kont) (val Kont_tag) (abort) _no_delimit_ (val Kont_tag) (delimit)
# where
# • `Kont_tag` is a continuation tag.
# • `_no_delimit_` is a sequence of instructions which does not contain (delimit)
#   directly preceded by (val Kont_tag).

→ (val Kont_fun) (val Kont_tag) (abort) _no_delimit_ (val Kont_tag) (delimit)
# where
# • `Kont_fun` is a delimited continuation created as follows:

making of a delimited continuation

In the previous reduction rule, Kont_fun is a function which takes a single argument. When the function is called, at the tail of the invocation, the following sequence of instructions is evaluated:

{push the argument of Kont_fun}
_no_delimit_
(val Kont_tag)
(delimit)

2.4.4.32. abort instruction

abort instruction moves to the corresponding delimit.

syntax

(abort)

reduction rule

(val Abort_fun) (val Kont_fun) (val Kont_tag) (abort) _no_delimit_ (val Kont_tag) (delimit)
# where
# • `Abort_fun` is a fun val.
# • `Kont_fun` is a fun val.
# • `Kont_tag` is a continuation tag.
# • `_no_delimit_` is a  sequence of instructions which does not contain (delimit)
#   directly preceded by (val Kont_tag).

→ (val Abort_fun) (nada) (emptyvec) (val Kont_fun) (add) (call "call") (val Kont_tag) (delimit)

2.4.4.33. delimit instruction

delimit instruction is a marker of a continuation tag in the stack.

continuation tag

A value used to mark the delimiter of a delimited continuation. The companion module of continuation tags is kink/KONT_TAG.

syntax

(delimit)

reduction rule

(val X) (val Kont_tag) (delimit) → (val X)

2.4.4.34. trace instruction

trace instruction places a non-tail trace.

syntax

(trace Symbol Location)

Symbol is a str. Location is a tuple of (program name, program text, pos index in the program text).

reduction rule

(trace Symbol Location) → ∅

This rule means that trace instruction acts as NOP.

2.4.4.35. tailtrace instruction

tailtrace instruction places a tail trace.

syntax

(tailtrace Symbol Location)

Symbol is a str. Location is a tuple of (program name, program text, pos index in the program text).

reduction rule

(tailtrace Symbol Location) → ∅

This rule means that tailtrace instruction acts as NOP.

2.4.5. Resource error

The following reduction rule can be applied anytime, when any type of resource error happens, for example, when the length of the sequence of instructions of the stack becomes too long (stack overflow).

reduction rule

^

→ ^ (str Message) (traces) (exception) (raise)
# where
# • `Message` is an error message.