1 Introduction

I wanted Worst to be a language that was easy to implement, yet flexible enough to grow beyond just being an experiment. Following a rigorous process of repeatedly deleting everything and starting over until new features stopped requiring complete redesigns, I discovered the following combination of properties that worked well together:

Each of these properties work together to support the others. Combined with a minimum of internal data structures (just two: the call stack and the data stack), they lead to other properties like dynamic scope and extensible error handling.

To combine all of this, here’s the main interpreter loop in brief:
  • Get the next thing from the program. If it’s not a symbol, put it on top of the stack and repeat.

  • If it is a symbol, look it up in the definition set. (If it’s not there, check the calling stack frames too.)

  • If the definition is a normal function, call it and start again from the top.

  • Otherwise, it’s a list, so treat it as a sub-program. Step into a new stack frame and interpret it.

  • Repeat until the program is empty. If there’s a calling stack frame, carry on with that.

That’s it, ignoring uplevel, and the read-eval loop that does the actual syntax parsing. Most of the rest of the code is dedicated to defining built-in functions.