Back-assward languages have, as a central concept, the use of a stack. Think of a stack of books. You can easily put a book on top of the stack, or remove one from the top. Removing or inserting books elsewhere is a pain in the ass, especially if the stack is tall and the book of interest is near the bottom of the stack. In a back-assward language, you never write an expression like "2 + 2", instead you write "2 2 add".
The overriding design principle in back-assward languages is "if it was good enough for the neanderthals, then it is good enough for us". The life of a back-assward interpreter is easy, it pulls something off the top of the stack, figures out what to do with it, and repeats the process. Your job as a back-assward programmer, is to make sure whatever operands the interpreter will need are on top of the stack, in the right order. Once this is done, you put the operator on the stack and hand the mess to the interpreter.
Postscript has 262 different operators. We probably won't cover them all here (this is a tutorial, not a reference manual).
2 2 plus 3 mul
The currentpoint operator places the x and y position of the current location on the stack.
/dog 10 def /dog dog 1 add defDefining a new procedure has much the same flavor, the only difference being that you wrap the body of the procedure in curly brackets:
/cube { dup dup mul mul } defThe curly bracket syntax is actually an "executable array", which is a single postscript object (and which is used in other situations, such as loops).
The default unit of measure in postscript is what you may choose to call "postscript points", of which there are exactly 72 in an inch. They are called "points" due to their accidental similarity to printers points (such as used in TeX), of which there are 72.27 in an inch. TeX calls the latter points (pt), and the former "big points" (bp) in its very meticulous scheme of things. You can probably forget about all of this now, but it is good to know about in case some typesetting purist takes you to task someday.
The default postscript coordinate system has its origin at the bottom left corner of the page. Commands exist to translate, rotate, and scale the postscript coordinate system.
Postscript has a graphics state stack (limited to 32 entries) that can be used to save and restore the current graphics state. The current path is part of the graphics state, and the goal of doing a graphics state save is often to save the current path so it can later be restored. Notably, the fill operation consumes the current path.
(this is a string)You can enclose balanced parenthesis in such a string without doing anything special; otherwise you use the backslash to escape them (and to do other special things).
It is also possible to dynamically create strings. You create an emptry string of a certain size (filled with null characters using the "string" operator:
10 stringYou encode values into such a string using the cvs operator, this works for any kind of postscript object.
/value 7 def value 10 string cvsTo find out how big a string will be (which can be useful if you want to do something like center it), you can use the stringwidth operator, which replaces the string with the width and height of the string shown using the current font.
/Times-Roman findfont 10 scalefont setfont (Hello World!) show showpagePostscript is quite clever about how it does font scaling, and you are expected to scale fonts to the size you need.
It is up to you to use "moveto" to start the display of each string. If you want to move to a new line, you decide how to get there and how much space should exist between lines. I have been pleased by moving down 1.25 times the font size, but you can do whatever you want.
1 1 10 { 3 string cvs show ( ) show } forThis would do the same:
/i 1 def { i 3 string cvs show ( ) show i 10 ge { exit } if /i i 1 add def } loop
I ran across this link which demonstrates a couple of nice postscript programs. I reproduce them here, lest they become lost someday.
The first makes use of executable arrays and the "exec" operator:
%!PS-Adobe-3.0 EPSF-3.0 %%BoundingBox: 0 0 170 50 % load standard font 24 /TimesBold findfont exch scalefont setfont .25 setlinewidth 1 setgray % white gsave % push graphic state { % push lambda expression gsave % push graphic state (transformations) dup % duplicate string 0 0 moveto show % print filled 0 setgray % set black 0 0 moveto true charpath stroke % print outlined grestore % pop graphic state } .6 .05 1.1 { % loop condition setgray % using loop iterator .8 dup 13 dup translate scale % matrix transformation -4 rotate dup exec % execute expression } for % loop operator grestore % pop graphic state exec % finally pop and execute 0 setgray % black [-1 0 0 1 150 30] concat % matrix transformation (affine) 0 0 moveto show % print showpage %%EOFThe second produces part of the Koch snowflake using recursion:
%!PS-Adobe-3.0 EPSF-3.0 /koch { dup depth le { % s < depth 1 add exch % s => s+1 3 div exch % t => t/3 % prepare stack: % t/3 s+1 60 t/3 s+1 240 t/3 s+1 60 t/3 s+1 60 3 copy 180 add 3 copy 180 sub 3 copy pop koch rotate koch rotate koch rotate koch } { pop 0 rlineto } % draw ifelse } def 4.0 4.0 scale /depth 5 def newpath 20 100 moveto 100 1 koch stroke showpage
Tom's Computer Info / [email protected]