A Worst program comprises a few main things:
A stack to hold data, which starts off empty
An environment of functions, which starts off containing a number of pre-defined builtins
The program itself, a sequence of literals and words parsed from the source code (see also the syntax documentation):
Literals are plain pieces of data, like numbers, strings, and lists, which are simply placed on top of the stack when evaluated
Words are bare symbols, to be looked up in the environment and evaluated in order to modify the stack, environment, or remaining program.
Worst is concatenative, meaning that
in order to evaluate things in sequence,
you can just place them next to each other.
For instance, here is a program consisting of a literal string that puts itself on top of the stack, followed by a word that expects a string on top of the stack that it can display on standard output:
"Hello, World!\n" prints
You can run the above program,
and all subsequent code listings on this page,
by putting it in a file called (e.g.) hello.w
and running it using
./worstc -i hello.w.
Most functions, like prints, only modify the stack, and there are a few that operate on the environment. However, there is one special function that modifies the program itself: quote, also abbreviated ' (a single quote).
quote hello ; or 'world
The quote builtin takes the next item in the program and puts it on the stack as if it were a literal, even if it is a word that would otherwise be evaluated immediately.
This breaks the concatenative property, but allows for more ergonomic constructs. Consider define and if, both definable in pure Worst:
define test [1 2 3 add greater-than] test if [ "It was true\n" ] [ "It was false\n" ] prints
Internally, both of these use quote to look ahead in the program, resulting in code that reads more naturally than otherwise possible.
Uplevel and the context stack
The notion of an environment described earlier was a minor over-simplification.
Like most languages, Worst has a call stack, where upon calling a function, a fresh stack frame is allocated; the function can do with that what it wishes as it is destroyed when the function returns.
A stack frame in Worst, also known as context, includes
an environment of definitions,
the remaining code in the function
(yet to be evaluated),
and a link to the parent context.
Looking up the definition of a word involves looking it up in the calling (parent) context if it cannot be found in the current environment. This is known as dynamic scope (in contrast to lexical scope, the way most languages resolve definitions, where the most recently given definition in source code is used).
Most other languages do not allow access to the stack frame of the
calling function, but this access is a core feature of Worst.
Enter uplevel, a builtin which evaluates the word on top of the stack as if in the calling function:
; 'uplevel' doesn't work at the top level; you can only use it inside a function define my-cool-quote [ ; Quote the symbol 'quote' and put it on top of the stack quote quote ; Evaluate 'quote' in the calling context uplevel ] ; Behold: 'my-cool-quote' is effectively an alias of 'quote'. my-cool-quote "Cool Quote" ; "Cool Quote" is now on the stack, as expected. ; undefine name: Remove the definition named by 'name' define undefine [ ; 'quote quote uplevel' is a common pattern ; when defining forward-reading functions like this. 'quote uplevel ; 'name take-definition' (where 'name' is a symbol) ; removes 'name' from the definition environment for the current context, ; and puts its body (a list of symbols) on the stack. 'take-definition uplevel ] ; Now, attempting to invoke 'my-cool-quote' will result in an error. undefine my-cool-quote
uplevel can be used in conjunction with itself, allowing code to access the full context stack.
That’s all there is to it. Aside from quote and uplevel (and the upcoming character-level syntax system), Worst is a fairly normal stack-based concatenative language.
The rest of your time with Worst will likely involve learning and developing patterns and idioms, overwriting it with your own language, making cool stuff, and of course finding bugs in the interpreter (which you are invited to report).
You may like to browse the documentation to have a look at the rest of the builtins and pre-defined functions.