I am a student in Istanbul American Robert College, and at the same time, one of the administrators of the Computer Labs in Faculty of Arts and Sciences in Istanbul Technical University. The over dominating operating system in these labs is LINUX. Interests: PovRay and PostScript, animation, CD design, programming, holography etc.. Linux user since 1994.
Drop after drop, it becomes a lakeThe author describes the operand stack of the PostScript language. The stack manipulations and mathematical operators are presented here. This article does not cover all properties of the operand stack. The future articles will continue to tell about it.
This is the second of a series of articles about PostScript. The main purpose of this article is to mention the stack operations. The operand stack is perhaps the most important part of PostScript. Assignments, arithmetical or mathematical operations, loops and logical operations are all handled in this specific piece of memory. Yes! The stack is a special memory region which is used by PostScript to execute almost everything we want PostScript to do. The stack keeps the information in an order such that the last entered data exits first. You can visualize this as a pipe whose one end is closed. When you put something into this pipe, it will push all existing things inside the pipe toward the closed end to make room for itself. So the last entered item will always be the closest one to the open end. The items in the stack can be strings, numerical constants, keys, blocks, ...
Although items are ordered in the stack, there are some stack operations which allow us to reorder the items. Manipulations are applied to one or more items in the stack. The operators, by definition, manipulate item(s) in the stack. They may or may not need parameters (operands in PostScript terminology) depending on the character of the manipulation. If they need the parameter(s) then these must be pushed into the stack first. The operator then takes action regarding these operands. Here we give a list of these operators with sufficient explanations. Then we give some examples to illustrate more details.
pop: This operator discards the topmost element (last entered) of the operand stack
exch: This operator exchanges the two topmost elements of the operand stack
dup: This operator creates a copy of the last entered element of the operand stack and pushes it into the operand stack. In other words, it duplicates the topmost element.
copy: This operator needs an integer operand (parameter) which must be given to the operand stack before the operator acts. If we say n for this integer parameter then the command will be given as n copy. When this is done, the copy of the set of n topmost elements will be created and located into the operand stack as the last entered elements. In other words, copy is a set duplication operator.
index: This operator needs an integer operand which must be given to the operand stack before the operator acts. If we say n for this integer parameter then the command will be given as n index. When this is done, the copy of n-th topmost element will be created and located into the operand stack as the last entered element. In other words, index can select an internal element and create and locate its copy into the operand stack. The indexing of the elements starts from the topmost element with 0.
roll: This operator needs two integer parameters which must be given to the operand stack before the operator acts. If we say m and n for these integer parameters then the command will be given as m n index. Here, m denotes how many elements will be rolled while n characterizes the number of the rolls. One roll is defined in such a way that the topmost element of the operand stack becomes the m-th element while the set of m-1 elements below the topmost element is shifted one place to top. This scheme is valid when n is equal to 1. If it would be 2 then two consecutive rolls will be occurred. In other words m 2 roll is same with m 1 roll m 1 roll. The parameter n can take negative values. If this occurs, it reverses the action that happens when n is positive. This means that the global effect of the command m n roll m -n roll is neutral. It leaves the operand stack unchanged. The indexing of the elements starts from the topmost element with 0.
clear: This operator discards all elements of the operand stack.
count: This operator counts the elements of the operand stack. The result is pushed to the operand stack as a new element. If you do not want this new element then you can issue the composite command count pstack pop where pop removes the new element created by count after the result of the counting is displayed through the action of the file operator pstack.
mark: This operator puts a marktype (-marktype-) element into the operand stack. This element can be used to divide the set of elements in the operand stack into subsets. Two other operators cleartomark and counttomark seek this element for their actions. If not found then an error message is issued.
cleartomark: This operator removes all elements from the topmost one to the first -marktype-. This -marktype- object is also removed. If there is no marktype object in the operand stack then an error message is issued.
counttomark: This operator counts the elements starting from the topmost element until the marktype object is encountered. The result which is an integer value is pushed into the operand stack as the last element. The first encountered marktype element is not included in the result. If there is no marktype element in the operand stack then PostScript complains and does nothing.
Let us now say something about the stack. If you want to see the above
operators in action, you must first activate
the PostScript interpreter. As mentioned in the first article, the Linux world uses a publicly available interpreter, ghostscript. Ghostscript can be activated in different ways by giving appropriate parameters in the command line. The usual way is just to enter gs in the X Window environment. Sometimes this may not work due to some configuration problems in X .An error message about the incapability for creating a convenient graphic console is displayed . Then these problems must be solved or you may force ghostscript to use the device x11. For this purpose you enter gs -sDEVICE=x11. This or just plain gs (if it works) creates a white background blank window which is used for displays to be created during the session. Since the displays are not involved in this article we do not need this window and it is possible to get rid of it. For this, the ghostscript interpreter can be activated without display by entering the -dNODISPLAY parameter in the command line after gs or gs -sDEVICE=x11. If this is done then a copyright header is displayed followed by the ghostscript prompt GS> at the beginning of a new line. At this point, ghostscript is ready for your commands. The operand stack is empty.
To see the content of the operand stack you can use a file operator, pstack. This is called a file operator because it sends information about the content of the stack to the standard output file which is the screen by default. If you enter this command at the prompt then nothing is displayed and a new GS> prompt appears at the beginning of the newline. This means that the operand stack is empty.
To enter elements into the operand stack you can enter the elements at the prompt. For example if you want to enter 1 as an element then you just enter 1 at the prompt. After this a new prompt is created at the beginning of the next line. But, this time, prompt is not GS>. It is GS<1>. This new form of the prompt reflects the number of the elements in the operand stack. So, if you have GS<123> at any moment in your ghostscript session then this means that there are 123 elements in the operand stack.
You can enter more than one element into the operand stack in one single attempt. For this, you should type all the elements consecutively ,but separated by a blank space. For example, if you enter 1 2 3 4 5 6 at the prompt then the elements 1, 2, 3, 4, 5, 6, are pushed into the operand stack respectively. If you issue pstack after this, then the elements are displayed in vertical order such that the last entered element is displayed first. The display of this two command session can be given as follows:
GS>1 2 3 4 5 6 GS<6>pstack 6 5 4 3 2 1 GS<6>It is also possible to enter elements into the operand stack and to see the contents of the stack in one single attempt. All you have to do is to give pstack command after the elements to be entered. That is:
GS>1 2 3 4 5 6 pstack 6 5 4 3 2 1 GS<6>Until now we used numbers for entering elements. Numbers are not the only type of elements, it is possible to enter other kind of elements like variables or keys, strings, blocks etc. We shall mention them later in detail. However we should say something now: If you attempt to enter for example, just a single character a or a string abc an error message is issued. This is because PostScript can not understand these things. If you want to enter a character or a string you have to encompass it by (). Here we talked about a special element type which is called marktype. To see this we can give the following session as an example:
GS>1 2 3 mark 4 5 6 pstack 6 5 4 -marktype- 3 2 1 GS<7>Now we are going to see some examples for the operators of operand stack manipulations. I give an example session to show how these operators act and I would like to close this section without any further explanation.
GS>1 2 3 mark 4 5 6 pstack 6 5 4 -marktype- 3 2 1 GS<7>pop pstack 5 4 -marktype 3 2 1 GS<6>exch pstack 4 5 -marktype 3 2 1 GS<6>dup pstack 4 4 5 -marktype- 3 2 1 GS<7>2 copy pstack 4 4 4 4 5 -marktype 3 2 1 GS<9>5 index pstack -marktype- 4 4 4 4 5 -marktype 3 2 1 GS<10>cleartomark cleartomark pstack 3 2 1 GS<3>3 1 roll pstack 2 1 3 GS<3>count pstack 3 2 1 3 GS<4>mark 7 8 pstack 8 7 -marktype- 3 2 1 3 GS<7>counttomark pstack 2 8 7 -marktype- 3 2 1 3 GS<8>clear pstack GS>
Aside from the manipulation operators on the operand stack of PostScript there are some arithmetical and mathematical operators as well. Below, these operators are given. There will be no examples. They are left up to the reader who should now be able to create them ,being equipped with the example session given above.
add: This command needs two numerical parameters whose values enter the addition operation. If these values are, say m and n, then the command is given as m n add. When this is done, first m then n is pushed into the operand stack. The action of the add operator onto these two topmost elements of the operand stack is the final stage. It creates a new element with the value which is equal to the sum of m and n. When the operation is completed m and n is not held in the operand stack. Instead, the result becomes the topmost element of the operand stack.
div: This command needs two numerical parameters whose values enter the division operation. If these values are, say m and n, then the command is given as m n div. The operation mechanism is same with the one for add. Division operation is at the floating point arithmetic level. After the operation is done only the result remains in the operand stack as a new element. m and n is not maintained.
idiv: This command needs two numerical parameters whose values enter the integer division operation. If these values are, say m and n, then the command is given as m n idiv. Everything is the same with div except the division level. It is an integer arithmetic division operation. If the parameters are not integers it works too.
mod: This command needs two integer parameters. It evaluates the remainder of the division of the first parameter by the second. If any one of the parameters is not an integer then it fails to work. The result is the only maintained value in the operand stack after the operation takes place.
mul: Same as add, div. It is a binary operator which needs two numerical values. The result is the multiplication of the parameters and is maintained as a new element in the operand stack.
sub: Same as add, div, mul. The only difference is the operation type. It subtracts the value of the second parameter from the value of the first. Parameters and the result are numerical values and the result is maintained in the operand stack after the operation is complete.
exp: This is a binary mathematical operator. It needs two parameters. The first is the base and the second is the exponent. It raises the value of the base to the power whose value is the exponent. The parameters must be within the limitations for the exponentiation operation. The result which is a floating point number that is maintained as a new element in the operand stack.
atan: This is another binary mathematical operator for the evaluation of an angle. The angle is given in degrees between 0 and 360. It needs two parameters. The ratio of the first parameter to the second equals the tangent of the angle to be evaluated. Any one of the parameters may be zero but both cannot be given zero values. The signs of the parameters determine the quadrant where the result will lie. The positive values in the first parameter corresponds to positive y plane. Whereas, the positive values in the second parameter mean positive x plane.
abs: This is a unary mathematical operator. It needs only one parameter whose absolute value is the result. Similarly as above, the result is maintained as a new element in the operand stack.
neg: This changes the sign of its only argument. It is a unary arithmetic operator.
ceiling: This is a unary operator which evaluates the integer value closest to its argument from above.
floor: This is a unary operator which evaluates the integer value closest to its argument from below.
round: This is a unary operator which evaluates the integer value closest to its argument.
truncate: This is a unary operator which removes the fractional part of its argument.
sqrt: This is a unary operator which evaluates the square root of its argument.
cos: This is a unary operator which evaluates the cosine of its argument. The argument is assumed to be given in degrees.
sin: This is a unary operator which evaluates the sine of its argument. The argument is assumed to be given in degrees.
ln: This is a unary operator which evaluates the natural logarithm of its argument.
log: This is a unary operator which evaluates the base-10 logarithm of its argument.
Before ending this article here is one more note. Although we mentioned above, perhaps implicitly, the parameter(s) (operands in PostScript terminology) of a command may create some unpleasant problems. The command (or operator in PostScript terminology) seeks its parameter(s) in the operand stack. When found they are used by the command and deleted from the operand stack. So, deliberately or by mistake, to give a command which needs parameter(s) onto the operand stack without parameter values, will cause either a complaintabout the parameter(s) which are some topmost elements of the operand stack if they are not suitable for the parameter type, or some top element(s) of the operand stack will be removed. The user must be very careful about this point.
Before the completion of this presentation we recommend to write more complicated and comprehensive programs for the users who want to proceed on PostScript. In the following articles of these series more details will be given about the PostScript language. All questions and comments are welcome for our presentations.
Reviewed by Jorge M. Paulo and Jose Quesada