Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

level 2 postscript debugger steps through procs and files

85 views
Skip to first unread message

luserXtrog

unread,
Sep 16, 2010, 4:46:33 AM9/16/10
to
643(2)03:38 AM:ps 0> for i in buggy.ps db2.ps ; do echo; echo; echo
%-- $i --; cat $i; echo %------------; echo; echo; done;


%-- buggy.ps --
/doit { moveto lineto stroke showpage } def

72 72
0
doit

%------------


%-- db2.ps --
%!
{ currentfile token pop /ENDINTRO eq { exit } if } loop

debug.ps - A postscript debugger


Switches
--------
stepon enable single-stepping
stepoff disable single-stepping
traceon enable tracing (echo each token before executing)
traceoff disable tracing

Procedures
----------
(filename) debug
Begin debugging file.

resume
Resume debugging after 'exit'ing from an error prompt.
or 'q'uiting from single-step.

object wherevalue dict key true
false
Perform reverse lookup for object in the dictionary stack.

dicttrace
Display the dictionary stack using wherevalue to obtain
dictionary names.

skip
Skip the current object.


Caveats
-------
You cannot change single-stepping directly from the error prompt.
But you may 'exit' and then 'stepoff resume'.


ENDINTRO

(ds.ps) run


/errmsg {
(Error /) print
$error /errorname get ( ) cvs print
( occurred attempting to execute ) print
} def

/dbdict <<
/infile null
/nexttoken null
/parray 250 array
/pptr -1
/trace false
/step false
>> def

/traceon { //dbdict /trace true put } def
/traceoff { //dbdict /trace false put } def
/stepon { //dbdict /step true put } def
/stepoff { //dbdict /step false put } def

/pushproc { % proc
//dbdict /pptr 2 copy get 1 add put
//dbdict /parray get
//dbdict /pptr get
3 -1 roll put
} def

/popproc {
//dbdict /pptr 2 copy get 1 sub put
} def

/pulltoken { % pulltoken token true | false
//dbdict /parray get
//dbdict /pptr get
get
dup length 0 gt {
dup 0 get exch
dup length 1 gt {
//dbdict /parray get
//dbdict /pptr get
3 -1 roll
1 1 index length 1 sub getinterval put
}{ %else length 1 eq
pop
//dbdict /parray get
//dbdict /pptr get {} put
//popproc exec
} ifelse
true
}{
//popproc exec
false
} ifelse
} def

/getnexttoken {
//dbdict /nexttoken get dup null eq {
pop
//dbdict /pptr get 0 ge {
pulltoken
}{
//dbdict /infile get token
} ifelse
}{ true } ifelse
} def

/handerr {
$error /errorname get /invalidexit eq {
exit
}{
(db:) print
//errmsg exec
//dbdict /nexttoken get ==
(Stack:\n) print
pstack
dicttrace
(\n) print
{
(db-error>) print flush
(%lineedit) (r) file cvx
exec
} stopped {
$error /errorname get /invalidexit ne
$error /errorname get /undefinedfilename ne
and {
//errmsg exec (user command\n) print
} if
exit
} if
} ifelse
} def

/stepcommands <<
/default { }
(y) 0 get { }
(s) 0 get { skip }
(q) 0 get { exit }
>> def

/stepquiz {
(db: Stack: \n) print pstack
(db: Step ) print
dup ==
(defined as ) print
{ load == } stopped { (/undefined\n) print } if
(db: execute step? ([(y)es]/(s)kip/(q)uit)?) print flush
(%lineedit) (r) file read {
//stepcommands
exch 2 copy known not { pop /default } if
get exec
} if
} def

/debugloop {
//getnexttoken exec
{ %ifelse
//dbdict /nexttoken 3 -1 roll put
{ %stopped
//dbdict /nexttoken get
//dbdict /trace get { dup == } if

dup xcheck {
dup type /arraytype ne {
//dbdict /step get { //stepquiz exec }{ pop }
ifelse
//dbdict /nexttoken get dup null eq {
pop
}{
load
dup type /arraytype eq {
dup length 0 eq {
pop
}{
pushproc
} ifelse
}{
exec
} ifelse
} ifelse
} if %else leave on stack
} if %else leave on stack

//dbdict /nexttoken null put
} stopped //handerr if
}{ %else no tokens
//dbdict /infile get closefile
//dbdict /infile null put
//dbdict /nexttoken null put
(db: finished\n) print
exit
} ifelse
} def

/debug { % (filename) debug -
//dbdict /trace get { (db: tracing ) }{ (db: debugging ) } ifelse
print
dup print (\n) print
{ %stopped
(r) file
} stopped { %if
(db:Error unable to open input file\n) print
//errmsg exec (\n) print
}{
//dbdict /infile 3 -1 roll put
(db: starting\n) print
//debugloop loop
}
ifelse
} def

/resume {
//dbdict /infile get null eq {
(db: resume what, pray?\n) print
} {
(db: resuming) print
//dbdict /trace get { ( trace) print } if
(\n) print
//debugloop loop
}
ifelse
} def

