In PostScript, define a procedure to define other procedures
The goal of this question is to understand PostScript a little better from a programming perspective. The purpose described below is just an example used for illustration.
In PostScript language, I can define a routine to set the current graphic color as follows:
/cRED { 1 0 0 setrgbcolor } def % define a procedure to set the color to red.
I was wondering if there is a way to define a routine that will define other color routines. Suppose that such a procedure has been defined, called cdef
. I could use it like this:
/cRED 1 0 0 cdef
This should have the same effect as the previous definition of cRED. The problem is that I cannot figure out how to "grab" the literal value of the items on the stack in a procedure passed to def
.
I tried the following:
/cdef { /B exch def /G exch def /R exch def { R G B setrgbcolor } bind def } def
/cRED 1 0 0 cdef
/cGRN 0 1 0 cdef
/cBLU 0 0 1 cdef
I expected that using the bind
value R
G
and B
would be written literally. That is, I expected the above code to be equivalent to this:
/cRED { 1 0 0 setrgbcolor } def
/cGRN { 0 1 0 setrgbcolor } def
/cBLU { 0 0 1 setrgbcolor } def
Unfortunately, the actual result is that cRED
cGRN
, and cBLU
are all set color blue . This is because cRED
cGRN
and cBLU
still depends on objects R
G
and B
(which are global). And so the color is blue for everyone, because it cBLU
is determined by the latter by setting R
G
and B
. Apparently bind
didn't work as I expected.
Is there a way to define cdef
to achieve this? The crux of the problem is to pop the value off the stack and store it literally with def
. For example. something like this pseudocode:
/cdef { { $ $ $ setrgbcolor } bind def } def
Where $
woudl will be replaced with the literal value of the element at the top of the stack when evaluating cdef. So, it /cCYN 0 1 1 cdef
is evaluated as/cCYN { 0 1 1 setrgbcolor } bind def
Is there any statement out there that accomplishes the purpose $
as described above? Operators pop
, =
and index
are close, but don't seem to work. Also, the use of immediately evaluated names (for example //name
) seems promising, but they seem to be evaluated even before execution cdef
.
thank
source to share
Do this by rolling the parameters in place instead of assigning them
/cdef { [ 4 1 roll /setrgbcolor load ] cvx bind def } def
Thus, when executed ] cvx bind def
internally, it finds on the operand stack
/YourNameForTheProcedure
[ (i.e. mark)
your three parameters (the mark has been rolled below them)
the setrgbcolor operator (or procedure?)
Then the closure ]
will make an array of three numbers and setrgbcolor, which will be made into the procedure using cvx
.
Note: Then you must pass the parameters r, g, b in the correct order:
/CR 1 0 0 cdef /CG 0 1 0 cdef /CB 0 0 1 cdef
source to share
Stefan's answer is probably the best and easiest way for this case. But there are several more ways to create procedures in postscript.
Since you know the exact number of elements, you can skip the stack label with the label and do it like this:
/cdef { /setrgbcolor load 4 array astore cvx def } def
And the more complex way can be useful for more complex functions. You can define arguments and substitute definitions for a string template. I think this is what you were aiming for with immediately appreciated names. Executing the string returns the body of the procedure, but the scan and evaluation is done at runtime.
/cdef {
3 dict begin
{ b g r } { exch def } forall
({ //r //g //b setrgbcolor }) cvx exec
end
def
} def
And one more way, which is perhaps awkward, but very flexible.
/curry {
/exec cvx
3 array astore cvx
} def
/cdef {
{setrgbcolor} 3{curry}repeat
def
} def
This results in this procedure being defined for values 1 2 3:
{ 1 { 2 { 3 { setrgbcolor } exec } exec } exec }
So maybe a small loss in microefficiency. But it can be used for all kinds of things.
source to share