fffff Documentation

fffff is a stack-oriented concatenative programming language. It has dynamic types, and first-class dynamic scopes. fffff is designed to be easy to implement, not easy to write in.

The following example defines a function named square, which squares the number on top of the stack by duplicating the number on top of the stack, then multiplying the two copies of it:

(@ *) >!square

The following example then defines a function which takes the fourth power of the number on top of the stack, by calling square twice:

(square square) >!fourth

This is effectively function compositionfourth is defined by composing square with itself. Programming in fffff is a mixture of low-level stack manipulation and high-level point-free functional programming.

Numbers

The only numeric type is a signed 32-bit integer. An integer literal is written in decimal, with a leading - for negative numbers. An integer literal is an instruction which, when executed, pushes its own value to the stack.

There are the following numeric operations:

Operations take their operands off the stack and push their results to the stack, so for example 3 2 ** means “3 to the power of 2” and leaves 9 on the stack.

Note that the operation ** is written after the instructions to push 3 and 2 to the stack; this is reverse Polish notation.

Booleans

The keywords true and false are Boolean literals. They are instructions which, when executed, push their own value to the stack.

The logical operations are the keywords and, or and not. Logical operations likewise take their operands off the stack, leave their results on the stack, and must be written using reverse Polish notation.

The logical operations and and or are not short-circuiting, since their Boolean operands must already be on the stack.

Strings

A string literal is delimited by an odd number of single-quote characters ', or by an odd number of double-quote characters ". The escape sequences \', \", \\, \n, \r, \t, \b and \f have their usual meanings. A unicode escape sequence may be written in the form \uXXXX, where XXXX is a four-digit hexadecimal value.

If a string literal’s delimiter has three or more quote characters, then it is a multi-line string literal and may contain unescaped newlines. It is a syntax error for a string literal delimited by one quote character to contain an unescaped newline.

Basic stack manipulation

The @ operation duplicates the top element of the stack. Repeating the @ symbol gives a variation of this operation which duplicates an element further from the top of the stack; for example, @@ duplicates the second-from-top element, @@@ duplicates the third-from-top element, and so on.

The keyword del deletes the top element of the stack. This is equivalent to popping from the stack and discarding the result.

Comments

A line comment begins with a #. Everything from this character until the line ending is ignored. fffff does not have multiline comments, but they can be simulated with multi-line string literals:

"""
This is a multi-line string, followed by the `del` keyword to delete it from
the top of the stack. This snippet will have no effect on the program's
execution, so it is effectively a comment.
""" del

Printing

The keywords print and println each pop a value from the stack and print it to the console. The difference between them is that println also prints a newline. For example, the following program prints the text Hello, world! to the console:

"Hello, world!" println

If the value to be printed is not a string, it is first converted to a string.

Variables

A variable name begins with a letter or underscore, followed by any number of letters, digits or underscores.

The operation >x pops a value from the stack and stores it as a variable named x in the current scope. The operation x or <x then loads that value and pushes it to the stack.

The syntax >x means “store into x”, and <x means “load out of x”.

Since values are popped from the stack in reverse order, an instruction like >x >y will assign the top two values to x and y out of order. For convenience, the instruction >x,y assigns them in order; this is equivalent to >y >x. Any number of variable names may be separated by commas in this way. You can also write <x,y when loading multiple variables, but this is equivalent to <x <y.

While it is convenient to think of variables as having values, this is not technically accurate. In fffff, variables contain instructions, and named functions (such as square in the example above) are also variables. The operation >x does not really store the value in the variable x; rather, it stores an instruction in the variable x to push that value to the stack. This matters because where a variable is defined as a function, it may do something other than simply push a stored value to the stack.

Quotes

A quote is analagous to a function literal in other languages. Quotes are delimited by round brackets, ( and ). Instructions between these brackets are not executed immediately; rather, the quote itself is pushed to the stack, and its instructions will be executed whenever the quote is “called”. The operation ! pops a quote from the stack, and calls it.

Variable names inside quotes are resolved when the quote is called, not evaluated or bound when the quote is written. Quotes may occur inside other quotes.

