John
John

Reputation: 1673

PostScript - defining a namespace for a library

I've been coding in PostScript for a couple of months, and I'm wondering how one would implement namespaces, and use libraries.

As usual, the idea is to address:

I've come up a first sketch of what that might look like for me. The case I'm most interested in is that of defining a library for general use, and how, as a consumer of such a library, I would like to interact with its procs.

Below are two short experiments for an imaginary 'charting' library. Both code examples execute without error.

Q1: Do the code snippets below have a reasonable design? Is one to be preferred over the other?

Q2: The docs speak of NamedResources and ProcSets. Should those be used instead, for implementing libraries?

Experiment number 1:

%!PS-Adobe-3.0

% Experiment for defining a namespace in PostScript.

/pg-wd 720 def
/pg-ht 720 def
<</PageSize [pg-wd  pg-ht]>> setpagedevice  
/Times-Roman 15 selectfont

% PICTURE THIS AS BEING IN A SEPARATE FILE, AND BEING INCLUDED into your 
% program with a 'run' OR 'runlibfile' (Ghostscript).
%
% At the top level, the library defines only 1 proc.
% Let's call this the INSTALLATION PROC.
% It returns a dict with various procs for making charts.
/some-glorious-charting-library-dict {
  % push a temporary dict on top of the dict-stack, as a working area
  2 dict begin 
    % pass in a data-dict to each charting proc
    /pie {
      (Making a pie-chart...)==
      0 0 moveto
      (title)get show
    } def
    % pass in a data-dict
    /bar {
      (Making a bar-chart...)==
      0 0 moveto
      (title)get show
    } def
    % push a duplicate of this temp dict onto the operand stack
    currentdict 
  % pop this temp dict from the dict-stack, so its left only on the operand stack
  end
  % STRANGE AND WEIRD: when executed, this proc deletes itself from userdict!
  % There's no need to keep it around, since it's done its job.
  currentdict /some-glorious-charting-library-dict undef
} def

% At this point, there's an entry named /some-glorious-charting-library-dict in userdict.
currentdict /some-glorious-charting-library-dict known ==  % true

% "INSTALL" the library in userdict.
% Use a long name for two reasons:
%  - to avoid name collisions
%  - to encourage you to define a "convenience proc" (see below)
(installed-library-for-charts) some-glorious-charting-library-dict  def

% At this point, the entry named /some-glorious-charting-library-dict has been deleted:
currentdict /some-glorious-charting-library-dict known ==  % false
currentdict (installed-library-for-charts) known ==        % true

% Define a proc to allow curt access to the charting procs.
% Let's call this a CONVENIENCE PROC.
% This helper proc hides several things: 
%   - the long name
%   - the three operators needed to access the proc (exch, get, exec).
% data-dict chart-proc-name
/chart { 
  installed-library-for-charts exch get exec
} def


% draw a pie chart
gsave 
  10 100 translate
  << (title) (I am ten leagues deep in calamity.) >>   % a data-dict needed by the pie proc
  (pie) chart
grestore

% draw a bar chart
gsave
  10 200 translate
  % You can identify the proc with either a string literal '(bar)' or a name literal '/bar'.
  << (title) (Of things that move not, I am the Himalayas.) >> 
  /bar chart
grestore

10 10 moveto
(Done) show

showpage

Also: it's likely a good idea for the consumer of the library to make it readonly during the "installation" step, such that the library-procs can't be overwritten:

(installed-library-for-charts) some-glorious-charting-library-dict readonly def

Experiment number 2:

%!PS-Adobe-3.0

% The dictionary stack IS the namespace.
%
% As long as you're aware of the name-shadowing that can 
% occur, this is the simplest implementation.
% I'm not sure, but this style may be more in agreement 
% with the original intent of the language designers.

/pg-wd 720 def
/pg-ht 720 def
<</PageSize [pg-wd  pg-ht]>> setpagedevice  
/Times-Roman 15 selectfont

% PICTURE THIS AS BEING IN A SEPARATE FILE, AND BEING INCLUDED into your 
% program with a 'run' OR 'runlibfile' (Ghostscript).
%
% At the top level, the library defines only 1 proc.
% Let's call this the INSTALLATION PROC.
% It returns a dict with various procs for making charts.
/some-glorious-charting-library-dict {
  % push a temporary dict on top of the dict-stack, as a working area
  2 dict begin 
    % pass in a data-dict to each charting proc
    /pie-chart {
      (Making a pie-chart...)==
      0 0 moveto
      (title)get show
    } def
    % pass in a data-dict
    /bar-chart {
      (Making a bar-chart...)==
      0 0 moveto
      (title)get show
    } def
    % push a duplicate of this temp dict onto the operand stack
    currentdict 
  % pop this temp dict from the dict-stack, 
  % so its left only on the operand stack
  end
} def

% Just add the chart lib temporarily to the current 
% namespace (that is, to the dictionary stack).
% WARNING: a name in the library will shadow the same name in userdict.
some-glorious-charting-library-dict begin 
  gsave 
    10 100 translate
    << (title) (Hocus Pocus.) >>  pie-chart
  grestore
end

% you can choose to make the library proc's unwriteable:
some-glorious-charting-library-dict readonly begin 
  gsave 
    10 200 translate
    << (title) (Abracadabra.) >>  bar-chart
    %/bar-chart {0 0 moveto} store % this fails: readonly
  grestore
end

% The same scheme could be used to reference multiple libraries at once,
% by nesting begin-end pairs.

showpage

Upvotes: 2

Views: 35

Answers (0)

Related Questions