Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In PostScript, define a procedure to define other procedures

Tags:

postscript

The purpose of this question is to understand PostScript a bit better from a programming perspective. The goal described below is just an example used for illustration.

In the PostScript language, I can define a procedure to set the current graphics color as follows:

/cRED { 1 0 0 setrgbcolor } def  % define a procedure to set the color to red.

I was wondering if there was a way to define a procedure that would define other color procedures. Assume that such a procedure called cdef were defined. I could use it as follows:

/cRED 1 0 0 cdef

This should have the same effect as the previous definition of cRED. The problem is that I can't seem to figure out how to "capture" the literal value of 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

My expectation was that by using bind the values of R G and B would be captured literally. I.e. I was expecting the code above 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 all set the color to blue. This is because cRED cGRN and cBLU are still dependent on the R G and B objects (which are globals). And so the color is blue for all because cBLU defined last, setting R G and B. Apparently bind didn't work the way I expected.

Is there a way to define cdef to achieve this? The crux of the issue is being able to pop a value off the stack and store it literally using def. E.g. something like this pseudo-code:

/cdef { { $ $ $ setrgbcolor } bind def } def

Where $ woudl be replaced with the literal value of the item at the top of the stack when cdef is being evaluated. So /cCYN 0 1 1 cdef is evaluated as /cCYN { 0 1 1 setrgbcolor } bind def

Is there some operator that fulfills the purpose of the $ as described above? The operators pop, =, and index are close, but don't seem to work. In addition, the use of Immediately Evaluated Names (e.g. //name) seems promising, but they seem to be evaluated even before cdef is executed.

Thanks

like image 284
drwatsoncode Avatar asked Dec 10 '25 12:12

drwatsoncode


1 Answers

Stefan's answer is probably the best and simplest way for this case. But there are many more ways to construct procedures in postscript.

Since you know the exact number of elements, you can skip the stack juggling with the mark and do it like this:

/cdef { /setrgbcolor load 4 array astore cvx def } def

And a more complicated way might be useful for more complicated functions. You can define the arguments and substitute definitions into a string template. I think this is what you were striving toward with the immediately-evaluated names. Executing the string yield the procedure body, but the scanning and evaluation is performed at run-time.

/cdef { 
    3 dict begin
        { b g r } { exch def } forall
        ({ //r //g //b setrgbcolor }) cvx exec
    end
    def
} def

And another way, which is perhaps clumsy 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 the values 1 2 3:

{ 1 { 2 { 3 { setrgbcolor } exec } exec } exec }

So, perhaps some slight loss of micro-efficiency. But it could be used for all sorts of things.

like image 156
luser droog Avatar answered Dec 12 '25 08:12

luser droog



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!