/skip { //dbdict /nexttoken null put } def

currentdict /dbdict undef
currentdict /errmsg undef
currentdict /handerr undef
currentdict /stepcommnds undef
currentdict /stepquiz undef
currentdict /steploop undef
currentdict /debugloop undef

stepon traceon (buggy.ps) debug
%eof
%------------


644(2)03:41 AM:ps 0> gsnd db2.ps
GPL Ghostscript 8.62 (2008-02-29)
Copyright (C) 2008 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
db: tracing buggy.ps
db: starting
/doit
{moveto lineto stroke showpage}
def
db: Stack:
def
{moveto lineto stroke showpage}
/doit
db: Step def
defined as --def--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
72
72
0
doit
db: Stack:
doit
0
72
72
db: Step doit
defined as {moveto lineto stroke showpage}
db: execute step? ([(y)es]/(s)kip/(q)uit)?
moveto
db: Stack:
moveto
0
72
72
db: Step moveto
defined as --moveto--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
lineto
db: Stack:
lineto
72
db: Step lineto
defined as --lineto--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
db:Error /stackunderflow occurred attempting to execute lineto
Stack:
72
Dict Stack:
userdict
globaldict
systemdict

db-error>0
lineto
db: Stack:
lineto
0
72
db: Step lineto
defined as --lineto--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
stroke
db: Stack:
stroke
db: Step stroke
defined as --stroke--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
showpage
db: Stack:
showpage
db: Step showpage
defined as --showpage--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
db: finished
GS>645(2)03:42 AM:ps 0>

luserXtrog

unread,
Sep 16, 2010, 6:59:29 AM9/16/10
to
Why does one always notice huge problems immediately after posting.

This version hijacks 'run' commands.

652(2)05:47 AM:ps 0> for i in db3.ps ds.ps meas.ps buggy.ps; do echo;
echo; echo %-- $i --------; cat $i; echo %-- eof --------; echo; echo;
done;


%-- db3.ps --------


ENDINTRO

(ds.ps) run

/dbdict <<
/nexttoken null
/stack 250 array
/ptr -1


/trace false
/step false
>> def

/traceon { //dbdict /trace true put } def
/traceoff { //dbdict /trace false put } def
/stepon { //dbdict /step true put } def
/stepoff { //dbdict /step false put } def

/pushs { % proc|file pushs
//dbdict /ptr 2 copy get 1 add put
//dbdict /stack get
//dbdict /ptr get


3 -1 roll put
} def

/pops {
//dbdict /stack get
//dbdict /ptr get
get dup type /filetype eq {
closefile
}{ pop } ifelse
//dbdict /ptr 2 copy get 1 sub put
} def

/pulltoken { % pulltoken token true | false

//dbdict /stack get
//dbdict /ptr get


get
dup length 0 gt {
dup 0 get exch
dup length 1 gt {

//dbdict /stack get
//dbdict /ptr get


3 -1 roll
1 1 index length 1 sub getinterval put
}{ %else length 1 eq
pop

//dbdict /stack get
//dbdict /ptr get {} put
//pops exec
} ifelse
true
}{
//pops exec
false
} ifelse
} def

/getnexttoken {
//dbdict /nexttoken get
dup null eq {
pop

//dbdict /stack get
//dbdict /ptr get
get
dup type /filetype eq {
token
}{
pop
pulltoken
} ifelse
}{ true } ifelse
} def

dup /run cvx eq {
pop (r) file pushs


}{
load
dup type /arraytype eq {
dup length 0 eq {
pop
}{

pushs
} ifelse
}{
exec
} ifelse


} ifelse
} ifelse
} if %else leave on stack
} if %else leave on stack

//dbdict /nexttoken null put
} stopped //handerr if
}{ %else no tokens

pops


//dbdict /nexttoken null put
(db: finished\n) print

//dbdict /ptr get 0 lt { exit } if
} ifelse
} def

/debug { % (filename) debug -
//dbdict /trace get { (db: tracing ) }{ (db: debugging ) } ifelse
print
dup print (\n) print
{ %stopped
(r) file
} stopped { %if
(db:Error unable to open input file\n) print
//errmsg exec (\n) print
}{

pushs


(db: starting\n) print
//debugloop loop
}
ifelse
} def

/resume {
//dbdict /ptr get 0 lt {
(db: nothing to resume.\n) print


}{
(db: resuming) print
//dbdict /trace get { ( trace) print } if
(\n) print
//debugloop loop
} ifelse
} def

