PostScript's Kafkaesque metamorphosis

TorgoX on 2006-04-21T04:40:38

Dear Log,

So, PostScript is hard. It's a stack language, and that made it ideal for what it needed to do decades ago; but in these days when people (understandably) are no more familiar with writing Forth than they are with coding in a hex assembler, it's just too damned hard to write by hand.

It sure would be nice to have an easier language that can do the same thing as PostScript -- i.e., write out pictures/text/etc. in the printer.

There have been some decent hacks at improving this situation:

  • Plisp aims to translate Common Lisp to a PostScript document; but is abandonware, and requires having a CL interpreter to do the translation.
  • PdB aims to translate a subset of C to PostScript (with a translator written in C), but it's apparently buggy, half-done, and abandoned.
  • And Logo2PS and Functional PostScript [the latter being a Scheme library] don't actually translate Logo/Scheme to PostScript, but instead provide graphics/text primitives that output PostScript code.

While these are all admirable, they all seem to me half-hearted, or maybe seven-eighths hearted. And I want to do things just a bit better: to implement a Lisp interpreter (possibly later a Logo interpreter) entirely in PostScript, so that it won't need a separate translator program-- instead, you would just send Lisp code (prefaced by the interpreter) right to the printer, and/or view it with GhostScript.

I'm "almost done" with it (i.e., 90% done, which leaves only 90% left to do), and I've already named it: Skreeth. One part Scheme (altho this isn't a straight Scheme implementation), one part Screech, one part proto-Romance for "writes" (cf Latin scribet and Old French escriţ) and one part, uh, Skreeth.

It has been very interesting working through SICP's discussion of implementing a full-featured Lisp using only a simpler Lisp -- because there are interesting matches between PostScript and an even well-developed Lisp (for example, PostScript has invisible garbage collection, and has distinct types for arrays/vectors, booleans, strings, names/symbols, reals, integers, hashes, and a null/nil/undef object), and yet PostScript has only global variables -- which means sensible recursion is out, which means the recursive eval/apply in SICP has run into trouble. Implementing/faking local variables in PostScript has been an especially fun use of PostScript's wide powers of introspection. It has been a great exercise in implementation.

It has also been a wonderful tour of PostScript's most bizarre features -- for example, there's no structure corresponding to if(X) {a} elsif(Y) {b} elsif... -- instead, you get to have visibly nested code that corresponds to:

  if(X) {
    a
  } else {
    if(Y) {
      b
    } else {
      ...
    }
  }
...which gets REALLY annoying after about four conditions. And there's no switch/case/cond statement in the language. Making a hacky one is esay -- making one that won't trip you up in weird ways, is not easy.

And the stack... the stack!... it never ceases to amaze and appall. It starts out great with things like "/inch {72 mul} def", but it quickly turns very scary. It's the perfect computational embodiment of the old X lyric "this is the game that moves as you play".

I'll post more here as I get this thing more ready for people to look at, but at the moment I'm just dumping raw stuff in here.

As a peek, I'm expecting that this PostScript code...

297 421 translate
0 .1 400 {
  dup dup sqrt 4 div 0 360 arc fill
  137.50775 rotate
} for
showpage

...will come out something like this in Skreeth:

(page
  (translate 297 421)
  (for (i 0 400 .1)
    (arc i i (/ (sqrt i) 4) 0 360)
    (fill)
    (rotate 137.50775)
  )
)

I'm not exactly a devotee of Lisp; but at the moment, given PostScript's limited string-manipulation powers and my own feebleness at implementing lexers/parsers, Lisp seems about the only choice. Lisp is not the friendliest language ever, but it's a step up from PostScript.


Fantastic!

bart on 2006-04-21T09:40:54

That's all really interesting... Converting Lisp to Postscript seems almost trivial, just using some First In Last Out inversion...

Anyway, I've seen your source code, It's all nice reading... but can you recommend a way to run it, apart from having and using a Postscript printer? Ghostscript? If so, how would that work? I've never used Ghostscript for anything other than processing/converting/showing pictures.

Re:Fantastic!

TorgoX on 2006-04-21T19:51:36

You just do "gs skreeth.psl" at a shell prompt to run the code. But for the moment that'll just run all the self-tests in the code and dump you at a GS prompt (where you can enter further PostScript commands). Enter "quit" to exit.

Fabulous!

drhyde on 2006-04-24T15:24:38

And it will of course be trivial to implement perl6 in Lisp, thus giving us perl on printers :-)