PostScript as a Programming Language

5.9 PostScript as a Programming Language

PostScript has all the programming functionality you would expect from an HP-caclulator, plus some interesting features hard to find in other languages. There are variables, loops, subroutines (of a sort), and an advanced idea for the scope of variables.

Variables in PostScript are defined with the `def` operator. To create a variable called `pi`, you would use:

```/pi
3.1416
def
```
Notice the slash before the variable name. You need this only when you define the variable. Thereafter, you would get the area of a circle of radius 4 like this: `4 4 mul pi mul`. You use the slash to refer to the variable's name, not its value.

Subroutines are made exactly the same way. However, you normally want more than one command in a subroutine. To do this, you group commands with curly braces. The following defines a subroutine that makes a filled circle:

```/filledcircle {
gsave
newpath
0 0 1 0 360 arc
fill
grestore
}
def
```
This isn't very interesting, however, since this is a very limited subroutine. We can make it take arguments by making it use the previous elements on the stack. So we could set the radius of the circle like this:
```/filledcircle {
gsave
newpath
0 0 radius 0 360 arc
fill
grestore
}
def
```
Now, to make a filled circle of radius 1, you would use `1 filledcircle`. Notice how `exch` is used in the variable definition. The stack starts with (if our argument is 1)
```1
```
After the `exch` we have
```/radius
1
```
and now the `def` command will work.

There still is something not quite ideal about our subroutine - the radius variable is global! You might be overwriting a value used somewhere else by other code. To fix this, you need to use a dictionary. This an advanced concept of variable scope that you will only learn a tiny bit of here. At the beginning of your subroutine, you can define a new dictionary with n variables using n` dict begin`. This says, for our purposes, that the next n variables defined will be local variables. They have scope until the next `end` statement. So here is our routine again. This time it takes three arguments: x center, y center, and radius.

```/filledcircle {
3 dict begin
/y exch def
/x exch def
gsave
newpath
x y radius 0 360 arc
fill
grestore
end
}
def
```
Notice how `gsave` and `grestore` are used here. They allow the circle to be drawn without disrupting any path that might be in progress.

Let us now turn to making a "target" with our filled circles. This will be a set of nested circles with different grey values. Obviously we will use a loop to do this, in this case a `for` loop. The `for` loop takes four arguments: an initial value for the loop variable, a step, a final value, and code to repeat. During each iteration of the loop, the current value of the loop variable is pushed on the stack, and then the code is repeated. This loop makes a target with 4 circles:

```4 -1 1 {
gsave
dup
1 sub 4 div setgray
0 exch 0 exch 4 div filledcircle
grestore
} for
```
We can put this all together in a routine that takes a position, radius and number of circles as its argument.
```/target {
5 dict begin
/count exch def
/y exch def
/x exch def

count -1 1 {
/level exch def
gsave

level 1 sub count div setgray
x y radius level count div mul filledcircle
grestore
} for
end
}
def
```
Here is the output of `0 0 1 10 target`:
 5.9.1 Language Commands

David Maxwell, who is still writing this, would like to hear your comments and suggestions. And remember, parts of this manual are based on P.J. Weingartner's work: A First Guide to PostScript.