Diane

Table of Contents

Diane is a toy stack-oriented language with dynamic scoping. It's not pretty or all that interesting, but it's simple enough to present the syntax and semantics in a straightforward way (and to build a reasonable playground for it).

Diane was born out of a need to visualize evaluation. During the spring (2024), I co-taught a course on programming languages in which we often used stack-oriented languages to motivate and demonstrate operational semantics. I worked through step-by-step evaluations on the board often enough to want a process for doing it which is automatic and interactive, along the same lines as the Python Tutor for Composing Programs.

Disclaimer. This page is not a tutorial, just a description of the language and playground.

The Playground

The primary goal of this project was to build a playground (in Elm) for toying with Diane programs. The playground consists of an editor window for writing programs, a visualization window for seeing how the stack and environment change over the course of evaluation, and a console window for seeing intermediate print statements and error messages.

The playground can also be embedded:

We take advantage of this below to highlight features of the semantics.

Partly out of laziness, partly out of a general bent towards minimal interfaces, there are no instructions for using the playground within the playground itself, so I've included them here.

Instructions

  • The step button consumes a single command of the program in the editor window and updates the stack and environment accordingly.
  • The run button consumes as much of the program as possible until
    • the entire program is consumed
    • there is an error
    • the evaluation times out
  • The undo button reverses a step taken by pressing either the step button or the run.
  • The reset button resets the program state to its initial state, clearing the undo history.
  • The save button sets the program in the editor window to be the program of the initial state. This also clears the undo history.1
  • The clear button in the console window clears the messages printed there.2

Keyboard Shortcuts

