Factoid
Poslin is a concatenative language with strict postfix notation and a metacircular interpreter.
It is similar to the language described in EWD28 in principle but is not inspired by it.
Unlike the language described in the linked paper, the immediateness of a symbol is definable in code, allowing the programmer to define almost arbitrary new syntax.
Things like the symbol table and lexical environments exist as first class objects.
Every token in Poslin is surrounded by whitespace.
Length 1 snippet
!
This is actually not a working program. Executing it in a fresh session gives a stack-bottom-error
. This operation is immediate and executes the operation on top of the current stack. Without it, nothing would ever happen.
Length 2 snippet
&+
This also isn't a working program. &
is an immediate operation to compile something into a thread. &+
also is an immediate operation, but after compiling it executes the operation stored at a place accessible via HOOK compilation-hook slot-sub-get
.
This way users can define their own optimization passes.
Length 3 snippet
[ ]
This creates an empty stack. []
also creates an empty stack ad does so much more efficiently. That's because Poslin works with something which is called the path. The path is a stack of environments, where environments are kind of like prototyped objects, that is, they are mappings (from symbols to bindings) which can have parent-environments. When some object is found by the reader which is not an immediate symbol, it is put onto a stack which is saved in a binding (besides the path, the return stack and arguably streams the only mutable data structures in poslin) which is found under the symbol STACK
in the environment on top of the path.
[
puts a new environment onto the path which contains a fresh binding containing an empty stack for it's STACK
. ]
pops the topmost environment off the path and then extracts the stack saved in the binding in its STACK
slot.
[]
just constantly returns an empty stack.
[]
, [
and ]
are all immediate.
Length 4 snippet
'& &
There is no single operation in poslin which defines a new operation. Well, actually there are several, but they're all defined in the standard library, which is written in a subset of poslin called "poslin0".
&
has the purpose of transforming a given object into a thread.
For threads it's just an identity operation.
Symbols are looked up in an environment saved in a binding in the OP
slot of the environment on top of the path.
Stacks are converted by turning everything but threads into constant functions returning that object and then concatenating together the resulting objects into one thread. At least, that's roughly how it works. This means, of course, that any operation which should be called inside a thread created this way needs to be turned into a thread via &
before the calling thread is constructed.
Every other object is turned into a constant thread returning that exact object.
'&
is special syntax: The leading '
tells the reader that this is a quotation, that is, everything after the '
is interpreted as a symbol (including any other '
s) and then just put onto the current stack without checking for immediateness.
So the given snippet returns the thread of our poslin compiler. It is used in the operation ]o
, which defines a new operation. With the knowledge given up to this point, you might be able to figure out how defining an operation works conceptually, even if you are missing some important operations.
Length 5 snippet
$:
":
This is the string you'd normally see as "\""
in most other languages, that is, the string containing exactly the double-quote character.
The character $
starts a delimited string. A delimited string has a delimiter, which is given between the $
and the next newline. The string contains all characters between that newline and the next occurence of the delimiter followed by some kind of whitespace.
If the delimiter occurs and is not followed by whitespace, it is a part of the string and does not function as delimiter. Remember: Every token in Poslin is surrounded by whitespace.
So, the given string could also be written as
$"
""
or even """
, as Poslin recognizes the usual syntax for strings, too. It does not recognize any escape sequences.
Length 6 snippet
+ call
This is almost equivalent to + &
. The important difference is that + &
returns the thread of +
while + call
returns a thread which reads the binding which holds the definition of +
and then calls the content of that binding. So, if you expect that the definition of +
might change and you need to call the new definition instead of the old one, use + call
.
This is necessary for defining recursive operations. There is no way to insert a thread into itself, so the binding which is later intended to contain the thread is inserted instead via call
and after the thread is constructed it is written into that binding.
Length 7 snippet
. ? ! !
?
consumes the top three elements off the stack. The third from the top needs to be a boolean value. If it is true, the second from top is put back onto the stack, if it is false the first from top is put back onto the stack.
.
is simply the no-op.
So this is an immediate when
. Imagine the following stack:
[ 2 TRUE P{negation} ]
This proceeds as follows with the above sequence:
[ 2 TRUE P{negation} . ]
[ 2 TRUE P{negation} . ? ]
[ 2 P{negation} ]
[ -2 ]
Whereas if the stack is
[ 2 FALSE P{negation} ]
It proceeds thus:
[ 2 FALSE P{negation} . ]
[ 2 FALSE P{negation} . ? ]
[ 2 . ]
[ 2 ]