/skip { //dbdict /nexttoken null put } def

currentdict /dbdict undef
currentdict /errmsg undef
currentdict /handerr undef
currentdict /stepcommnds undef
currentdict /stepquiz undef
currentdict /steploop undef
currentdict /debugloop undef

%eof
%-- eof --------


%-- ds.ps --------
%!

/wherevalue { % value wherevalue dict key true | false
MYDICT
countdictstack array dictstack % val my-dict [ds]
exch begin
/found false def

dup length 1 sub -1 0 { % val [ds] i
1 index exch get % val [ds] dict

dup { % val [ds] dict key val
dup type /stringtype eq {
dup rcheck {
4 index eq {
/found true def
exit
} if
}{
pop
} ifelse
}{
4 index eq { % val [ds] dict key
/found true def
exit %forall
} if % val [ds] dict key
} ifelse
pop % val [ds] dict
} forall % val [ds] (found: dict key) (not: dict)

found {
exit %for
} if % val [ds] dict
pop % val [ds]

} for % (found: val [ds] dict key) (not: val [ds])

found {
4 2 roll pop pop true
}{
pop pop false
} ifelse
end
} dup 0 1 dict put def

/dicttrace {
(Dict Stack:\n) print
countdictstack array dictstack
dup length 1 sub -1 0 {
1 index exch get
wherevalue {
exch pop ( ) cvs print
(\n) print
}{
(-anonymous- ) print
} ifelse
} for
pop
} def

%-- eof --------


%-- meas.ps --------
%!
/in {72 mul} def
/cm {in 2.54 mul} def
%-- eof --------


%-- buggy.ps --------
(meas.ps) run

/doit { moveto lineto stroke showpage } def

1 in 1 in
0
doit

%-- eof --------


653(2)05:48 AM:ps 0> gsnd db3.ps


GPL Ghostscript 8.62 (2008-02-29)
Copyright (C) 2008 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.

GS>stepon traceon (buggy.ps) debug


db: tracing buggy.ps
db: starting

(meas.ps)
run
db: Stack:
run
(meas.ps)
db: Step run
defined as --run--


db: execute step? ([(y)es]/(s)kip/(q)uit)?

/in
{72 mul}
def
db: Stack:
def
{72 mul}
/in


db: Step def
defined as --def--
db: execute step? ([(y)es]/(s)kip/(q)uit)?

/cm
{in 2.54 mul}
def
db: Stack:
def
{in 2.54 mul}
/cm


db: Step def
defined as --def--
db: execute step? ([(y)es]/(s)kip/(q)uit)?

db: finished


/doit
{moveto lineto stroke showpage}
def
db: Stack:
def
{moveto lineto stroke showpage}
/doit
db: Step def
defined as --def--
db: execute step? ([(y)es]/(s)kip/(q)uit)?

1
in
db: Stack:
in
1
db: Step in
defined as {72 mul}


db: execute step? ([(y)es]/(s)kip/(q)uit)?
72

mul
db: Stack:
mul
72
1
db: Step mul
defined as --mul--


db: execute step? ([(y)es]/(s)kip/(q)uit)?

1
in
db: Stack:
in
1
72
db: Step in
defined as {72 mul}


db: execute step? ([(y)es]/(s)kip/(q)uit)?
72

mul
db: Stack:
mul
72
1
72
db: Step mul
defined as --mul--


db: execute step? ([(y)es]/(s)kip/(q)uit)?

0
doit
db: Stack:
doit
0
72
72
db: Step doit
defined as {moveto lineto stroke showpage}
db: execute step? ([(y)es]/(s)kip/(q)uit)?
moveto
db: Stack:
moveto
0
72
72
db: Step moveto
defined as --moveto--

db: execute step? ([(y)es]/(s)kip/(q)uit)?q
GS<3>0 pstack
0
0
72
72
GS<4>resume
db: resuming trace


moveto
db: Stack:
moveto
0
0
72
72
db: Step moveto
defined as --moveto--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
lineto
db: Stack:
lineto
72

72
db: Step lineto
defined as --lineto--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
stroke
db: Stack:
stroke
db: Step stroke
defined as --stroke--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
showpage
db: Stack:
showpage
db: Step showpage
defined as --showpage--
db: execute step? ([(y)es]/(s)kip/(q)uit)?
db: finished

GS>quit
654(2)05:49 AM:ps 0>

tlvp

unread,
Sep 17, 2010, 1:48:53 AM9/17/10
to
On Thu, 16 Sep 2010 06:59:29 -0400, luserXtrog <mij...@yahoo.com> wrote:

> Why does one always notice huge problems immediately after posting.

Your choice: (1) It's Mother Nature's way of keeping one humble;
and/or (2) better *immediately* after than *long* after :-) .

Cheers, -- tlvp
--
Avant de repondre, jeter la poubelle, SVP

luserXtrog

unread,
Sep 17, 2010, 2:29:59 AM9/17/10
to
On Sep 17, 12:48 am, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:

> On Thu, 16 Sep 2010 06:59:29 -0400, luserXtrog <mijo...@yahoo.com> wrote:
> > Why does one always notice huge problems immediately after posting.
>
> Your choice: (1) It's Mother Nature's way of keeping one humble;
> and/or (2) better *immediately* after than *long* after :-) .
>
> Cheers, -- tlvp

I'll take both.
The next milestone is for it to debug itself debugging a file.
I think I'm going to have to wrap all functions into another
dictionary instead of userdict and remove that while executing
the program tokens.

The problem I see now is that loading a second instance of the
debugger overwrites the existing procedures with new ones tied
to the new dbdict. And currentfile will require much more trickery.

