PostScript control structures

Tuesday 16 December 2003This is close to 21 years old. Be careful.

Hacking around with PostScript, I’m being reacquainted with its unusual language structure.

The whole thing is stack oriented, like Forth. Adding two numbers is done by pushing the two numbers on the stack, then invoking the add operator, which pops two numbers, adds them, and pushes the result:

2 2 add   % leaves 4 on the stack

Control structures work the same way. The ifelse operator takes three things from the stack: a boolean and two executable blocks. If the boolean is true, it executes the first block and discards the second. If the boolean is false, it executes the second block and discards the first:

1 2 eq { (y) } { (n) } ifelse   % leaves "n" on the stack

The for operator is used for looping. It takes a start value, an increment, an end value, and an executable block as operands:

% Calls doSomething ten times with 1 through 10 as arguments
1 1 10 { doSomething } for

Here’s where it gets tricky: these control operators take their values from the stack, and it doesn’t matter how the values got there. Often, as in these examples, they’ll have been pushed there by literals just before the operator is called. But they can be placed there any other way, and the operators will still work:

% Loop up or down, depending on the value of up?
up? { 1 1 10 } { 10 -1 1 } ifelse
{ doSomething } for

This will loop from 1 to 10 if up? is true, and from 10 to 1 if up? is false.

Also, because executable blocks are objects on the stack that can be passed around and manipulated like any other object, you can write your own control operators:

% probability block 'domaybe' -
% Excecute the block randomly, determined by the probability.
/domaybe {
    exch            % bring the probability to the top
    1000 mul        % scale the probability to 0-1000
    rand 1000 mod   % pick a random integer in 0-999
    lt              % should we execute the procedure?
    exch            % swap args to make them right for if
    if              % execute the block if we should
} def

.01 { veryInfrequently } domaybe
.95 { almostCertainly } domaybe

Not that you’d want to build large systems in PostScript, but it’s good to bend your brain in different ways occasionally. Smalltalk has a similar build-your-own-control-structures descriptive power, though it is not stack based.

Comments

[gravatar]
The ability to extend the set of language control structures is also a prominent feature of Lisp and its derivatives. And don't forget Forth which predates Postscript with a lower-level but very similar notion of stack-based computing.

This ability to define language extensions with the same power sort of expressive power as the base language doesn't seem to be too popular these days. I guess it's more interesting to language researchers or defining domain-specific "little" languages than the general community of developers.

The problem with this sort of power is it can easily result in "write only" code.
[gravatar]
Hey Bob ... been awhile...

In the business process world there is a raging debate around PI Calculus (Barry Briggs among others has been chiming in). My take on PI calculus as I tried to follow some of the threads was that it required lambda calculus and this is the basis for languages like lisp (self-referential capabilities being one of them).

Stack languages like Forth ... I actually don't like them (ever worked with APL?) ... but will use them to build things like FSMs, certain types of dataflow managers, etc.

I'll go way out on a limb here ... the .NET CLR is as close (if not more in some respects) to what I could do with a lisp machine nearly 20 years ago. One-time use of delegates launched on an async thread are equivalent (in my opinion) to closures in lisp/scheme and are incredibly powerful. I was initially anti-.NET ... but I'm now very much pro-CLR and am eagerly awaiting some of the features being alluded to in the next release (hinting for the garbage collector -- very powerful when coupled to use of Interops).
[gravatar]
Hi Phil. Yes it has bee a long time. Last I heard you were doing Groove-related stuff.

I agree that the .NET CLR is very powerful and is evolving in interesting ways. Beyond the power that you mention, I think its clear that we're turned a corner here. We're finally developing in "safer" languages with GC as the norm. We don't spend cycles chasing down memory leaks, bad pointer references, bad array indexing, etc.
[gravatar]
Jonathan M. Richardson 4:45 PM on 17 Dec 2003
Why are you writing postscript?

Alternative formulations:

What leads a man to write postscript?
What horrible thing did your computer do to you to drive you to write postscript?
[gravatar]
While you're in the neighborhood of Postscript and Forth, I urge you to take a look at REBOL, a network-ready, platform-independent language that implements aspects of these other languages in a functional style.

Particularly interesting is REBOL's grammars-based parsing, which gives programmers much of the power of REGEX while allowing easy creation of domain specific languages (little languages, etc.).

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.