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, extensible error handling, and a REPL for free.

So, in brief, here’s the main interpreter loop:
  • 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.