Rocksolid Light

News from da outaworlds

mail  files  register  groups  login

Message-ID:  

BOFH excuse #264: Your modem doesn't speak English.


comp / comp.lang.lisp / PostScript With Continuations

SubjectAuthor
o PostScript With ContinuationsLawrence D'Oliveiro

1
Subject: PostScript With Continuations
From: Lawrence D'Oliv
Newsgroups: comp.lang.lisp
Organization: A noiseless patient Spider
Date: Wed, 5 Jun 2024 23:06 UTC
Path: eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail
From: ldo@nz.invalid (Lawrence D'Oliveiro)
Newsgroups: comp.lang.lisp
Subject: PostScript With Continuations
Date: Wed, 5 Jun 2024 23:06:47 -0000 (UTC)
Organization: A noiseless patient Spider
Lines: 85
Message-ID: <v3qr27$15i98$1@dont-email.me>
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Injection-Date: Thu, 06 Jun 2024 01:06:47 +0200 (CEST)
Injection-Info: dont-email.me; posting-host="f4d69e0b55552fd1d553b40d4565c4a9";
logging-data="1231144"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/F+qOCaoIz6BWT4F5SddpP"
User-Agent: Pan/0.158 (Avdiivka; )
Cancel-Lock: sha1:2kzN58GaKDFnqiF9pt17aMfxF9U=
View all headers

I’ve been messing around with a toy implementation (in Python) of a
variant of the old PostScript language that I call “GXScript”.

One of the features I added to it is continuations, à la Scheme and other
Lisps. I added an operator called “cexec”, which invokes a procedure you
pass to it but first pushes on the stack a continuation that, when
invoked, resumes execution from just after the cexec call.

Such continuations can be used in the all usual ways, for example to
implement looping:

{
/Count 5 ldef
/Top null ldef
{/Top exch lstore} cexec
{
/Break exch ldef
Count =
/Count Count 1 sub lstore
Count 0 eq {Break} if
Top
}
cexec
}
exec

Output:

5
4
3
2
1

Note that care has to be taken that no extra code inadvertently
occurs after the first cexec, since that would also be reexecuted when
“Top” is invoked. For example, the lines

/Top null ldef
{/Top exch lstore} cexec

cannot be written in a form such as

/Top {} cexec exch ldef

since you do not want the “exch ldef” to be reexecuted after “Top” has
been defined. Hence there is some slightly convoluted code to ensure
that 1) the “Top” variable is initially defined in the right block
scope, and 2) it gets assigned a continuation pointing at the right
point in the code.

Of course it would be fiddly to have to write this sort of thing out
every time you wanted to use continuations to implement a loop. Here
is a wrapper routine that is invoked with a procedure as argument:
that procedure is repeatedly invoked, each time passing it a
continuation that it can use to exit the loop.

/DoLoop
{
/Proc exch ldef
/Top null ldef
{/Top exch lstore} cexec
{Proc Top} cexec
}
ddef

DoLoop will take your loop-body procedure as argument, and execute it
repeatedly, each time pushing a continuation that can be used to exit the
loop, until you invoke that continuation. Example use:

/Count 5 ddef
{
/Break exch ldef # always pop arg even if not used
Count =
/Count Count 1 sub lstore
Count 0 eq {Break} if
}
DoLoop

The output is the same as before.

Note that DoLoop is defined as a procedure, not a macro. PostScript never
had macros, and I’m not sure I need to add them to GXScript.

The source is here <https://bitbucket.org/ldo17/gxscript/>.

1

rocksolid light 0.9.8
clearnet tor