abusing this usenet forum s some kind of personal
postscript blog??
Assuming 'no',
I finally cleaned some this old program I call 'ibis'
last posted in 2011 in a bugridden form.
https://github.com/luser-dr00g/ibis.ps/blob/master/ibis.ps
So the first bit of drama was fixing the bug(s).
'Convenient!' I thought to myself, 'that I now have
a debugger to use on it.'
https://github.com/luser-dr00g/debug.ps/blob/master/db5.ps
Then I had to debugger the debug debugging the buggy ibis.
(...5xfast).
So back to 'ibis'. First it does some little convenience
routine which I had conveniently commented % Utiliy
"Utility"?? Thanks, past self. See that? That's a stupid.
Wise up! ya baloney.
Next, it encapsulates the "device" info, and the "text-setting"
api, and this crazy function called 'find' which is mostly a
wrapper for `search`.
% find takes 3 procedures a string and search-string
% and executes on_a and on_b upon the returned substrings if found
% or the not procedure if not found
/find { % {not} {on_b} {on_a} (aXb) (X) find -
search { % n b a (b) (X) (a)
4 1 roll pop % n b (a) a (b)
4 1 roll % n (b) b (a) a
5 -1 roll pop % (b) b (a) a
/exec cvx 5 3 roll % (a) a exec (b) b
/exec cvx 6 array astore cvx exec
%exec exec
}{ % n b a (a_b)
4 1 roll pop pop % (a_b) n
exec
} ifelse
} def
%{(n)= =}{(b)= =}{(a)= =} (pretextpost) (text) find
%(stack:)= pstack
%quit
The three lines at the top are new, based on my current
understanding. My past self had left the stack comments
(bravo, self) and the testing comments (again, good job,
past self). But had utterly failed to simply say what
the dang thing does.
Then it does some really crazy stuff to try to generalize
the behavior of built-in commands.
I should backtrack and preface this whole discussion by
saying that, in a way, this is the program that I truly
wanted to write when I first set out to learn the
postscript language initially. And the main reason I
got an "incomplete" in Physics 101. I tried to do all
my reports in pure postscript. I was also the editor
of the Honors College newsmagazine at the time, but
was wise enough not to convert the whole thing to
postscript, because I knew I was not mature enough as
a programmer to accomplish anything in a timely manner.
Sloppy as it was, the Pagemaker template was already
created. But over the course of my editorship, I tweaked
every dang thing from front to back, eventually designing
new cover art for each issue. (with some hacked versions
of Photoshop and stuff supplied by a Malaysian kid in the
dorms)
So 'ibis' is very dear to my heart. Even though I abandoned
it for, um, 4 years. It's also very new because I just
picked it up again, having almost completely forgotten
about it.
The built-in commands like 'italics' and 'bold', attempt
to encapsulate an on/off behavior that can be applied
automatically to keep the state in sync with the expectations
of the markup language. I'm not sure how well I've
succeeded so far. It saves the currentfont as its "undo"
action, so effects can be undone in a stack-wise manner,
but not out-of-order as I'd really like it to do.
This is more-or-less the basic execution framework to which
I'd like to add things like line-fitting and justifying
and columns and tables and kerning and stuff.
Questions/Comments/Chit-chat?
$ cat
ibis.ps
%!
%(
debug.ps/db5.ps)run %currentfile cvx debug
%/show { dup print (\n) print show } bind def
% mark k1 v1 .. kN vN dicttomark dict(N)
/dicttomark { counttomark dup dict begin 2 idiv { def } repeat pop currentdict end } def
/-= { 1 index load exch sub store } def
% Composite Index inc/dec -
% (n.b. userdict is a composite object)
/inc { 2 copy get 1 add put } def
/dec { 2 copy get 1 sub put } def
% Stack type is an array where element 0 contains the index of the top of the stack
% n stack array{n+1}:[0]=0
/stack { 1 add array dup 0 0 put } def
/top { dup 0 get get } def % S top a
/spop { dup top exch 0 dec } def % S spop a (S{n}->S'(n-1})
/spush { % S a spush - (S{n}->S'{n+1})
1 index 0 inc
1 index 0 get exch put
} def
% output device
/dev mark
/size [ clippath pathbbox ]
/bounds null
dicttomark def
/savebounds { dev /bounds [ x y X Y ] put } def
/restorebounds { dev /bounds get aload pop setbounds } def
/setbounds { % x y X Y setbounds -
/Y exch def /X exch def /y exch def /x exch def
} def
/setmargin { % pts setmargin -
dev /size get aload pop
4 index sub 4 1 roll 4 index sub 4 1 roll
4 index add 4 1 roll 4 index add 4 1 roll
setbounds pop savebounds
} def
/nextpage { showpage restorebounds } def
% text setting
/text mark
/eol { /Y lead -=
y Y lt { x Y moveto }
{ nextpage } ifelse }
/heol { eol }
/blank { eol }
/settext { show }
dicttomark def
% processing
% find takes 3 procedures a string and search-string
% and executes on_a and on_b upon the returned substrings if found
% or the not procedure if not found
/find { % {not} {on_b} {on_a} (aXb) (X) find -
search { % n b a (b) (X) (a)
4 1 roll pop % n b (a) a (b)
4 1 roll % n (b) b (a) a
5 -1 roll pop % (b) b (a) a
/exec cvx 5 3 roll % (a) a exec (b) b
/exec cvx 6 array astore cvx exec
%exec exec
}{ % n b a (a_b)
4 1 roll pop pop % (a_b) n
exec
} ifelse
} def
%{(n)= =}{(b)= =}{(a)= =} (pretextpost) (text) find
%(stack:)= pstack
%quit
%shift a 1-char string off of a larger string
/first { % (abc) first (bc) (a)
dup 1 1 index length 1 sub getinterval exch
0 1 getinterval
} def
% delimiter pairs
/pairs mark
([)(]) (<)(>) (\()(\)) ({)(}) (`)(') (:)(;)
dicttomark def
% ([) rhs (])
% (q) rhs (q)
/rhs { pairs exch 2 copy known { get }{ exch pop } ifelse } def
/nest 10 stack def
% {on_a} (]) deferal {[{on_a} (])] nest exch spush}
% create a save-it-for-later proc
% for the not-found clause of find
/deferal {
2 array astore
[ exch /nest cvx /exch cvx /spush cvx ] cvx
} def
% {on_a} ([a]b) delim (b)
/delim {
exch /settext cvx exch /exec cvx 3 array astore cvx exch
first rhs % on_a (a]b) (])
3 copy exch pop % on_a (a]b) (]) on_a (])
deferal % on_a (a]b) (]) not %not-clause
{} % on_a (a]b) (]) not on_b %on_b clause: leave string on stack
5 2 roll % not on_b on_a (a]b) (])
%(delim)= pstack()=
find
} def
% ([arg]rem) /name short (rem)
/short {
alt exch get % s d
dup /ini get exec % s d ?
exch /fin get % s ? {}
[ 3 1 roll /exec cvx ] cvx exch % undo s
(short)= pstack()=
delim
} def
% str execute -
/execute { token { exec process }{ BAD_COMMAND } ifelse } def
% the "at" sign indicates the start of an embedded command
/sigil <40> def
% scan string for embedded @commands
% and call settext for other text.
/process { % str process -
%(process:)= pstack()=
dup length 0 eq { pop blank }{
{settext} {execute} {settext} 4 3 roll
sigil find
} ifelse
} def
% call process on each line
%
/src null def
/buf 200 string def
/exitflag false def
/ibis { % file|string ibis -
dup type /stringtype eq { (r) file } if
dup type /filetype ne { NOT_A_FILE } if
/src exch def
pstack()=
{
src buf readline {process}{process exit}ifelse
heol
exitflag { exit } if
} loop
showpage
} def
% @@ define the at-sign as a command to print itself
sigil { sigil settext } def
%alterations
/alt 20 dict def
% name dict newalter -
%
% dict should contain 2 procs
% - ini ?
% ? fin -
% where init returns an object that should be
% passed to final
/newalter {
%install in alt dict
2 copy alt 3 1 roll put
%create short-form procedure
pop [ 1 index /short cvx ] cvx def
%create long-form
} def
%/i mark /ini {currentfont /Palatino-Italic 10 selectfont} /fin {setfont} dicttomark newalter
%/name /fontname fontsize fontproc -
/fontproc {
/currentfont cvx 3 1 roll /selectfont cvx 4 array astore cvx
mark exch /ini exch /fin {setfont} dicttomark newalter
} def
/r /Palatino-Roman 10 fontproc
/b /Palatino-Bold 10 fontproc
/i /Palatino-Italic 10 fontproc
/default {
text begin
72 setmargin
%/Palatino-Roman 10 selectfont
alt /r get /ini get exec pop
/lead 12 def
%eol
} def
/bye {pstack()= /exitflag true def} def
%/i load == quit
%stepon
%traceon
default
%currentfile /ibis load debug
currentfile ibis
Text is passed to the output.
Only ragged-right, explicit linebreaks for now.
There are various ways of executing a command to change
a section of text. All commands are introduced by the @@ character,
known internally as the "sigil".
The simple command @@ i for italics, can use the short form
with various sets of delimiters.
@@ i[italics] @i[italics]
@@ i(italics) @i(italics)
@@ i<italics> @i<italics>
@@ i{italics} @i{italics}
@@ i `italics' @i `italics'
@@ i :italics; @i :italics;
These last two require an extra space after the command name since
the backquote and colon are not postscript delimiters.
Incidentally, since the command name is scanned with 'token' and
executed with 'exec', it can even be a postscript procedure.
@{/oldfont currentfont def}
@@ {/Courier 11 selectfont}text in Courier
@{/Courier 11 selectfont}text in Courier
@{oldfont setfont}
Only "short-form" commands take a delimited argument.
The @@ {arbitrary ps code} commands are not "short-form",
and do not take an argument but apply directly to the current state.
Now with @b[bold].
@bye
(stack:)= pstack(---)= currentfile flushfile