anastigmatix.net

This document has a standard, validated CSS2 stylesheet, which your browser does not seem to display properly. In a browser supporting web standards, this table of contents would be fixed at the side of the page for easy reference.

anastigmatix home
  • MetaPre: Staged PostScript
  • Quick intro by example
  • Application: performance, simplicity
  • Application: modularity
  • Another intro, by concept
  • Concepts mapped to PostScript
  • Bracket
  • Run
  • Escape
  • Escape height
  • Oversimplification corrected
  • MetaPre Reference
  • MetaPre dictionary contents
  • Staging
  • metapre
  • metabind
  • Stack protection
  • hide
  • hide+ap
  • hide+k
  • hvhide
  • hvhide+ap
  • hvhide+k
  • Control flow
  • if:
  • else:if
  • else:
  • :if
  • :and
  • :or
  • xforall
  • ingroups
  • Miscellany
  • deq
  • enq
  • errorstop
  • export
  • fix
  • MetaPre: Staged Programming in the PostScript language

    PostScript programs can see themselves. As in languages like Scheme and ML, a procedure in PostScript is also data that can be examined, manipulated, or created on the fly by another procedure.

    At first blush that may sound like esoteric hackery, but PostScript programmers already commonly use it in some simple ways. MetaPre subsumes those ad hoc techniques to give any PostScript interpreter a simple syntax for writing programs in stages. The name plays on other staged programming languages like MetaML, allowing that MetaPost was already taken by John Hobby's fine graphics language, and MetaPre works a bit like a preprocessor, anyway.

    A quick introduction by example

    Here is a simple technique sometimes seen in existing PostScript programs. A procedure may begin a private dictionary in order to have local named variables. If the procedure does not need to be reentrant, there is no need to create a new dictionary on each call. The code below creates a dictionary once and places it in the procedure:

    /myproc {
      DUMMY begin
      /foo 42 store
      /bar 7 store
      foo bar add
      end
    } bind def
    /myproc load 0 << /foo 0 /bar 0 >> put
    

    The last line replaces the placeholder DUMMY, knowing it is index zero in the array, with a private dictionary allocated only once in advance. The technique is simple enough, but gets harder to read if replacing more than one element, and you have to count to know the right indices, and count again when something changes. It also won't work if array packing is enabled, because a packed array can't be stored into.

    If you were able to write something like:

    /myproc {
      -|[ << /foo 0 /bar 0 >> ]|- begin
      /foo 42 store
      /bar 7 store
      foo bar add
      end
    } metabind def
    

    you would have a language for staged programming, where ad hoc messing with arrays has given way to a simple syntax for procedures with parts that are filled in at different stages of execution. In this example, the hole before the begin is filled in with a dictionary allocated once at the time the procedure is defined. Staged programming turns out to be an idea made for PostScript: easy to implement because so much is already there (and because PostScript has no static typing, which is where interesting staged-programming research issues lurk), and an elegant approach to some of the less elegant corners in PostScript programming.

    Applications: performance and simplicity

    A traditional appeal for staged programming is to performance. Here is a procedure Rot2 that takes two points and rotates them about the origin by a given angle:

    /Rot2 {  % x0 y0 x1 y1 ang *rot2* x0' y0' x1' y1'
      matrix rotate
      5 -2 roll 2 index transform
      5 2 roll transform
    } bind def
    

    What if there will be many pairs of points all to be rotated by an angle known in advance? Rot2 recomputes the transformation matrix for each pair of points. How about a procedure MakeRot2 that takes an angle and returns a procedure that will rotate any pair of points by that angle? Now the computation is staged: the matrix is computed at one stage when we have the angle; the transformations are done in the next stage when we have the points.

    /MakeRot2 {  % ang *MakeRot2*  x0 y0 x1 y1 => x0' y0' x1' y1'
      {
        4 2 roll -| matrix rotate [ 1 index ] |- transform
        4 2 roll -|[ exch ]|- transform
      }
    } metabind def
    

    For any angle ang, MakeRot2 will produce the following procedure:

    { 4 2 roll M transform 4 2 roll M transform }
    

    where M is the precomputed transformation matrix. Not only has the computation of the matrix been moved out of the procedure, but the stack manipulations are simpler: there is no need to keep track of M on the stack along with the four coordinate values, because M is simply wired into the procedure in the right places. PostScript is a language that puts unusual emphasis on stack manipulation, and this potential to keep the stack cleaner is, on top of the performance possibilities, one of the reasons staged programming is especially useful in PostScript. Staged code can be not only more efficient, but more readable.

    As a hint for reading MakeRot2, shift your eyes' focus to only what you see between -| and |-, imagining a consume at every |- like this:

    matrix rotate [ 1 index ] consume [ exch ] consume
    

    All of that happens in the first stage, at the time MakeRot2 is applied to the angle ang, where consume means an array on top of the stack gets consumed—think pop. You can convince yourself it produced two arrays, each containing a copy of M. The arrays got consumed by splicing their contents into the procedure being generated, which is everything you see when your eyes focus outside the -| and |-:

    { 4 2 roll -|...|- transform 4 2 roll -|...|- transform }
    

    If you think of the -|...|-s as “holes” in the procedure being generated, then what you see through the holes is a consistent story of stack manipulations and computations going on at the time the procedure is being built, and the rest—what you see with the holes filled in—is the computation that will go on when the procedure is used.

    Interestingly, the language Forth, a much older and less sophisticated progenitor of PostScript, had a way to intermix compilation and interpretation states for a similar effect.

    Applications: modularity

    PostScript supports the distribution of reusable code in the form of resources, which can be managed in an extensible naming scheme using operators built into the language. A new resource is distributed as a PostScript program that ultimately invokes defineresource to register its category and name, and is well-behaved as set out in the language specification, leaving stacks as it found them and having no effect on state other than memory consumption and resource registration. MetaPre itself is distributed in this form.

    A program that is a client of the resource can use findresource to obtain a single object, often a dictionary containing the definitions the resource makes available to the client. Unless the client pushes this dictionary on the dictionary stack, there can be no clash between names used in one resource and those used in another, or otherwise available to the program. Such a separation of name spaces becomes especially important in ambitious programs that may import many different resources.

    If a resource uses many internal definitions that its procedures refer to by name, its dictionary will be cluttered with many definitions of no use to the client and possibly conflicting with other names in use, and the client will be forced to have it on the dictionary stack in order for the resource's procedures to work. Also, the resource's procedures may misbehave if names of internal definitions they rely on match other definitions earlier on the dictionary stack. If the resource is coded with internal references pre-resolved, those name lookups are avoided. Speed is not the issue, because PostScript name lookup is extremely fast, but the risks of name conflicts and unforeseen interactions can be minimized this way. The client is then able to use the resource's procedures by finding them with get in the dictionary returned by findresource and execing them, without ever putting the dictionary on the lookup stack.

    A resource may be loaded in global or persistent VM. If it offers settings to be chosen by the client, they should not be made directly in the global data structures—and possibly cannot be made there, if they involve local objects allocated by the client, which cannot be stored in global structures. One solution can be a staged design where the dictionary returned by findresource contains only a single procedure the client will call to generate a fresh local dictionary containing procedures specialized to the client's settings. The design can be space-efficient because the generated, exported procedures are only stubs made mostly of pre-resolved references to global, internal definitions.

    Another introduction, by concept

    A good introduction to the concepts and benefits of staged programming (notwithstanding some typos) is Tim Sheard's paper, Using MetaML. It introduces three fundamental concepts in a staged language:

    bracket
    A construct that delays execution. If the source text 5-cbrt(27) performs a function call and a subtraction and produces 2, the source text <5-cbrt(27)> produces a fragment of program code that will, if run, perform a call and a subtraction and produce 2. And the text <<5-cbrt(27)>> produces a fragment of code that, when run, will produce that fragment, which, when run, will produce 2.
    run
    The construct that reverses the effect of brackets, taking a fragment of code representing an operation, and performing that operation.
    escape
    A construct only valid inside delayed (bracketed) code, marking an expression that should not be delayed. Instead, the code fragment is created from the delayed code for everything between the brackets that is not escaped, but the escaped expression is run at that time and its result is copied, in its place, into the code fragment being generated. If ~ represents escape, then <5-~cbrt(27)> executes cbrt immediately, obtaining 3, and produces delayed code that will subtract 3 from 5 when run, producing 2. The result of an escaped expression, however, can be code of any length, which is spliced at the proper point into the code being generated.

    Mapping the concepts to PostScript

    To add staging to languages that have static typing involves challenging research questions: how to guarantee that a well-typed program at one stage generates a well-typed program for the next? Because PostScript is only dynamically typed, it can be staged on the cheap; the hard questions are left to the statically typed languages. And the natural affinity of PostScript for staged programming lies in that bracket and run are already in the language! Only an escape notation needs to be supplied.

    Bracket

    PostScript's curly brackets are exactly the delay notation needed for a staged programming language. This may not be obvious at first glance, because many programmers accustomed to languages like Java write and format PostScript strictly as if the brackets were no different from compound statement notation in those other languages, and the programs work, which seems to confirm the idea:

        foo bar eq {	    if ( foo == bar ) {
          doSomething	      doSomething
        } { 		    } else {
          doSomethingElse	      doSomethingElse
        } ifelse		    }
    

    That impression can be shaken loose by doing something in PostScript you'll never see in Java:

        { doSomethingElse } foo bar eq { doSomething } 3 -1 roll ifelse
    

    In that example it is easy to see that ifelse is simply an operator that takes three values on the stack, and two of those values are delayed code. It discards one, and executes the other. Another way to see the point, if it needs reinforcement, is to see the curly brackets in the Java example are unnecessary: meaning does not change when Java brackets are put around doSomething to make a compound statement of it. Meaning does change in PostScript from doSomething to { doSomething }: the first will do something, but the second will push a piece of code on the stack to do something later.

    Once it is clear that PostScript curly brackets are exactly delay brackets, another interesting fact falls out: PostScript has no syntax for grouping except for delay brackets. As a consequence, PostScript programs have many uses of delay brackets that have nothing to do with deliberate staging, just because they are the only way to write a simple conditional or loop, and that motivates some extra flexibility you will see in MetaPre's escape notation.

    Run

    PostScript also has the operator corresponding to run. It is, of course, exec, with exactly the effect its code argument would have with one outer pair of delay brackets eliminated. Of course there are many other operators in PostScript that have the effect of exec as part of what they do, starting with all the conditionals and loops, but exec is the operator that purely undoes the effect of delay brackets and is the exact counterpart of run.

    Escape

    The piece that completes the staged programming environment is the escape notation, and this is the piece that MetaPre provides. Within a bracketed code sequence, any code between -| and |- is not delayed. When exactly is the escaped code executed? The best answer, and only slightly oversimplified, is exactly when the bracketed code immediately containing it is pushed on the stack:

    { (Pushed: ) print -| (hi!) = [realtime] |- = (Executed: ) print realtime = }
    

    When PostScript encounters the procedure above, the effect will be to push it on the stack. At that time, the escaped sequence (hi!) = [realtime] is executed, printing hi! and producing an array. The array's contents—a single integer—will be spliced into the procedure being pushed. The procedure as it actually winds up on the stack looks like this:

    { (Pushed: ) print R = (Executed: ) print realtime = }
    

    where R is the time the procedure was pushed. If it is then exec'd at some later time, it will print:

    Pushed: R
    Executed: X
    

    where X is the time of execution.

    What happens if more delay brackets are present?

    {
      -|[realtime]|-
      {
        (Inner pushed: ) = -|[realtime]|- =
        (Outer pushed: ) = =
        (Executed: ) print realtime =
      }
    }
    

    The interpreter will push this code on the stack, executing the first escaped sequence as before. The second escaped sequence is not yet executed, because the bracketed code immediately containing it is not being pushed yet. The stack now contains (where Q is the current time):

    {
      Q
      {
        (Inner pushed: ) = -|[realtime]|- =
        (Outer pushed: ) = =
        (Executed: ) print realtime =
      }
    }
    

    Following one exec, the stack will contain (where R is the time of that exec):

    Q { (Inner pushed: ) = R = (Outer pushed: ) = =
        (Executed: ) print realtime = }
    

    Another exec will produce the output:

    Inner pushed: R
    Outer pushed: Q
    Executed: X
    
    Escape height

    Here is another route to the same output:

    {
      {
        (Inner pushed: ) =  -|[realtime]|- =
        (Outer pushed: ) = -1|[realtime]|- =
        (Executed: ) print realtime =
      }
    }
    

    Here both escapes are contained in the inner, nested bracketed code, but they are substituted at different times. Corresponding to the depth of bracket nesting, each escape has a height. The first escape above has height zero and, just as before, it is not executed until its immediately containing bracketed code is pushed on the stack. But the escape with height one is executed at the time its next outer containing code is pushed. So, when the interpreter first pushes the procedure above, what gets pushed is:

    {
      {
        (Inner pushed: ) = -|[realtime]|- =
        (Outer pushed: ) = Q =
        (Executed: ) print realtime =
      }
    }
    

    After one exec, the stack contains:

    {
      (Inner pushed: ) = R =
      (Outer pushed: ) = Q =
      (Executed: ) print realtime =
    }
    

    One more exec produces output matching the earlier example. The notation for escape height is important in PostScript because the structure of the language requires delay brackets for non-staging purposes like conditionals and loops, so that many places in the same procedure where you might want to place escapes will actually be at different depths of delay nesting:

    {
      -| (The ) [] |-
      rand 16#3fffffff le {
        -1| (same ) [] |-
        1 {
          -2| exch print print (stage.) = [] |-
        } repeat
      } if
    }
    

    The stage at which each escape will be executed is the depth of enclosing delay brackets minus the escape's height; all of the escapes in the above code are executed at the same stage. A consistent indentation scheme for curly brackets can simplify getting the escape heights right.

    The example also shows information propagated from one escaped sequence to another on the stack. Each escaped sequence is expected to leave an array on top of the stack, containing the code to splice into the procedure being generated, and that array will be consumed, but other stack effects are unrestricted. Escaped sequences at the same stage are executed in a left-to-right, depth-first traversal; the leftmost sees the stack on which the procedure under construction was to be pushed, and each leaves the stack as the next will see it.

    The oversimplification corrected

    The rule of thumb that escapes (of height zero) are expanded at the moment their immediately-containing bracketed code is pushed on the stack is conceptually right, but missing one practical detail. MetaPre does not actually hook PostScript's scanner to process procedures as they are scanned and placed on the stack. Instead, a procedure that contains escapes (at any depth of nesting) must be followed by metapre:

    {
      {
        (Inner pushed: ) =  -|[realtime]|- =
        (Outer pushed: ) = -1|[realtime]|- =
        (Executed: ) print realtime =
      }
    } metapre
    

    In reality, then, PostScript's scanner first pushes the procedure on the stack just as it was read in—to the scanner, MetaPre's escape delimiters are merely names—and then metapre, like the operator bind, leaves the preprocessed version on the stack in its place. As with bind, only one metapre is needed after the outermost closing bracket; it makes the necessary arrangements for any nested code with later-stage escapes to be expanded later when pushed on the stack. Because bind and metapre will often be applied together at the end of a procedure body, as a convenience metabind does both.

    MetaPre Reference

    MetaPre is a ProcSet resource. To make it available to your own code, include in the setup section of your file:

    /net.anastigmatix.MetaPre /ProcSet findresource begin
    

    The findresource will succeed if you have made the MetaPre resource file [download] available in any of these ways:

    The resource file is in a compact form. That is for efficiency, not to keep you from viewing it; there is a script for that on the resource packaging page.

    The MetaPre dictionary is read-only. Before creating any definitions, you will want either
    userdict begin or your own dict begin so that you have a writable dictionary on top of the dictionary stack.

    If the code you are writing will itself be distributed as a resource, be sure to finish with the right number of ends to leave the dictionary stack as it was before MetaPre and your own dictionary were put on it. Also consider using immediately-evaluated names to refer to MetaPre definitions inside of procedures you create: instead of /foo { 3 hide } def, you would write /foo { 3 //hide exec } def. This will allow your code to work whether or not the MetaPre dictionary is on the lookup stack at run time. This applies only to references to MetaPre definitions inside your procedures; there is no point in doing anything special with metapre or metabind (or anything else) used outside brackets, or to the -|, -n|, and |- escape delimiters in your procedures.

    MetaPre dictionary contents

    Staging

    metapre
    proc metapre proc'

    Scans proc left to right, applying itself recursively to nested procedure objects (depth first), looking for escaped sequences, that is, sequences of code delimited on the left by an executable name -| or -h| where h is a positive integer and -| is equivalent to -0|, and on the right by the executable name |-. metapre immediately executes, in the current environment, every escaped sequence whose height h equals its depth in procedure nesting, where proc itself is at depth 0. Each escaped sequence is expected to leave an array result on top of the stack, which is consumed and whose contents are spliced into the containing procedure in place of the escaped sequence and its delimiters. The code just spliced is then scanned itself as if it had been newly pushed at depth 0. proc is removed from the stack while escaped sequences are executed, and the stack is otherwise left alone, so the first escaped sequence sees the stack as it was when metapre was invoked (but with proc removed), and each subsequent sequence sees the stack left by the prior one (but with the array result consumed).

    For every escaped sequence found with height h less than its depth, metapre finds the hth enclosing procedure p and the procedure q enclosing p, and inserts a resolved reference to metapre into q immediately following p, so that when p is later pushed on the stack, metapre will process it and expand the escaped sequences intended for that stage. The resolved reference to metapre is made execute-only as a convenience, so procedures incorporating metapre can be usefully printed with == without getting lost in metapre itself. When metapre inserts a metapre reference after a procedure p, the p reference is made literal, so that intervening metapre passes will not unnecessarily recurse into it. At the time p is pushed on the stack, the following metapre will make it executable again.

    Because metapre may be invoked on packed or read-only arrays, and the expansion of escaped sequences is likely to change lengths, it is not safe to assume that proc' is the same array as proc, or to make that assumption for any recursively contained procedure, unless it and its descendants contain no escapes.

    metabind
    proc metabind proc'

    Because bind and metapre will commonly both be applied to new procedures as they are defined, as a convenience metabind is equivalent to bind metapre.

    Stack protection

    It is often useful to be able to execute some code with certain values removed from the operand stack, and recover them later. Sometimes it simplifies a bit of stack manipulation that needs to be done, and sometimes robustness dictates that a user callback or other procedure of unknown quality should be called with a stack clean of any values it does not need so the damage will be contained if its stack management is a little off. metapre removes its mess temporarily from the stack when executing escaped code sequences, so the programmer can remember a simple rule about what those sequences will see on the stack. The same capability is made available to other code, in six flavors:

    hide
    anyn-1...any0 proc n hide array

    Executes proc with the n top values anyn-1...any0 popped from the stack. On completion of proc, which may have consumed and added other values on the stack, hide restores the n temporarily hidden values as an n-element array on the top of stack. Restoring them in array form simplifies access to any results from proc below the array, or a simple aload pop will restore the hidden elements to their rightful individual places atop the stack.

    hide is built on hide+k by supplying the continuation {exch{stop}if}.

    hide+ap
    anyn-1...any0 proc n hide+ap anyn-1...any0

    As hide followed by aload pop, so the n hidden items are replaced on the stack as individual items rather than an n-element array.

    hide+ap is built on hide+k by supplying the continuation {exch{stop}if aload pop}.

    hide+k
    anyn-1...any0 proc n k hide+k

    Executes proc with the n top values anyn-1...any0 popped from the stack, then executes k with whatever was left on the stack by proc, one boolean (true if proc encountered a stop, false otherwise), and an n-element array containing the values removed before executing proc.

    hvhide
    anyv+h-1...anyv anyv-1...any0 proc h+v v hvhide array

    Sometimes the things you want visible to proc are on top of the stack, and the things you want to hide are below them. Hides the h items anyv+h-1...anyv, executes proc and then replaces the h hidden items on top of its result as an h-element array, like hide.

    hvhide+ap
    anyv+h-1...anyv anyv-1...any0 proc h+v v hvhide+ap anyv+h-1...anyv

    As hvhide followed by aload pop, so the h hidden items are replaced as h individual items on the top of stack rather than an h-element array.

    hvhide+k
    anyv+h-1...anyv anyv-1...any0 proc h+v v k hvhide+k

    Executes proc with the h top items anyv+h-1...anyv popped from the stack, then executes k with whatever was left on the stack by proc, one boolean (true if proc encountered a stop, false otherwise), and an h-element array containing the values removed before executing proc.

    In PostScript's no-nonsense ancestor Forth, you could get things temporarily off the operand stack by shuffling them onto the return stack, but the crashes could be spectacular if you forgot to shuffle them back. These procedures offer a more robust, if less exhilarating, way to get the same effect in PostScript.

    Control flow

    The language Forth, also stack-based and postfix, did manage to have a readable structured IF/ELSE construct, achieved with Forth's version of staged programming; the IF, THEN, and ELSE words accumulated information on the stack during parsing, then used it to generate a final procedure with the corresponding control flow. A staging environment for PostScript ought to be able to use similar techniques. MetaPre provides a control flow construct using the words if:, else:if, else:, :if, and short-circuiting boolean connectives :and and :or.

    -|
      if: condition                condition : proc    (with boolean result)
        proc                                 | condition condition :and
      else:if condition                      | condition condition :or
        proc                                 ;
      else:
        proc
      :if
    |-
    

    As in any familiar if-construct, the else:if part can occur zero or more times, the else: part can be present or absent, and nested instances of the structure can be present. The source of MetaPre itself contains a fairly elaborate example, which I would not have enjoyed writing using plain PostScript if and ifelse with all the parallel cases getting ever more buried in brackets. A new if-construct may be syntactic sugar, but sometimes a little sugar makes a big difference.

    The construct must appear in an escape as shown (with the proper height for its context, not necessarily as shown) because it generates code. if:, else:if, and else: put things on the stack, interleaved with the conditions and procs, and :if consumes them all and generates a procedure out of PostScript operators (the usual form, ever more buried in brackets) to be spliced in for the escape.

    Similarly, :and and :or are not connectives that take two boolean values and produce a boolean result; they are connectives that take two procedures embodying boolean tests, and generate a procedure combining both tests in left-to-right, short-circuit fashion.

    In infix languages, the short-circuiting && and || operators usually have different precedence, with && binding more strongly, and parentheses are available to adjust the default. The usual translation from infix to postfix works to match any such expression with :and and :or and no parentheses:

    InfixTreePostfix
    a && b || c
           ||
         &&  c
        a  b      
    {a}{b}:and {c}:or
    a && ( b || c )
         &&
        a  ||
          b  c    
    {a}{b}{c}:or :and

    Think of the position of your :ands and :ors as determining the shape of an expression tree, not as determining which operator is evaluated first. The generated code will always do a left-to-right, short-circuit evaluation; in both trees above, {a} is the first leaf condition tested—by :and—but in the second form, if you were stuck thinking evaluation order instead of tree shape, you might mistakenly expect {b}{c}:or to be evaluated first.

    Fine points

    While you are building an if: construct—before the final :if—the stack contents can be reasoned about. The :if and each else:if adds an item, each condition and proc is an item, so there are three items for each branch. else: has no condition, but adds two items rather than one so the invariant is preserved. If you have a complicated series of tests that do not factor easily so every proc is unique, in place of a proc you can use 2 index to share the proc from the previous branch, 5 index for the branch before that, and so on. Sharing reduces duplicated code and the risk that maintenance overlooks some copies. Be advised that if you do this with a proc that contains escapes, it will wind up referenced in two places with different nesting depths, and I am not sure what MetaPre will do but it's probably not what you want.

    More control flow
    xforall
    array|packedarray|dict|string proc xforall

    A variation of forall that, instead of executing a fixed proc each iteration, executes the proc found on top of the stack before pushing each iteration's value(s). The proc must leave itself, or another proc, on top of the stack for the next iteration. xforall pops it on completion.

    In other words, xforall is equivalent to {exch exec} forall pop except when applied to a dictionary, in which case it is {3 -1 roll exec} forall pop. This design pattern is simple enough to code using plain forall, but with xforall it's a little clearer. It can be used (with ingroups, for example) to apply a proc to each group of n items from an array or string, to write a loop with special behavior for the first iteration(s), or in implementing a state machine.

    ingroups
    proc int ingroups procint

    Construct procint as would be used with xforall to apply proc once every intth iteration. That is, proc1 is equivalent to proc but leaves procint on top of the stack, and prock for every k > 1 simply leaves prock−1 on top of the stack, disturbing nothing else. When used with xforall then, items pushed from the array, dictionary, or string accumulate undisturbed on the stack so that proc can find the values from int iterations on the stack as a group.

    As an example, with int = 4, procint is effectively {{{{proc //procint}}}}, without the name lookups. Again, it's a design pattern that's not too hard to code explicitly, but ingroups looks a little nicer.

    This example will treat myarray, with even length, as consecutive pairs of numbers and print the sum of each pair:

    myarray {add =} 2 ingroups xforall

    However, because ingroups does some allocation, if this code will be used more than once it is more efficient to do the ingroups in advance:

    /addbypairs {add =} bind 2 ingroups def
    myarray //addbypairs xforall

    Note that bind, metapre, or metabind, if needed, should be applied to the procedure before ingroups.

    Future

    The need to put explicit escape delimiters around this if construct is an annoyance. In Forth, IF is an immediate word, which effects an automatic switch from delaying to evaluating code, and MetaPre should one day have that ability also.

    This if-construct is syntactic sugar for the usual PostScript if/ifelse form, where later branches are at increasing depth of delay nesting. When placing escapes in such deeply nested code, it can be a challenge to get the heights right, and it is even more of a challenge with this construct, which conceals the nest of brackets from view. Ultimately, MetaPre should understand some form of “virtual” depth correction, which the if-construct could place automatically in the code that it generates so that MetaPre's understanding of nesting depth matches what appears in the source. That is not likely to require many lines of code—they just have to be the right ones, and they are not there yet.

    Miscellany

    MetaPre supplies a few procedures that have little to do with staged programming, but are useful for developing modular and reusable PostScript resources, one of the reasons for developing MetaPre, and they had to go somewhere. The queue procedures are just there because MetaPre needed them and they might as well be exported to save the trouble again.

    deq
    queue deq false | item true

    Dequeue an item from a singly-linked queue. queue is an array expected to have, at index 0, null if the queue is empty, or the tail item in the queue. Each item is an array whose last element is the next item (but for the tail, whose last element is the head). If the queue is not empty, the head element is removed and returned, with the flag true. If the queue is empty, only the flag false is returned.

    The element returned is shortened (with getinterval) so that it does not include the former last element (queue link).

    enq
    queue new enq head new

    Enqueue an item on a singly-linked queue. queue is an array expected to have, at index 0, null if the queue is empty, or the tail item in the queue. new is an array that is to be the next item in the queue. enq updates queue and the tail of the queue, as necessary, but does not update new. head is the value that should be stored in new's last element to complete the insertion; the stack order is convenient if the caller will finish populating new with an astore.

    errorstop
    any name errorstop

    Initiate the handling of error name. If name is an error known in errordict, fetch and execute the associated value with any on top of the stack, just as an error detected by the interpreter is handled.

    If name is not known in errordict, do what the default handlers in errordict do: save name as the errorname and any as the command in $error, record the stacks, set local allocation mode, and execute stop. errorstop makes it simple for modular resources to signal their own specific exceptions, as it is not necessary to enter anything in errordict first, though an entry can still be made in errordict to alter the handling of the error.

    export
    namearray export dict

    Create a new dictionary exactly the size of namearray, and copy into it only the names in namearray and their values as found on the current dictionary stack by load. Remove the top dictionary on the dictionary stack and discard it, and leave the new dictionary, containing only the exported definitions, on the operand stack.

    These are common operations for a reusable module packaged as a resource, as MetaPre itself is. The dictionary on top of the lookup stack is assumed to be the private one used in the resource-defining file, containing both the definitions meant to be visible and any number of internal implementation factors. The procedures are assumed to contain bound references to each other rather than relying on name lookup, so it is now safe to discard the dictionary with the names of internal factors. The dictionary returned, containing just the definitions to be exported, is ready to register with defineresource.

    fix
    proc fix proc

    To write a recursive procedure that will not need to look up its name at run time, simply write it as if you expect one extra operand, a procedure, on top of the stack. At a recursive call point, exec that procedure. Although you are writing as if you expect the extra object on the stack, do not pass anything extra when you exec it (i.e., do not write dup exec). Assume its stack effect is exactly that of the procedure you originally wanted to write.

    After writing the procedure that way, just apply fix to it to get the procedure you wanted to write. (The name comes from the slightly esoteric fact that the procedure you want is a “fixed point” of the procedure you write.) Everybody uses factorial as the example, so here goes:

          /f {
            1 index 0 eq {pop pop 1} {1 index 1 sub exch exec mul} ifelse
          } bind fix def
          7 f =
          5040

    Notice that if other procedures using f use bound references to it, the dictionary with its name can be safely discarded; in fact, there was no need to name it at all.

    If you use bind, metapre, or metabind on your procedure, they go before fix.

    Valid XHTML 1.0! Valid CSS! $Id: MetaPre.html,v 1.11 2009/12/03 05:28:31 chap Exp $