I am not sure I understand the semantics of Postscript dictionaries. What is the difference if a 'def' is used when defining an entry to a dictionary as opposed to just listing the key value pair. Below, I define /a and /b in dictionary /d. /a is defined using 'def'; /b is defined using a ky value pair. When printing out /d with the '===' operator, we only see /b. If we load /d onto the dictionary stack, we can access and print both /a and /b. If we do a get on /d /b we get back the value of /b. But if we do a get on /d /a, we get an undefined error.
Could someone help me understand why /a is not defined/visable using 'get' on dictionary /d, why it does not show up when printing /d and why it is accessible/defined when we load /d onto the dictionary stack?
The definition of the 'def' operator is pretty straightforward:
def - associates key with value in the current dictionary—the one on the top of the dic- tionary stack (see Section 3.4, “Stacks”). If key is already present in the current dictionary, def simply replaces its value; otherwise, def creates a new entry for key and stores value with it. - p.568 Postscript Language Reference 3rd edition
In this case, I see the currentdict at the time of defining /a as being /d. But 'get' can't see it. However, it is in the dictionary as when you load the dictionary onto the dictionary stack, it is defined.
/d <<
/a 22 def
/b 33
>> def % Define dictionary 'd'
d === % Print out contents of d; Prints /b 33 but no /a
d begin % Push d onto the dictionary stack
a = % Prints value of d/a '22'
b = % Prints value of d/b '33'
end % Pop d off of the dictionary stack
d /b get = % Prints value of d/b '33'
d /a get = % Error /undefine. Cannot find /a in d
Output (using ghostscript):
$ gs -q -dALLOWPSTRANSPARENCY -dBATCH -dNOPAUSE -dQUIET -dNOSAFER -sDEVICE=pdfwrite -o tmp_test.pdf tmp_test.ps
<< /b 33 >>
22
33
33
Error: /undefined in --get--
Operand stack:
--dict:1/1(L)-- a
Execution stack:
%interp_exit .runexec2 --nostringval-- --nostringval-- --nostringval-- 2 %stopped_push --nostringval-- --nostringval-- --nostringval-- false 1 %stopped_push 1990 1 3 %oparray_pop 1989 1 3 %oparray_pop 1977 1 3 %oparray_pop 1833 1 3 %oparray_pop --nostringval-- %errorexec_pop .runexec2 --nostringval-- --nostringval-- --nostringval-- 2 %stopped_push --nostringval--
Dictionary stack:
--dict:795/1123(ro)(G)-- --dict:0/20(G)-- --dict:77/200(L)--
Current allocation mode is local
Current file position is 317
GPL Ghostscript 10.01.2: Unrecoverable error, exit code 1
$
def
defines a key value pair in the current dictionary. There is always a dictionary which is 'current', it's whatever is at the top of the dictionary stack. Initially this will be 'userdict' but if you 'begin' another dictionary it will be pushed onto the top of the dictionary stack and will become the current dictionary.
You need to understand that PostScript is a stack based interpreted language, not a compiled language. When you do:
/a 22
What you are actually doing is putting two objects on the operand stack; the name object /a
and the integer value 22.
You then execute the def
operator which takes two objects from the stack and makes a key/value pair in the current dictionary.
Your code:
/d <<
/a 22 def
/b 33
>> def % Define dictionary 'd'
Starts by placing the name object /d
on the operand stack, then it places a mark object on the operand stack (<<
is a mark, this particular form of mark is used to introduce a dictionary, but you could also use [
or even just mark
, the different mark types are essentially identical)
Then you put two more objects on the stack, /a
and 22. Then you execute the def
operator.
That immediately takes two objects from the stack and defines them as a key/value pair in the current dictionary (probably userdict).
NOTE it does not define them in the dictionary which you are in the middle of constructing, which is what you seem to be expecting. The interpreter cannot know that you are constructing a dictionary, it just executes tokens as it encounters them, it can't look ahead.
Then you put two more objects on the stack, /b
and 23, and a closing mark >>
which is actually an operator. That counts back along the stack to find the most recent mark, checks that the number of objects is a multiple of two, and then removes pairs one at a time and places them in a new dictionary. The new dictionary is left on the operand stack.
Then you execute the def
operator, which looks at the stack, finds two objects, /d
and a dictionary, and creates a key/value pair in the current dictionary, again most likely userdict.
The overall effect is to modify the current dictionary by adding two key/value pairs:
/a 22
/d <</b 23>>
You then do:
d ===
You should avoid using ===
as that's a Ghostscript-specific operator and you won't find it in any other interpreter. You can dump a dictionary by doing:
{== ==} forall
But executing d
causes the interpreter to search the current dictionary for that name, which it finds (because you def
ined it). It pushes that on the operand stack. ===
then prints out that dictionary.
You then do:
d begin
which searches for d
, finds a key/value pair with that key, and pushes the value onto the operand stack, leaving the dictionary d
on the operand stack.
You then do :
a =
So the interpreter searches the dictionaries on the dictionary stack looking for a key /a
. It doesn't find it in the current dictionary, so it searches the next dictionary back on the stack, which will be the one that was current when you defined a
, probably userdict. It finds the key/value pair, and pushes the value onto the operand stack. You then =
it and get the output 22.
Similarly for b
except that it is defined in the current dictionary so we don't need to search back on the stack.
Then you do :
d /b get =
So the interpreter searches the current dictionaries for an object with the name d
, finds one and pushes it on the operand stack, then you put the name object /b
on the operand stack, and execute the 'get' operator.
get
checks the operands to see that the first one is a dictionary, and if it is, looks for the name in that dictionary. Finds it, and returns it on the operand stack. Finally =
prints it out.
You then do
d /a get =
Skipping all the same stuff as above up to the execution of get
, that looks for the name /a
in the dictionary and doesn't find it. At that point it gives you an /undefined
error in 'get'.