--
lxt

tlvp

unread,
Sep 18, 2010, 1:00:33 AM9/18/10
to
On Fri, 17 Sep 2010 02:29:59 -0400, luserXtrog <mij...@yahoo.com> wrote:

> On Sep 17, 12:48 am, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:
>> On Thu, 16 Sep 2010 06:59:29 -0400, luserXtrog <mijo...@yahoo.com> wrote:
>> > Why does one always notice huge problems immediately after posting.
>>
>> Your choice: (1) It's Mother Nature's way of keeping one humble;
>> and/or (2) better *immediately* after than *long* after :-) .
>>
>> Cheers, -- tlvp
>
> I'll take both.

They're yours -- enjoy them, as the Rom say, in good health.

> The next milestone is for it to debug itself debugging a file.

Whew! I had no idea how right I was earlier, when I wrote,
"Heh ... you enjoy re-entrant inductive looping phenomena, don't you?"

> I think I'm going to have to wrap all functions into another
> dictionary instead of userdict and remove that while executing
> the program tokens.
>
> The problem I see now is that loading a second instance of the
> debugger overwrites the existing procedures with new ones tied
> to the new dbdict. And currentfile will require much more trickery.

I'll observe the results with interest ... but I don't think
my poor little brain will be able to help in any way, sorry.

> --
> lxt

luserXtrog

unread,
Sep 18, 2010, 3:47:04 AM9/18/10
to
On Sep 18, 12:00 am, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:

> On Fri, 17 Sep 2010 02:29:59 -0400, luserXtrog <mijo...@yahoo.com> wrote:
> > On Sep 17, 12:48 am, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:
> >> On Thu, 16 Sep 2010 06:59:29 -0400, luserXtrog <mijo...@yahoo.com> wrote:
> >> > Why does one always notice huge problems immediately after posting.
>
> >> Your choice: (1) It's Mother Nature's way of keeping one humble;
> >> and/or (2) better *immediately* after than *long* after :-) .
>
> >> Cheers, -- tlvp
>
> > I'll take both.
>
> They're yours -- enjoy them, as the Rom say, in good health.
>

ALL YOUR BASE ARE BELONG TO US.

> > The next milestone is for it to debug itself debugging a file.
>
> Whew! I had no idea how right I was earlier, when I wrote,
> "Heh ... you enjoy re-entrant inductive looping phenomena, don't you?"
>

I was meaning to question that term. Certainly I like "looping
phenomena". It is the very essence of automatic computation.
Babbage's crankshaft. But I don't quite see the connection to
induction. And doesn't re-entrant have something to do with
airplanes? Or is it porn?

> > I think I'm going to have to wrap all functions into another
> > dictionary instead of userdict and remove that while executing
> > the program tokens.
>
> > The problem I see now is that loading a second instance of the
> > debugger overwrites the existing procedures with new ones tied
> > to the new dbdict. And currentfile will require much more trickery.
>
> I'll observe the results with interest ... but I don't think
> my poor little brain will be able to help in any way, sorry.
>

But should it succeed, the world will reap the benefit!
Then I can die happy ;{>

But I consider the test of debugging itself to a shortcut to
verifying its, um ... veracity. I mean it seems sound. There's
no garbage on the stack. But writing it and reading it are
very different things. It's pretty hairy in there.
I'm not sure I could have written what I've got without syntax
highlighting :)

--
santa louisa

tlvp

unread,
Sep 19, 2010, 12:48:21 AM9/19/10
to
On Sat, 18 Sep 2010 03:47:04 -0400, luserXtrog <mij...@yahoo.com> wrote:

> On Sep 18, 12:00 am, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:
>> On Fri, 17 Sep 2010 02:29:59 -0400, luserXtrog <mijo...@yahoo.com> wrote:
>> > The next milestone is for it to debug itself debugging a file.
>>
>> Whew! I had no idea how right I was earlier, when I wrote,
>> "Heh ... you enjoy re-entrant inductive looping phenomena, don't you?"
>>
>

> I was meaning to question that term. ...

Sorry, the term "inductive"? Probably a poor choice of word.
Certainly magnetic induction has nothing go do with it.
Most likely "iterative" or "recursive" would have been
a better choice of word.

> ... Certainly I like "looping


> phenomena". It is the very essence of automatic computation.
> Babbage's crankshaft. But I don't quite see the connection to

> induction. ...

Recursion, then?

> ... And doesn't re-entrant have something to do with
> airplanes? Or ... ?

I was using "re-entrant" loosely, to refer to the sort of nearly
recursive repetition that has one start at A, go to B, go on to C,
go further to D, and then repeat the to-B, to-C, to-D pattern
without ever first returning to A again. Not too clear, was I?

