That matches what i got from a very partial reading of the PLRM. I
did pick up that there is some kind of name to user defined function
association thing but it never became clear in my cursory browsing.
Nor do i get the multiple stacks yet.
The main ones are the operand stack (i.e. "the stack"), and the dictionary
stack.
The operand stack is reasonably obvious; values are pushed onto the stack,
operators pop their arguments from the stack and push their results onto
it.
The dictionary stack determines the scope of names. Whenever the
interpreter needs to evaluate a name, it looks it up in the topmost
dictionary on the dictionary stack. If the lookup fails, it tries the next
one down, and so on until it either finds a match or it exhausts the
stack (at which point you get an "undefined" error).
If you bind a name with "def", it is added to the topmost dictionary on
the stack. At startup, there are two dictionaries on the stack: systemdict
at the bottom and userdict at the top. The standard operators are defined
in systemdict, which is read-only; userdict is where any user-defined
bindings go (unless additional dictionaries are pushed onto the
dictionary stack).
For complex functions, it's often simpler to start by pushing a new
dictionary onto the stack so that you can associate names with parameters
and intermediate results, rather than having to manipulate the operand
stack explicitly with dup, index, roll, etc. Using a new dictionary
ensures that you don't accidentally trash any bindings which were being
used by the caller; you just pop it off when you're done with it.
The other important stacks are the VM stack (the save and restore
operators save and restore the state of composite values such as arrays
and dictionaries) and the graphics stack (gsave and grestore save and
restore the state of the graphics engine, which allows elements of the
page to be encapsulated so that any state changes within each element
don't affect subsequent elements).