I was going doing an exercise from Thinking in PostScript and became confused by this piece of code, from Chapter 5, exercise 3 (pp. 64-65):
This code defines a function ushow
, and uses a local dictionary of
some kind. But where is LOCAL
defined? It’s not defined in the
code, and it isn’t defined in any Adobe documentation. And yet this
code ran in Ghostscript. I ended up asking on Stack
Overflow. The answer I
got showed me what was really happening, and taught me something I
hadn’t quite realized: PostScript has some LISPish qualities to it.
A normal functioin definition in PostScript looks more like this:
(Function definitions often end with bind def
instead of def
, but
that doesn’t matter here). Notice the difference? Not only is that
LOCAL begin...end
block missing from inside the function definition,
but the end doesn’t contain all of that dup 0 4 dict put
stuff.
That it turns out, is the key to how LOCAL
works here. To
understand what’s really happening, let’s evaluate the puzzling code
as the PostScript interpreter does. First:
And the operand stack is now:
/ushow
Then comes the start of the procedure:
This puts the PostScript in a special mode where it creates an
executable array. From now on until the }
is parsed, tokens are
not executed, but are simply added to the executable array. That
means that nothing in the procedure needs to be defined until it
executed. That includes LOCAL
, so these lines:
Simply add the symbols LOCAL
, begin
, whatever code is elided by
...
, and end
to the executable array. None of it gets executed
until the function is actually called, so it doesn’t matter what it is
until then. It can be giberish, or a mix of giberish and regular
code. Do you see where this is going?
The end of the executuble array is signalled the }
at the beginning
of the last line:
let’s take this one operator at a time. On the right, we’ll show what’s on the stack at the completion of that operation:
This takes the interpreter out of the “defining a procedure” mode and back into regular, “executing code” mode, then it pushes the procedure onto the top of the stack. Now set up some arguments:
then create a dictionary with room for at least four entries:
Finally, here’s the fun part:
put
stores the dictionary at index 0 of the procedure. There’s a
reason that PostScript calls it an executable array. It really is
an array, and it can be manipulated as one. This code replaced the
symbol LOCAL
at the beginning of the procedure with a dictionary.
When the procedure executes, LOCAL
won’t be there at all. In its
place will be a dictionary.
After that excitement, the rest is really boring:
Finally, store the procedure in the dictionary with the key /ustore.
This is what makes PostScript a little bit like LISP. In LISP, code is just data that you execute. A LISP program can manipulate other code as though it were data (because it is, really): change it, add things to it, replace it with other code–anything you want. And so can you in PostScript.