>> > I think I'm going to have to wrap all functions into another
>> > dictionary instead of userdict and remove that while executing
>> > the program tokens.
>>
>> > The problem I see now is that loading a second instance of the
>> > debugger overwrites the existing procedures with new ones tied
>> > to the new dbdict. And currentfile will require much more trickery.
>>
>> I'll observe the results with interest ... but I don't think
>> my poor little brain will be able to help in any way, sorry.
>>
>
> But should it succeed, the world will reap the benefit!
> Then I can die happy ;{>
>
> But I consider the test of debugging itself to a shortcut to
> verifying its, um ... veracity. I mean it seems sound. There's
> no garbage on the stack. But writing it and reading it are
> very different things. It's pretty hairy in there.
> I'm not sure I could have written what I've got without syntax
> highlighting :)
>
> --
> santa louisa
>

Cheers, -- tlvp

luserXtrog

unread,
Sep 19, 2010, 1:49:26 AM9/19/10
to
On Sep 18, 11:48 pm, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:

> On Sat, 18 Sep 2010 03:47:04 -0400, luserXtrog <mijo...@yahoo.com> wrote:
> > On Sep 18, 12:00 am, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:
> >> On Fri, 17 Sep 2010 02:29:59 -0400, luserXtrog <mijo...@yahoo.com> wrote:
> >> > The next milestone is for it to debug itself debugging a file.
>
> >> Whew! I had no idea how right I was earlier, when I wrote,
> >> "Heh ... you enjoy re-entrant inductive looping phenomena, don't you?"
>
> > I was meaning to question that term. ...
>
> Sorry, the term "inductive"? Probably a poor choice of word.
> Certainly magnetic induction has nothing go do with it.
> Most likely "iterative" or "recursive" would have been
> a better choice of word.
>
> > ... Certainly I like "looping
> > phenomena". It is the very essence of automatic computation.
> > Babbage's crankshaft. But I don't quite see the connection to
> > induction. ...
>
> Recursion, then?

Well, yeah. That seems natural to the domain here. Just about
everything's recursive in postscript, from a certain point of
view. Even 'loop' is recursive. loop takes a procedure off of
the operand stack and pushes it onto the execution stack.
Then it pushes a continuation operator. Then it pushes another
copy of the copy (this will become the first iteration of the
loop body on the next cycle of the interpreter). The continuation
%loop-continue finds the copy stashed higher up and pushes first
another copy of itself and then the proc again (the second
iteration of the loop body).

Having discovered this part of the interpreter, I can't seem
to "unlearn" it. So now I see all loops this way. A code block
treated as data, a little bookkeeping, and then whatever the
loop has to do so many times.

> > ... And doesn't re-entrant have something to do with
> > airplanes? Or ... ?
>
> I was using "re-entrant" loosely, to refer to the sort of nearly
> recursive repetition that has one start at A, go to B, go on to C,
> go further to D, and then repeat the to-B, to-C, to-D pattern
> without ever first returning to A again. Not too clear, was I?
>

Actually, I seem to recall where I heard the term before.
It has somthing to do with multiprogramming, where you fork
a process and don't exec a different program. The child just
does something different.

from strtok(3) manpage:
The strtok_r() function is a reentrant version strtok(). The saveptr
argument is a pointer to a
char * variable that is used internally by strtok_r() in order
to maintain context between succes-
sive calls that parse the same string.

So a program that forks has to make sure that none of its functions
are carrying static state because the forked copy may well attempt
to share that state. Like trying to share a pencil, somebody's going
to get upset.

Which is exactly the problem I'm facing right now. So that word,
at least, was well-chosen.

And, now it seems to me that "induction" is spot on, too. I really
only think through loops until just past the seam where the first
iteration ends and the second one begins. Expecting it to keep
working thereafter really is induction.

So, I suppose I do like these things; but more than that (and perhaps
more dangerously) i see them as necessary. It's my hammer.

--
loopiphen

tlvp

unread,
Sep 20, 2010, 1:58:56 AM9/20/10
to
On Sun, 19 Sep 2010 01:49:26 -0400, luserXtrog <mij...@yahoo.com> wrote:

>
> ... Just about


> everything's recursive in postscript, from a certain point of
> view. Even 'loop' is recursive. loop takes a procedure off of
> the operand stack and pushes it onto the execution stack.
> Then it pushes a continuation operator. Then it pushes another
> copy of the copy (this will become the first iteration of the
> loop body on the next cycle of the interpreter). The continuation
> %loop-continue finds the copy stashed higher up and pushes first
> another copy of itself and then the proc again (the second
> iteration of the loop body).
>
> Having discovered this part of the interpreter, I can't seem

> to "unlearn" it. So now I see all loops this way. ...

Heh ... reminds me of the Seeker and the Guru. The Guru tells the Seeker
how the whole world is being supported on the back of a huge turtle.

And what's the turtle standing on? "Oh, each foot rests on the back of
another turtle." And those four turtles? "Oh, same thing, each foot of
each of those four turtles rests on the back of yet another turtle."
And those sixteen turtles? "Look, before you get carried away with
your questions, young Seeker, let me just tell you that it's turtles
like that, all the way down, and beyond."

[In another version, it's elephants, not loops or turtles;
but you get the idea.]

luserXtrog

unread,
Sep 20, 2010, 2:27:49 AM9/20/10
to
On Sep 20, 12:58 am, tlvp <tPlOvUpBErLeL...@hotmail.com> wrote:

