- 1. Dictionaries
- 1.1. Overview
- 1.2. Dictionary Characteristics
- 1.3. Dictionary Creation
- 1.4. Dictionary Operations
- 1.5. The Dictionary Stack
- 1.6. Standard Dictionaries
- 1.7. Dictionary Patterns
- 1.8. Dictionary Iteration
- 1.9. Dictionary Copying
- 1.10. Dictionary Lookup Optimization
- 1.11. Dictionary Scoping Patterns
- 1.12. Dictionary as Data Structure
- 1.13. Dictionary Performance
- 1.14. Dictionary Best Practices
- 1.15. Common Pitfalls
- 1.16. Advanced Dictionary Techniques
- 1.17. See Also
1. Dictionaries
Dictionaries are associative arrays that store key-value pairs. They are fundamental to PostScript’s name resolution system, variable scoping, and object-oriented features. Understanding dictionaries is essential for advanced PostScript programming.
1.1. Overview
A dictionary is a composite object that maps keys to values. Keys are typically names, but can be any object. Dictionaries provide fast lookup, dynamic binding, and hierarchical scoping through the dictionary stack.
1.2. Dictionary Characteristics
1.2.1. Associative Array
Dictionaries store pairs of keys and values:
<< /name (John) /age 30 >> % Dictionary with two entries
<< /x 42 /y 100 >> % Coordinate dictionary
<< >> % Empty dictionary
1.3. Dictionary Creation
1.3.1. Using dict Operator
The dict operator creates a dictionary with specified capacity:
10 dict % Create dictionary with capacity 10
0 dict % Empty dictionary (minimal capacity)
100 dict % Large dictionary
% Capacity vs length
10 dict
dup /a 1 put
dup /b 2 put
dup maxlength % Returns 10 (capacity)
length % Returns 2 (current entries)
1.3.2. Level 2 Dictionary Syntax
Level 2+ supports inline dictionary creation with << >>:
% Basic syntax
<< /key value >>
% Multiple entries
<< /x 10 /y 20 /z 30 >>
% Mixed types
<<
/name (PostScript)
/version 3
/active true
/data [1 2 3]
>>
% Nested dictionaries
<<
/outer <<
/inner 42
>>
>>
1.4. Dictionary Operations
1.4.1. Basic Operations
| Operator | Stack Effect | Description |
|---|---|---|
|
|
Create dictionary |
|
|
Get entry count |
|
|
Get capacity |
|
|
Push onto dict stack |
|
|
Pop from dict stack |
|
|
Define in current dict |
|
|
Look up value |
|
|
Store in dict on stack |
|
|
Get value from dict |
|
|
Put value in dict |
|
|
Test if key exists |
|
|
Remove key from dict |
% Create and populate
5 dict dup
dup /x 10 put
dup /y 20 put
% Access
dup /x get % Returns 10
dup /x known % Returns true
dup /z known % Returns false
% Modify
dup /x 99 put % Change x to 99
% Remove
dup /x undef % Delete x entry
1.4.2. Dictionary Stack Operations
| Operator | Stack Effect | Description |
|---|---|---|
|
|
Push dict onto dict stack |
|
|
Pop dict from dict stack |
|
|
Count dict stack depth |
|
|
Copy dict stack to array |
|
|
Get topmost dict |
% Stack depth
countdictstack % Returns initial depth
% Push dictionary
10 dict begin
countdictstack % Depth increased by 1
% Get current dict
currentdict /x 42 put
% Pop dictionary
end
countdictstack % Back to initial depth
1.5. The Dictionary Stack
1.5.1. Stack Structure
PostScript maintains a stack of dictionaries for name resolution:
Dictionary Stack (top to bottom):
┌─────────────────────┐
│ User Dictionary │ ← Most recent begin
├─────────────────────┤
│ Local Dictionary │
├─────────────────────┤
│ Global Dictionary │
├─────────────────────┤
│ User Dict │
├─────────────────────┤
│ System Dict │ ← Bottom (always present)
└─────────────────────┘
1.5.2. Name Resolution
When a name is executed, PostScript searches the dictionary stack top to bottom:
% systemdict has: /add → <operator>
% userdict is empty
% Lookup finds add in systemdict
add % Uses built-in operator
% Override in userdict
/add { mul } def % Define in current dict
% Now lookup finds add in userdict first
add % Uses redefined version (multiply)
% Original still in systemdict
systemdict /add get exec % Uses original add
1.6. Standard Dictionaries
1.6.1. systemdict
Contains all built-in operators and PostScript language features:
% Access systemdict
systemdict /add known % true
systemdict /moveto known % true
systemdict length % Large number (all operators)
% Get operator
systemdict /add get % Returns add operator
1.6.2. userdict
The default dictionary for user definitions:
% Definitions go in userdict by default
/myvar 42 def
% Equivalent to:
userdict /myvar 42 put
% Access
userdict /myvar get % Returns 42
1.6.3. globaldict (Level 2+)
Persistent dictionary across jobs:
% Check VM mode
currentglobal % false (local VM)
% Switch to global VM
true setglobal
globaldict /persistent 42 put
false setglobal
% Value persists across jobs
1.7. Dictionary Patterns
1.7.1. Local Variables
/MyProc {
% in: x y z
% Create local scope
3 dict begin
/z exch def
/y exch def
/x exch def
% Use local variables
x y add z mul
end
% out: result
} def
1 2 3 MyProc % Returns (1+2)*3 = 9
1.7.2. Configuration Dictionary
/Config <<
/PageWidth 612
/PageHeight 792
/Margin 72
/Font /Times-Roman
/FontSize 12
/Color [0 0 0]
>> def
% Access configuration
Config /PageWidth get % Returns 612
Config /Font get % Returns /Times-Roman
1.7.3. Object-Like Structures
/MakePoint {
% in: x y
% out: point-dict
<<
/x 3 -1 roll
/y 3 -1 roll
/distance {
% Closure over x and y
x dup mul y dup mul add sqrt
} bind
>>
} def
% Create point
10 20 MakePoint /pt exch def
% Access fields
pt /x get % Returns 10
pt /y get % Returns 20
% Call method
pt /distance get exec % Returns distance from origin
1.7.4. Namespace Management
% Create module dictionary
/MyModule 50 dict def
MyModule begin
% Private helpers (not exported)
/_helper { 2 mul } def
% Public interface
/PublicFunc {
_helper 1 add
} def
/PublicVar 42 def
end
% Use module
MyModule /PublicFunc get exec
MyModule /PublicVar get
% MyModule /_helper get % Can access but not intended
1.8. Dictionary Iteration
1.9. Dictionary Copying
1.9.1. Shallow Copy
/source << /a 1 /b 2 /c 3 >> def
% Method 1: Copy entries
source dup length dict exch {
3 copy pop put
} forall
% Method 2: Using copy (Level 2+)
source dup length dict copy
1.9.2. Merging Dictionaries
/Merge {
% in: dict1 dict2
% out: merged-dict
% Create result dict
2 copy length exch length add dict
% Copy first dictionary
3 1 roll {
3 copy pop put
} forall
% Copy second dictionary (may override)
exch {
3 copy pop put
} forall
} def
<< /a 1 /b 2 >> << /c 3 /d 4 >> Merge
% Result: << /a 1 /b 2 /c 3 /d 4 >>
1.10. Dictionary Lookup Optimization
1.10.1. where Operator
Find which dictionary contains a key:
% where: key → dict true | false
/add where {
% Found, dict is on stack
/add get type = % /operatortype
} {
% Not found
(Undefined) =
} ifelse
% Check custom definition
/myvar where {
pop /myvar get
} {
0 % Default value
} ifelse
1.10.2. Cached Lookups
% SLOW - repeated lookup
/MyProc {
/Config where { pop Config /Value get } if
% ... use Value multiple times ...
/Config where { pop Config /Value get } if
/Config where { pop Config /Value get } if
} def
% FAST - cache the value
/MyProc {
/Config where {
pop Config /Value get
dup dup % Use cached value
} if
} def
1.11. Dictionary Scoping Patterns
1.11.1. Save/Restore Pattern
% Save state
save
% Make temporary changes
/temp-dict 20 dict def
temp-dict begin
/x 100 def
/y 200 def
% ... use x and y ...
end
% Restore state (removes temp-dict)
restore
1.12. Dictionary as Data Structure
1.12.1. Hash Table Behavior
Dictionaries provide O(1) average lookup time:
% Create large lookup table
1000 dict begin
0 1 999 {
dup dup 100 string cvs
exch def
} for
currentdict end /lookup exch def
% Fast access
lookup (500) load % Quick lookup
1.13. Dictionary Performance
1.14. Dictionary Best Practices
1.14.1. Naming Conventions
% Use descriptive names
/PageWidth 612 def % GOOD
/pw 612 def % POOR
% Prefix private entries
/_internalHelper { } def % Private
/PublicAPI { } def % Public
% Use namespaces for modules
/MyModule.Init { } def
/MyModule.Process { } def
1.15. Common Pitfalls
1.15.1. Dictionary Stack Imbalance
% WRONG - missing end
10 dict begin
/x 42 def
% ... more code ...
% Dictionary stays on stack!
% RIGHT - balanced
10 dict begin
/x 42 def
end % Properly removed
1.15.2. Key Type Confusion
% These are DIFFERENT:
<< /name 42 >> /name get % Works: 42
<< /name 42 >> (name) get % Error: string ≠ name
% Keys must match exactly
/dict 10 dict def
dict /key 1 put
dict /key get % OK
dict (key) cvn get % OK (converted to name)
dict (key) get % ERROR
1.15.3. Scope Leakage
% WRONG - pollutes current dict
/GlobalProc {
/x 42 def % Defines in current dict
} def
% RIGHT - uses local dict
/LocalProc {
1 dict begin
/x 42 def % Local to this dict
x % Use it
end
} def
1.15.4. Dictionary Modification During Iteration
% DANGEROUS
<< /a 1 /b 2 /c 3 >> {
pop % Key
dup (remove) eq {
currentdict exch undef % Modifying during iteration!
} if
} forall
% SAFE - collect keys first
<< /a 1 /b 2 /c 3 >>
[ exch { pop } forall ] % Collect all keys
{ ... } forall % Now safe to modify
1.16. Advanced Dictionary Techniques
1.16.1. Method Dispatch
/Object <<
/type /MyObject
/data [1 2 3]
/Methods <<
/print {
(Object: ) print
Object /data get =
} bind
/size {
Object /data get length
} bind
>>
>> def
% Invoke method
Object /Methods get /print get exec
Object /Methods get /size get exec
1.16.2. Prototype-Based Inheritance
/Prototype <<
/sharedMethod { (From prototype) = } bind
>> def
/Instance <<
/prototype Prototype
/instanceData 42
>> def
% Lookup with fallback
/Lookup {
% in: object key
% out: value
2 copy known {
get
} {
exch /prototype get
exch Lookup % Recursive lookup
} ifelse
} def
Instance /sharedMethod Lookup exec
1.16.3. Memoization
/FibonacciMemo << >> def
/Fibonacci {
% in: n
% out: fib(n)
% Check cache
FibonacciMemo 1 index known {
FibonacciMemo exch get
} {
% Compute
dup 2 lt {
% Base case
} {
dup 1 sub Fibonacci
exch 2 sub Fibonacci
add
} ifelse
% Store in cache
dup
FibonacciMemo 3 1 roll put
} ifelse
} def
1.17. See Also
-
Objects - Dictionary object model
-
Arrays - Similar composite structure
-
Procedures - Local variables with dictionaries
-
Operators - Dictionary operators
-
Tokens - Name syntax for keys
-
dict - Create dictionaries
-
begin - Push onto dict stack
-
end - Pop from dict stack
-
def - Define entries
-
load - Look up values
-
where - Find definitions