Quotes are immutable, and are first-class objects. A quote can be pushed to a stack or stored in a variable.

When a quote is stored in a variable with an instruction such as >f, the variable f contains an instruction to push the quote to the stack; not to call it. The quote can be called by writing f!, which uses the ! operation to call the quote.

Alternatively, the syntax >!f can be used to make f a function — the variable f holds an instruction to call the quote, so that the quote can be called simply by writing f.

Control flow

The keyword repeat is a looping instruction which calls a quote repeatedly. Its operands are the quote and the number of repetitions. The following example prints the word “Hello” five times:

("Hello" println) 5 repeat

The keyword if is a conditional instruction which calls (or doesn’t call) a quote depending on a Boolean condition. Its operands are the quote and a Boolean value. The following example prints the word “Hello” if x equals y, otherwise it does nothing:

("Hello" println) x y = if

Stacks

Unlike other stack-based languages which use a single stack, fffff allows the use of local stacks.

A left square bracket [ creates a new local stack and descends to it. A right square bracket ] ascends from it. These are separate operations; in particular, it is not a syntax error for them to be unmatched.

Stacks are mutable, and are first-class objects. A reference to a stack can be pushed to another stack (or itself), or “stored” in a variable.

The operation .[ enters a stack by popping a reference from the current stack, and descending to the stack at that reference. The operation ]. exits the current stack by ascending from it and pushing a reference to it to the new current stack. For example, [1 2 3]. creates a stack containing the numbers 1, 2, 3, leaving a reference to it on the current stack.

Note that there are no commas between the stack’s elements. The integer literals are really instructions to push values to the stack, with spaces separating the instructions.

The following keywords are operations on stacks:

The keyword stack pushes a reference to the current stack to the current stack (i.e. itself).

The metastack

There is a special “stack of stacks” called the metastack, which is manipulated when descending to or ascending from a stack. The current stack is the one at the top of the metastack. Unless otherwise specified, in this documentation the phrase “the stack” means the current stack.

Initially, the metastack contains a single empty stack, which is the global stack. Descending to a stack pushes it to the metastack, making it the current stack. Ascending from the current stack pops it from the metastack. It is a runtime error to ascend from the global stack leaving the metastack empty.

The metastack is not a first-class object; there is no way to obtain a reference to it.

Scopes

A scope contains variables, each of which is a name associated with an instruction. The instruction is either to push a value to the stack, or to call a quote.

A left curly brace { descends to a new local scope. A right curly brace } ascends from it. These are separate operations; in particular, it is not a syntax error for them to be unmatched.

Scopes are mutable, and are first-class objects. A reference to a scope can be pushed to a stack, or “stored” in a variable.

The operation .{ enters a scope by popping a reference to it from the stack, and descending to the scope at that reference. The operation }. exits the current scope by ascending from it and pushing a reference to it to the stack.

For convenience, a variable can be accessed from a scope on the stack by writing .x. This is equivalent to .{ <x }, so it enters the scope, loads x, and then exits the scope. This is analogous to member access in object-oriented programming languages, and allows scopes to be used as objects.

It is also possible to use member access in store/load instructions, for example >obj.x or <obj.x. These are equivalent to obj .{ >x } and <obj .{ <x } respectively.

The keyword this pushes a reference to the current scope to the current stack.

The scopestack

Like the metastack, there is a special “stack of scopes” called the scopestack, which is manipulated when descending to or ascending from a scope. The current scope is the one at the top of the scopestack.

Initially, the scopestack contains a single empty scope, which is the global scope. Descending to a scope pushes it to the scopestack, making it the current scope. Ascending from the current scope pops it from the scopestack. It is a runtime error to ascend from the global scope leaving the scopestack empty.

An instruction like >x always stores in the current scope, and an instruction like <x always loads from the current scope. However, the instruction x does not necessarily load from the current scope; it loads from whichever scope nearest the top of the scopestack has a variable named x. It is a runtime error if no scopes in the scopestack have such a variable. This is known as dynamic scoping.

The scopestack is not a first-class object; there is no way to obtain a reference to it.