I heard it as turtles, one by one, and it was supposed to be
a heckler at some metaphysics lecture. I think it was Ouspensky.
Or was it Faraday?

Incidentally, I've discovered a slightly different way to do loop.
loop can push itself, cvx, and a literal version of the proc, and
then the executable version of the proc. This way there's no need
for a continuation operator at all. And no mark on the exec stack.
exit now just pops until it finds the loop operator (because it
naturally stays at the bottom of it's "frame").

One complication arises in forall with a dictionary. I had to make
a little hack function to produce a sub-dict. Easy enough since
I have dicts defined as flat arrays. Just increase the pointer
by 2, decrease the length by 1.

--
office bowling party. I bowled 53 and then 80.
I won. Don't know whether to laugh or cry.

luserXtrog

unread,
Sep 20, 2010, 4:04:44 AM9/20/10
to
A much improved version, I think. You can now step into
a proc or file or just execute it. A replacement currentfile
will deliver the correct file which should allow debugging
files with images.

For variety, the transcript comes first. Hitting just enter
at the step prompt will re-do the previous command.
Unfortunately, if the last thing you did was quit (to play
with the stack, maybe) then enter will quit again.
ctrl-D will quit.

9(1)02:47 AM:ps 0> gsnd db4.ps


GPL Ghostscript 8.62 (2008-02-29)
Copyright (C) 2008 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.

GS>(buggy.ps) stepon debug
db: debugging buggy.ps
db: starting


db: Stack:
run
(meas.ps)
db: Step run
defined as --run--

db: execute step? (continue|next|bypass|step|quit)?n
db: finished


db: Stack:
def
{moveto lineto stroke showpage}
/doit
db: Step def
defined as --def--

db: execute step? (continue|next|bypass|step|quit)?n


db: Stack:
in
1
db: Step in
defined as {72 mul}

db: execute step? (continue|next|bypass|step|quit)?n


db: Stack:
in
1
72
db: Step in
defined as {72 mul}

db: execute step? (continue|next|bypass|step|quit)?n


db: Stack:
doit
0
72
72
db: Step doit
defined as {moveto lineto stroke showpage}

db: execute step? (continue|next|bypass|step|quit)?s


db: Stack:
moveto
0
72
72
db: Step moveto
defined as --moveto--

db: execute step? (continue|next|bypass|step|quit)?q
GS<3>0


GS<4>resume
db: resuming

db: Stack:
moveto
0


0
72
72
db: Step moveto
defined as --moveto--

db: execute step? (continue|next|bypass|step|quit)?n
db: Stack:
lineto
72


72
db: Step lineto
defined as --lineto--

db: execute step? (continue|next|bypass|step|quit)?


db: Stack:
stroke
db: Step stroke
defined as --stroke--

db: execute step? (continue|next|bypass|step|quit)?


db: Stack:
showpage
db: Step showpage
defined as --showpage--

db: execute step? (continue|next|bypass|step|quit)?
db: finished
GS>quit
10(1)02:49 AM:ps 0> for i in db4.ps ds.ps meas.ps buggy.ps; do echo;
echo %--- $i ----; cat $i; echo %--- eof ----; echo; echo; done;

%--- db4.ps ----


%!
{ currentfile token pop /ENDINTRO eq { exit } if } loop

debug.ps - A postscript debugger

The debugger takes a file and executes each token within
a stopped context, catching errors and retaining the rest
of the program. It requires an interactive session with
the interpreter. Oh, and a file system, probably.

Switches
--------
stepon enable single-stepping
stepoff disable single-stepping

steppstackon enable display of stack before each step
steppstackoff disable display of stack before each step


traceon enable tracing (echo each token before executing)
traceoff disable tracing

With 'traceon stepon' you might want to 'steppstackoff'.
to avoid too much information. Default is on.

Procedures
----------
(filename) debug
Begin debugging file.

resume
Resume debugging after 'exit'ing from an error prompt.
or 'q'uiting from single-step.

skip
Skip the current object.

object wherevalue dict key true
false
Perform reverse lookup for object in the dictionary stack.

dicttrace
Display the dictionary stack using wherevalue to obtain
dictionary names.


ENDINTRO

(ds.ps) run

11 dict begin

%
% internal names
% will be undef'ed later
%

% print error message


/errmsg {
(Error /) print
$error /errorname get ( ) cvs print
( occurred attempting to execute ) print
} def

% the debugger state


/dbdict <<
/nexttoken null
/stack 250 array

/ptr -1
/trace false
/step false
/steppstack true
>> def

% push a source (file or array or even string)
% on the debug stack (defined as the typical size of the
% exec stack from the red book).


/pushs { % proc|file pushs

//dbdict /ptr 2 copy get 1 add put
//dbdict /stack get
//dbdict /ptr get


3 -1 roll put
} def

% remove a source from the stack
% if it's a file, close it.


/pops {
//dbdict /stack get
//dbdict /ptr get
get dup type /filetype eq {
closefile
}{ pop } ifelse

//dbdict /ptr 2 copy get 1 sub put
} def

% fetch the next token from the
% array on top of the stack
% TODO: there's some duplication here with getnexttoken


/pulltoken { % pulltoken token true | false

//dbdict /stack get
//dbdict /ptr get


get
dup length 0 gt {
dup 0 get exch
dup length 1 gt {

//dbdict /stack get
//dbdict /ptr get


3 -1 roll
1 1 index length 1 sub getinterval put
}{ %else length 1 eq
pop

//dbdict /stack get
//dbdict /ptr get {} put
//pops exec
} ifelse
true
}{
//pops exec
false
} ifelse
} def

% fetch next token from
% top of stack


/getnexttoken {
//dbdict /nexttoken get
dup null eq {
pop

//dbdict /stack get
//dbdict /ptr get
get
dup type /filetype eq {
token
}{
pop

//pulltoken exec
} ifelse
}{ true } ifelse
} def

% handerr
% print message and present the error prompt
%
% some things you can do at the error prompt:
% skip - this will clear the /nexttoken entry
% in the dictionary so the next loop
% will fetch the next thing
% (may cause problems later if the
% program needed this to happen)
% exit - you're only in one loop so the regular
% postscript exit operator will exit the
% debugger function and take you back to
% the outer level from which you invoked
% the debug procedure.you can 'skip' here
% too and then 'resume' to restart the
% debugger where you left off.
% This is a legitimate postscript prompt. So if the error
% is a typecheck or a stack underflow, you can fix the
% stack, hit enter, and the debugger will try again to
% execute the token. If the stack manipulation succeeds,
% then that's what the program needs to do at that point.
% TODO: make a separate trace output that inserts
% error commands into the text of the debugged program.


/handerr {
$error /errorname get /invalidexit eq {
exit
}{
(db:) print
//errmsg exec
//dbdict /nexttoken get ==
(Stack:\n) print
pstack
dicttrace
(\n) print
{
(db-error>) print flush
(%lineedit) (r) file cvx
exec
} stopped {
$error /errorname get /invalidexit ne
$error /errorname get /undefinedfilename ne
and {
//errmsg exec (user command\n) print
} if
exit
} if
} ifelse
} def

% the debug loop performs this on each executable object
/debugheart {
dup /run eq {
pop (r) file //pushs exec


}{
load
dup type /arraytype eq {

dup length 0 eq { pop } //pushs ifelse
}{
exec
} ifelse
} ifelse
} def

% these are the single-character codes for the stepwise
% prompt you can type full words if you like. it only
% reads the first character. the initial default is next.
% whereupon the token in question will be executed
% in a stopped context in the big loop.
% default is changed to whatever happened last.
/stepcommands <<
(n) 0 get { %next
/stepon load //pushs exec stepoff //debugheart exec
}
/default 1 index
(c) 0 get { stepoff //debugheart exec } %continue
(b) 0 get { pop skip } %bypass
(s) 0 get //debugheart % :)
(q) 0 get { pop exit } %quit
>> def

% present the step prompt,
% read a line from the user
% execute the matching step command
/stepheart {
//dbdict /steppstack get { (db: Stack: \n) print pstack } if


(db: Step ) print
dup ==
(defined as ) print

dup { load } stopped { pop (/undefined\n) print } { == } ifelse
(db: execute step? (continue|next|bypass|step|quit)?) print flush
(%lineedit) (r) { file } stopped { % ctrl-D
pop (\n) print exit
}{
read { % something typed


//stepcommands
exch 2 copy known not { pop /default } if
get

//stepcommands /default 2 index put
exec
}{ % blank line
//stepcommands /default get exec
} ifelse
} ifelse
} def

% the big ol' loop


/debugloop {
//getnexttoken exec
{ %ifelse
//dbdict /nexttoken 3 -1 roll put
{ %stopped
//dbdict /nexttoken get
//dbdict /trace get { dup == } if

dup xcheck {
dup type /arraytype ne {
//dbdict /step get

//stepheart
//debugheart


ifelse
} if %else leave on stack
} if %else leave on stack

//dbdict /nexttoken null put
} stopped //handerr if
}{ %else no tokens

//pops exec


//dbdict /nexttoken null put
(db: finished\n) print

//dbdict /ptr get 0 lt { exit } if
} ifelse
} def


%
% External names
%
% the dict of internal names is juggled off the dictstack
% so they're available during scanning but not present
% when the 'def'ing.
%

% enable/disable tracing/stepwise-prompting


/traceon { //dbdict /trace true put }

currentdict end 3 1 roll def begin


/traceoff { //dbdict /trace false put }

currentdict end 3 1 roll def begin


/stepon { //dbdict /step true put }

currentdict end 3 1 roll def begin


/stepoff { //dbdict /step false put }

currentdict end 3 1 roll def begin
/steppstackon { //dbdict /steppstack true put }
currentdict end 3 1 roll def begin
/steppstackoff { /dbdict /steppstack false put }
currentdict end 3 1 roll def begin

% the main entry point
% i could probably modify this to take a proc as well


/debug { % (filename) debug -
//dbdict /trace get
{ (db: tracing ) }{ (db: debugging ) } ifelse
print
dup print (\n) print
{ %stopped
(r) file
} stopped { %if
(db:Error unable to open input file\n) print
//errmsg exec (\n) print
}{

(db: starting\n) print
//pushs exec
//debugloop loop
}
ifelse
} currentdict end 3 1 roll def begin

% resume exit'ed session


/resume {
//dbdict /ptr get 0 lt {

(db: nothing to resume.\n) print


}{
(db: resuming) print
//dbdict /trace get { ( trace) print } if
(\n) print
//debugloop loop
} ifelse

} currentdict end 3 1 roll def begin

% skip the current token
% if used outside the debug loop (having exit'ed),
% it will clear the current token so the 'resume'ing
% will fetch the next thing from whereever it's
% getting them (array or file).


/skip {
//dbdict /nexttoken null put

} currentdict end 3 1 roll def begin

%
% interfaced builtin operator
%

/oldcurrentfile /currentfile load
currentdict end 3 1 roll def begin
/currentfile {
//dbdict /ptr get dup 0 ge {
-1 0 { %for
//dbdict /stack get exch get
dup type /filetype eq {
exit
} if
pop
} for
}{
oldcurrentfile
} ifelse
} currentdict end 3 1 roll def begin

end % discard internal names

% debug crashes upon attempting to debug its own array
%stepon traceon (db3.ps) debug
%stepon (buggy.ps) debug

%eof
%--- eof ----

%--- ds.ps ----
%!

% value wherevalue dict key true

% false
% find dictionary and key for value
/wherevalue {

% print names of dictionaries on the stack


/dicttrace {
(Dict Stack:\n) print
countdictstack array dictstack
dup length 1 sub -1 0 {
1 index exch get
wherevalue {
exch pop ( ) cvs print
(\n) print
}{
(-anonymous- ) print
} ifelse
} for
pop
} def

%--- eof ----

%--- meas.ps ----


%!
/in {72 mul} def
/cm {in 2.54 mul} def

%--- eof ----

%--- buggy.ps ----
(meas.ps) run

/doit { moveto lineto stroke showpage } def

1 in 1 in
0
doit

%--- eof ----


11(1)02:53 AM:ps 0>
--
these posts are getting long
I had to learn a new way to select text.
hurray for the legacy of athena widgets!

luserXtrog

unread,
Sep 20, 2010, 4:51:41 AM9/20/10
to
On Sep 20, 3:04 am, luserXtrog <mijo...@yahoo.com> wrote:

> /steppstackoff { /dbdict /steppstack false put }
>     currentdict end 3 1 roll def begin

make that:

/steppstackoff { //dbdict /steppstack false put }


currentdict end 3 1 roll def begin

This is why it needs to be able to debug itself.
One missing slash.

I discovered this while testing a more plausible
use case.

11(1)02:53 AM:ps 0> gsnd db4.ps


GPL Ghostscript 8.62 (2008-02-29)
Copyright (C) 2008 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.

GS>traceon steppstackoff (buggy.ps) debug
Error: /typecheck in --execute--
Operand stack:
dbdict steppstack false
Execution stack:
%interp_exit .runexec2 --nostringval-- --nostringval-- --
nostringval-- 2 %stopped_push --nostringval-- --
nostringval-- %loop_continue 1785 2 3 %oparray_pop --
nostringval-- --nostringval-- false 1
%stopped_push .runexec2 --nostringval-- --nostringval-- --
nostringval-- 2 %stopped_push --nostringval--
Dictionary stack:
--dict:1152/1684(ro)(G)-- --dict:0/20(G)-- --dict:105/200(L)--
Current allocation mode is local
Current file position is 22
GS<3>12(1)03:41 AM:ps 0> vi db4.ps
13(1)03:42 AM:ps 0> gsnd db4.ps


GPL Ghostscript 8.62 (2008-02-29)
Copyright (C) 2008 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.

GS>traceon steppstackoff (buggy.ps) debug


db: tracing buggy.ps
db: starting

(meas.ps)
run


/in
{72 mul}
def
/cm
{in 2.54 mul}
def

db: finished


/doit
{moveto lineto stroke showpage}
def
1
in

72
mul
1
in
72
mul
0
doit
moveto
lineto


db:Error /stackunderflow occurred attempting to execute lineto
Stack:
72
Dict Stack:
userdict
globaldict
systemdict

db-error>72 stepon
lineto


db: Step lineto
defined as --lineto--
db: execute step? (continue|next|bypass|step|quit)?

-dict-
/step
true
put


stroke
db: Step stroke
defined as --stroke--
db: execute step? (continue|next|bypass|step|quit)?

-dict-
/step
true
put


showpage
db: Step showpage
defined as --showpage--
db: execute step? (continue|next|bypass|step|quit)?

-dict-
/step
true
put
db: finished
GS>
GS>quit
14(1)03:44 AM:ps 0>

0 new messages