\ step
ALT run
` undo

It's possible to step through a program while writing it using the above keyboard shortcuts. In most cases you can also hold down the key to rapidly step through a program, providing a make-shift animation mechanism.

Syntax

The following is a simple EBNF Grammar describing a Diane program. In short, a program is a sequence of whitespace-separated commands.


<program>  ::= { <command> }
<body>     ::= '{' <program> '}'
<command>  ::= <int> | 'drop' | 'swap' | 'dup' | 'rot'
             | '+' | '-' | '*' | '/' | '%' | '=' | '<'
             | 'def' <ident> <body> | '#' <ident>
             | '@' <ident> | '[' | ']'
             | '?' <body> 'else' <body> | 'while' <body> 'do' <body>
<int>      ::= โ„ค
<ident>    ::= ALL_CAPS_SNAKE

Semantics

We take a value (๐•) to be a integer (โ„ค) or a program representing a subroutine (โ„™).3 A configuration, written ( S, E, P ), is made up of a stack of integers (S), an environment consisting of a list of collections identifier-value bindings (E), and a program as given by the above grammar (P). The operational semantics of Diane are given by the reduction rules below.

Notation for Environments

โŸจโŸฉ empty collection of bindings
๐’ข global bindings
L ยท E E with (shadowing) local bindings L
E[X โ†ฆ A] E with X bound to the value A (in topmost local bindings)
X โˆˆ E X is bound in E
E[X] the value to which X is bound in E (searched for top down)
E/X E with any binding of X removed

Stack Manipulation

Several commands are dedicated to manipulating the stack. We can

  • push an integer onto the stack
  • drop the top element off the stack
  • swap the top two elements on the stack
  • dup-licate the top element on the stack
  • rot-ate the top three elements on the stack

In the case that there aren't enough integers on the stack to perform a given operation, a stack underflow error message is printed to the console.

There is also an unofficial command for print-ing the top element of the stack and removing it. See the example below.4


               ๐‘˜ โˆˆ โ„ค
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( push )
( S, E, k P ) โŸถ ( ๐‘˜ :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( drop )
( ๐‘› :: S, E, drop P ) โŸถ ( S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( dropErr )
( โŠฅ, E, drop P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( swap )
( ๐‘š :: ๐‘› :: S, E, swap P) โŸถ ( ๐‘› :: ๐‘š :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( swapErrโ‚ )
( ๐‘› :: โŠฅ, E, swap P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( swapErrโ‚€ )
( โŠฅ, E, swap P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( dup )
( ๐‘› :: S, E, dup P ) โŸถ ( ๐‘› :: ๐‘› :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( dupErrโ‚€ )
( โŠฅ, E, dup P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( rot )
( ๐‘™ :: ๐‘š :: ๐‘› :: S, E, rot P ) โŸถ ( ๐‘š :: ๐‘› :: ๐‘™ :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( rotErrโ‚‚ )
( ๐‘š :: ๐‘› :: S, E, rot P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( rotErrโ‚ )
( ๐‘› :: โŠฅ, E, rot P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( rotErrโ‚€ )
( โŠฅ, E, rot P ) โŸถ StackUnderflow

The following is a small example program using these commands. Press the step button to see how the stack changes as each command is consumed and evaluated. Note that the last command cannot be consumed because there are no integers on the stack to duplicate. Attempting to evaluate this command results in a stack underflow error message.

Arithmetic and Comparisons

The commands in this section are used to operate on the integers on the stack. We can

  • (+) add
  • (-) subtract
  • (*) multiply
  • (/) divide
  • (%) determine the modulus
  • (=) check for equality
  • (<) check for less-than

As above, if there aren't enough integers on the stack to perform an operation, a stack underflow error message is printed. An error may also occur when trying to divide by zero.


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( add )
( ๐‘š :: ๐‘› :: S, E, + P ) โŸถ ( ๐‘š ๏ผ‹ ๐‘› :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( addErrโ‚ )
( ๐‘› :: โŠฅ, E, + P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( addErrโ‚€ )
( โŠฅ, E, - P ) โŸถ StackUnderflow


  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( sub )
( ๐‘š :: ๐‘› :: S, E, - P ) โŸถ ( ๐‘š โ”€ ๐‘› :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( subErrโ‚ )
( ๐‘› :: โŠฅ, E, - P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( subErrโ‚€ )
( โŠฅ, E, - P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( mul )
( ๐‘š :: ๐‘› :: S, E, * P ) โŸถ ( ๐‘š ร— ๐‘› :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( mulErrโ‚ )
( ๐‘› :: โŠฅ, E, * P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( mulErrโ‚€ )
( โŠฅ, E, * P ) โŸถ StackUnderflow


                       n โ‰  0
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( div )
( ๐‘š :: ๐‘› :: S, E, / P ) โŸถ ( ๐‘š ๏ผ ๐‘› :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( divErrByZero )
( ๐‘š :: 0 :: S, E, / P ) โŸถ DivByZero


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( divErrโ‚ )
( ๐‘› :: โŠฅ, E, / P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( divErrโ‚€ )
( โŠฅ, E, / P ) โŸถ StackUnderflow


                       n โ‰  0
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( mod )
( ๐‘š :: ๐‘› :: S, E, % P ) โŸถ ( ๐‘š mod ๐‘› :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( modErrByZero )
( ๐‘š :: 0 :: S, E, % P ) โŸถ DivByZero


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( modErrโ‚ )
( ๐‘› :: โŠฅ, E, % P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( modErrโ‚€ )
( โŠฅ, E, % P ) โŸถ StackUnderflow


                   ๐‘š ๏ผ ๐‘›
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( eq )
( ๐‘š :: ๐‘› :: S, E, = P ) โŸถ ( 1 :: S, E, P )


                   ๐‘š โ‰  ๐‘›
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( neq )
( ๐‘š :: ๐‘› :: S, E, = P ) โŸถ ( 0 :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( eqErrโ‚ )
( ๐‘› :: โŠฅ, E, = P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( eqErrโ‚€ )
( โŠฅ, E, = P ) โŸถ StackUnderflow

                   ๐‘š < ๐‘›
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( le )
( ๐‘š :: ๐‘› :: S, E, < P ) โŸถ ( 1 :: S, E, P )


                   ๐‘š โ‰ฎ ๐‘›
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( nle )
( ๐‘š :: ๐‘› :: S, E, = P ) โŸถ ( 0 :: S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( leErrโ‚ )
( ๐‘› :: โŠฅ, E, = P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( leErrโ‚€ )
( โŠฅ, E, = P ) โŸถ StackUnderflow

Subroutines

A subroutine is just a named program. We can define subroutines and call them. Defining a subroutine adds a binding in the environment of its name to the program in the body of its definition. Calling a subroutine simply prepends its body to the program being evaluated.5


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( def )
( S, E, def F { Q } P ) โŸถ ( S, E[F โ†ฆ Q], P )


  F โˆˆ E                  E[X] โˆˆ โ„™
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( call )
( S, E, #F P ) โŸถ ( S, E, E[F] P )


  F โˆˆ E             E[X] โˆ‰ โ„™
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( callErrโ‚ )
( S, E, #F P ) โŸถ InvalidCall


                F โˆ‰ E
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( callErrโ‚€ )
( S, E, #F P ) โŸถ UnknownVariable

Variables

A variable is just a named integer. We can

  • assign an integer to a variable, which adds a binding to the environment
  • lookup a variable binding in the environment (by typing the variable itself)

Variables bindings are available within blocks (or globally). Local blocks can be opened and closed using the delimiters '[' and ']', respectively.6 Bindings created within a local block are available only within the evaluation of that block. Bindings not created within a local block are available anywhere in the program after they've been created.


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( assign )
( ๐‘› :: S, E, @X P ) โŸถ ( S, E[X โ†ฆ ๐‘›], P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( assignErrโ‚€ )
( โŠฅ, E, @X P ) โŸถ StackUnderflow


  X โˆˆ E                    E[X] โˆˆ โ„ค
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( lookup )
( S, E, X P ) โŸถ ( E[X] :: S, E, P )


  X โˆˆ E              E[X] โˆ‰ โ„ค
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( lookupErrโ‚ )
( S, E, X P ) โŸถ InvalidLookup


               X โˆ‰ E
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( lookupErrโ‚‚ )
( S, E, X P ) โŸถ UnknownVariable


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( openLocal )
( S, E, [ P ) โŸถ ( S, โŸจโŸฉ ยท E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( closeLocal )
( S, L ยท E, ] P ) โŸถ ( S, E, P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( closeLocalErr )
( S, ๐’ข, ] P ) โŸถ GlobalClose

Conditionals

Finally, there are if-statements for conditional reasoning. We also include while-loops with the usual semantics (i.e., defined in terms of if-statements).


                         ๐‘› โ‰  0
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( ifTrue )
( ๐‘› :: S, E, ? { Qโ‚ } else { Qโ‚‚ } P ) โŸถ ( S, E, Qโ‚‚ P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( ifFalse )
( 0 :: S, E, ? { Qโ‚ } else { Qโ‚‚ } P ) โŸถ ( S, E, Qโ‚ P )


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( ifErrโ‚€ )
( โŠฅ, E, ? { Qโ‚ } else { Qโ‚‚ } P ) โŸถ StackUnderflow


โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ( while )
( S, E, while { Qโ‚ } do { Qโ‚‚ } P ) โŸถ
( S, E, Qโ‚ ? { Qโ‚‚ while { Qโ‚ } do { Qโ‚‚ } } else { } P )

Footnotes:

1

Note that this only saves the program, not the whole program state. When reseting, the stack and environment will be empty, but the inital program will be the most recently saved program.

2

This cannot be undone.

3

In other word, ๐• = โ„ค โˆช โ„™.

4

It's unofficial because printed strings are not a part of the configuration. Semantically, print is identical to drop.

5

Its truly amazing to me how simple this is.

6

Note that they aren't actually delimiters. It's possible to write program which opens more blocks than it closes.