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

Learning LISP?

10 views
Skip to first unread message

Dave Vandervies

unread,
Nov 29, 1998, 3:00:00 AM11/29/98
to
I've recently started playing around with the LISP distributed along
with Slackware Linux (KCL aka GCL), and have a few questions.

Most important: Where can I find an online introduction to LISP for
perfectly intelligent people who have never used it before? (No `LISP
for Dummies', please.) I've used C for quite a while and BASIC before
that, and now I'm trying to expand my horizons. I've looked through
the FAQ for pointers to resources, but all of the introductory
materials listed are books, and for various reasons I can't buy a
book.

Also, what relevant reasons (besides novelty) are there to learn LISP?
I guess what I want to know is, what can I do in LISP that is more
difficult or not possible in C? Will a program to generate more
digits of Pi than any sane human would ever need be any easier in LISP
than other languages? What about prime numbers? A game such as
Tetris?

If anybody has any simple but nontrivial code samples they're willing
to share (with an explanation of what they do and how would also be
nice), I would probably also find those useful once I've figured out
the basic concepts of the language (beyond figuring out things like
the fact that
(caar (cons (cons 'a 'b) 'c))
returns
A
, that is.)


Thanks in advance.

Replies by email would be appreciated but I've been on usenet too long
to ask for them. >:]
--
If you feel the need to email me personally, feel free to correct the
obvious typo in my email address.

Steve Gonedes

unread,
Nov 29, 1998, 3:00:00 AM11/29/98
to

daveva#earthling.net (Dave Vandervies) writes:

< If anybody has any simple but nontrivial code samples they're willing
< to share (with an explanation of what they do and how would also be
< nice), I would probably also find those useful once I've figured out
< the basic concepts of the language (beyond figuring out things like
< the fact that
< (caar (cons (cons 'a 'b) 'c))
< returns
< A
< , that is.)

You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead
of car; some like this better, I'm surprised that c{ad}r haven't been
declared archaic.

Are you interested in fonts? I have some code that will extract the
string table of truetype fonts (like the name, copyright, etc), as
well as some stuff for working with type1, and pfm files.

You like graphics? I have a terminfo decompiler/parser and some other
stuff that you probably wouldn't like to re-write :) (those
conditionals are _real_ nasty - and then there's the blasted xterm
entry; it should not be that complicated to move a cursor to a given
address - it's very sick.)

I have a silly inference program that does the rule/assertion/forward
chaining, and a silly prolog thing.

What are you interested in?

Marco Antoniotti

unread,
Nov 30, 1998, 3:00:00 AM11/30/98
to
Steve Gonedes <jgon...@worldnet.att.net> writes:

> daveva#earthling.net (Dave Vandervies) writes:
>
> < If anybody has any simple but nontrivial code samples they're willing
> < to share (with an explanation of what they do and how would also be
> < nice), I would probably also find those useful once I've figured out
> < the basic concepts of the language (beyond figuring out things like
> < the fact that
> < (caar (cons (cons 'a 'b) 'c))
> < returns
> < A
> < , that is.)
>
> You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead
> of car; some like this better, I'm surprised that c{ad}r haven't been
> declared archaic.

Are you sure this is "allowed" by the ANSI standard? The third
argument to ACONS should be an alist (association list).

>
> Are you interested in fonts? I have some code that will extract the
> string table of truetype fonts (like the name, copyright, etc), as
> well as some stuff for working with type1, and pfm files.
>
> You like graphics? I have a terminfo decompiler/parser and some other
> stuff that you probably wouldn't like to re-write :) (those
> conditionals are _real_ nasty - and then there's the blasted xterm
> entry; it should not be that complicated to move a cursor to a given
> address - it's very sick.)
>
> I have a silly inference program that does the rule/assertion/forward
> chaining, and a silly prolog thing.
>
> What are you interested in?

Everything. Could you post them?

Cheers

--
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - (0)6 - 68 10 03 17, fax. +39 - (0)6 - 68 80 79 26
http://www.parades.rm.cnr.it

Gareth McCaughan

unread,
Nov 30, 1998, 3:00:00 AM11/30/98
to
Dave Vandervies wrote:

> Also, what relevant reasons (besides novelty) are there to learn LISP?
> I guess what I want to know is, what can I do in LISP that is more
> difficult or not possible in C? Will a program to generate more
> digits of Pi than any sane human would ever need be any easier in LISP
> than other languages? What about prime numbers? A game such as
> Tetris?

Well, if you're interested in digits of pi and prime numbers,
you might be amused by something I hacked up recently that would
be much harder and less fun in (say) C. (It could be done
adequately in other languages with Lisp-like semantics; someone
has done it in Python[1], for instance.)

It's a little library of functions for working with infinite-precision
real numbers. And I really *do* mean "infinite", not just "variable
and possibly very large": a real number is represented as a function
that generates approximations to the number. (More precisely, it's
a function that takes a positive integer argument n and returns some
number within 1/2 of n*x, however big n is. This is not necessarily
the best way to do it[2].)

In a few hundred lines you can implement the usual arithmetic
operations, conversion between my representation and infinite
continued fractions (represented, roughly, as infinite lists,
which again use the first-class-ness of functions to good effect),
some interesting transcendental numbers, roots of arbitrary
polynomials, and so on.

So, if I want to know what pi - root(17)*e^1/2 is, I say something
like

(real:print-via-pipe
(real:minus
real:pie ;; yes, there is a reason for not calling this PI.
(real:times
(real:nth-root-of-rational 2 17)
(real:e-to-the-rational 1/2))))

and it starts churning out digits, and won't stop until either I
interrupt it or it runs out of memory.

The whole thing is very primitive and rather slow, and there are
plenty of important things I haven't implemented yet and plenty
of others that are a real mess. But that's a few evenings' work
in Lisp; I couldn't have done anything like as much in a few
evenings of C. (And I'm relatively more expert in C than in Lisp.)

Things about Lisp that make it much easier:

- we already have arbitrary-precision rational numbers
- we have garbage collection, so it's safe to construct
complicated things on the fly and use them without the
need to keep a pointer around so we can free them
- we have closures as first-class objects, allowing a simple
representation of numbers and of "infinite lists"
- it's interactive, so debugging is easier and the resulting
system gets I/O and parsing for free


[1] real.py, by Jurjen Bos. Actually, I've just looked and it
*doesn't* do the same as my code (though it does do some
important things my code doesn't yet do).

[2] It would possibly be better to use a function that takes n
and returns a rational number within 1/n of x. The main reason
why I didn't do that is that I started off implementing this
in Scheme, and the Scheme implementation I use doesn't have
rationals.

In fact, better still would be a "mixed" representation
allowing you to record simple representations of simple numbers
(notably, rationals and algebraic numbers). Operations on these
could then work better.

--
Gareth McCaughan Dept. of Pure Mathematics & Mathematical Statistics,
gj...@dpmms.cam.ac.uk Cambridge University, England.

Erik Naggum

unread,
Nov 30, 1998, 3:00:00 AM11/30/98
to
* Steve Gonedes <jgon...@worldnet.att.net>

| You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead
| of car; some like this better, I'm surprised that c{ad}r haven't been
| declared archaic.

because CONS is not a list-building operator, it's a tree-building
operator. lists are just special cases of trees. and vice versa. :)

FIRST, SECOND,... and REST work on lists, C[AD]+R on trees.

#:Erik
--
The Microsoft Dating Program -- where do you want to crash tonight?

Dave Vandervies

unread,
Nov 30, 1998, 3:00:00 AM11/30/98
to
On 29 Nov 1998 20:15:04 GMT, Steve Gonedes <jgon...@worldnet.att.net>
wrote:

>daveva#earthling.net (Dave Vandervies) writes:
>
>< If anybody has any simple but nontrivial code samples they're willing
>< to share (with an explanation of what they do and how would also be
>< nice), I would probably also find those useful once I've figured out
>< the basic concepts of the language (beyond figuring out things like
>< the fact that
>< (caar (cons (cons 'a 'b) 'c))
>< returns
>< A
>< , that is.)
>

>You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead
>of car; some like this better, I'm surprised that c{ad}r haven't been
>declared archaic.

I would think (based on my admittedly very limited experience) that
c[ad]r would stack better than first and last (which I'm assuming is
the equivalent of cdr?) (though I noticed that the version I was using
only lets me stack them four deep without needing to do something like
(caadar (cdar foo)).) Perhaps the reason why I think this is an
advantage is that I'm enough of a C programmer to consider terseness
to be nearly equivalent to clarity. >:]

[''Are you interested in...'']


>
>What are you interested in?

At the moment I'm interested in anything that will help me learn the
language, though in general I tend to lean more toward
processing-based programs than I/O-based programs.

Dave Vandervies

unread,
Nov 30, 1998, 3:00:00 AM11/30/98
to
On 30 Nov 1998 16:52:48 +0000, Gareth McCaughan
<gj...@dpmms.cam.ac.uk> wrote:

>Dave Vandervies wrote:
>
>> Also, what relevant reasons (besides novelty) are there to learn LISP?
>> I guess what I want to know is, what can I do in LISP that is more
>> difficult or not possible in C? Will a program to generate more
>> digits of Pi than any sane human would ever need be any easier in LISP
>> than other languages? What about prime numbers? A game such as
>> Tetris?
>
>Well, if you're interested in digits of pi and prime numbers,
>you might be amused by something I hacked up recently that would
>be much harder and less fun in (say) C. (It could be done
>adequately in other languages with Lisp-like semantics; someone
>has done it in Python[1], for instance.)

This looks interesting. Would you be willing to send me a copy?
(Probably the easiest way for me would be to send it to me by email
<dav...@earthling.net> (either as plain text or as a MIME-encoded
compressed file (.tar.gz would be best)). If you'd prefer another
method please email me first to find out if I can handle it (I
probably can, but I'd like to make sure).

The way I did digits of Pi in C was to store the numbers as large
(currently working on 500000 digits) arrays of integers, and perform
the math (using the arctangent formula to calculate Pi, addition and
subtraction of these big numbers and multiplying/dividing them by
32-bit integers is all that is needed) using them as fixed-point
numbers. The major disadvantage to this (at least as I see it) is
that if I want more digits I have to start over at the beginning, and
that I can't get a partial value before it's done the n digits (though
with an evening of work I *might* be able to get it to store its
progress digit-by-digit as it works it out).

[...]

>In a few hundred lines you can implement the usual arithmetic
>operations, conversion between my representation and infinite
>continued fractions (represented, roughly, as infinite lists,
>which again use the first-class-ness of functions to good effect),
>some interesting transcendental numbers, roots of arbitrary
>polynomials, and so on.

That `infinite lists' looks interesting... is that a basic construct
of the language, or just something that the semantics of the language
makes easy?

[...]

>Things about Lisp that make it much easier:
>
> - we already have arbitrary-precision rational numbers
> - we have garbage collection, so it's safe to construct
> complicated things on the fly and use them without the
> need to keep a pointer around so we can free them

In general, I like to know where my memory is going, but I can see how
that would be a pain for something like this. >:]

> - we have closures as first-class objects, allowing a simple
> representation of numbers and of "infinite lists"

I may sound completely clueless here, but what does ``closures'' refer
to?

> - it's interactive, so debugging is easier and the resulting
> system gets I/O and parsing for free

Lyman S. Taylor

unread,
Dec 1, 1998, 3:00:00 AM12/1/98
to
In article <3663049...@news.tct.net>,

Dave Vandervies <daveva#earthling.net> wrote:
>On 29 Nov 1998 20:15:04 GMT, Steve Gonedes <jgon...@worldnet.att.net>
>wrote:
...

>>
>>of car; some like this better, I'm surprised that c{ad}r haven't been
>>declared archaic.

I think the standard says that it is "depreciated".

In the context of dealing with lists FIRST and REST seem much
more appropriate. If you are dealing with conses then CAR and CDR.
[ Yes, there is a difference if you want to respect the List Abstract
Data Type. Most of the List ADT operations take proper lists.
Whereas when you are dealing with conses you don't need to maintain
a proper list. If lift the "hood" then lists are cons and Lisp
sometimes doesn't do a good job of cleanly seperated the two. ]


>I would think (based on my admittedly very limited experience) that
>c[ad]r would stack better than first and last (which I'm assuming is
>the equivalent of cdr?)

FIRST and REST. LAST is something you use to "grab" a the list
cons cell. It is not the "dual" of FIRST.

>only lets me stack them four deep without needing to do something like
>(caadar (cdar foo)).) Perhaps the reason why I think this is an

It is highly likely that the cdar of any data structure you might
be using to solve a problem has a much more meaningful name than
CDAR. However, that is a question of style.


--

Lyman S. Taylor "Computers are too reliable to replace
(ly...@cc.gatech.edu) humans effectively."
Commander Nathan Spring, "Starcops"

Marco Antoniotti

unread,
Dec 1, 1998, 3:00:00 AM12/1/98
to
daveva#earthling.net (Dave Vandervies) writes:

> On 30 Nov 1998 16:52:48 +0000, Gareth McCaughan
> <gj...@dpmms.cam.ac.uk> wrote:
>
> [...]
>
> >In a few hundred lines you can implement the usual arithmetic
> >operations, conversion between my representation and infinite
> >continued fractions (represented, roughly, as infinite lists,
> >which again use the first-class-ness of functions to good effect),
> >some interesting transcendental numbers, roots of arbitrary
> >polynomials, and so on.
>
> That `infinite lists' looks interesting... is that a basic construct
> of the language, or just something that the semantics of the language
> makes easy?

The semantics of the language makes it easy. They are not part of the
language, but they are trivial to implement.

> [...]
>
> >Things about Lisp that make it much easier:
> >
> > - we already have arbitrary-precision rational numbers
> > - we have garbage collection, so it's safe to construct
> > complicated things on the fly and use them without the
> > need to keep a pointer around so we can free them
>
> In general, I like to know where my memory is going, but I can see how
> that would be a pain for something like this. >:]

No you don't! :) As it apperas in the C++ ARM, "C programmers think
memory management is too important to be left to the computer. Lisp
programmers think memory management is too important to be left to the
user."

In non-embedded applications (but I would not be so sure that embedded
applications would not benifit as well) Lisp programmer are right.

>
> > - we have closures as first-class objects, allowing a simple
> > representation of numbers and of "infinite lists"
>
> I may sound completely clueless here, but what does ``closures'' refer
> to?

They are what in Java Newspeak are called "anonymous classes". :)

> > - it's interactive, so debugging is easier and the resulting
> > system gets I/O and parsing for free

IMHO, this is the biggest plus ever.

Marco Antoniotti

unread,
Dec 1, 1998, 3:00:00 AM12/1/98
to

Erik Naggum <er...@naggum.no> writes:

> * Steve Gonedes <jgon...@worldnet.att.net>


> | You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead

> | of car; some like this better, I'm surprised that c{ad}r haven't been
> | declared archaic.
>

> because CONS is not a list-building operator, it's a tree-building
> operator. lists are just special cases of trees. and vice versa. :)
>
> FIRST, SECOND,... and REST work on lists, C[AD]+R on trees.
>

Right. How about a set of operations like

TREE-T tree top
TREE-LT tree left top
TREE-RT tree right top
TREE-LRT tree left rigth top
TREE-RLLRT tree right left left right top
TREE-[RL]+T
?

Cheers

Stig Hemmer

unread,
Dec 1, 1998, 3:00:00 AM12/1/98
to
daveva#earthling.net (Dave Vandervies) writes:
> I may sound completely clueless here, but what does ``closures''
> refer to?

It is essentially a set of variable bindings that aren't accessible in
the normal way, but which there are still function objects around that
has access to.

As a first example:

(defun make-adder (number)
(lambda (x) (+ x number)))

Calling (make-adder 8) returns a function that remembers that NUMBER
is 8, even after the function MAKE-ADDER returns.

If you then call (make-adder 6), you will have two function objects,
each of which have their own opinion of what NUMBER is:

(setf add-8 (make-adder 8))

(setf add-6 (make-adder 6))

(funcall add-8 10)
18

(funcall add-6 10)
16

Second example:

(let (womble)
(defun set-womble (x)
(setf womble x))
(defun get-womble ()
womble))

After the let has finished, the variable WOMBLE is only accessible
through the two functions.

Note: The last example is a not considered 'The Right Thing' by some
as it is hard to change the accessor functions without losing the
state of the closure variable, because running the (let) again creates
a _new_ WOMBLE.

The Right Thing, these days, is using CLOS for stuff like this.

Stig Hemmer, perl -e 'print "Not a Perl hacker.\n"'
Jack of a Few Trades.
"Just when you accept that life's a bitch, it has puppies."

Tim Bradshaw

unread,
Dec 1, 1998, 3:00:00 AM12/1/98
to
* Dave Vandervies wrote:

> That `infinite lists' looks interesting... is that a basic construct
> of the language, or just something that the semantics of the language
> makes easy?

It's something the semantics makes easy. You can represent a lazy
list as a pair where the second (cdr) element of the pair is a promise
to create more list on demand. And that in turn you can implement by
having operators DELAY which take an expression and produce an object
which is a promise to evaluate it later, and FORCE which takes a
promise and causes it to be evaluated.

An obvious way of representing a promise is as a function of no
arguments. Forcing the promise is just calling the function:

(defmacro delay (thing)
`(function (lambda ()
,thing)))

(defun force (promise)
(funcall promise))

DELAY must be a macro because it can not evaluate its arguments.

This has problems because you probably want to remember whether a
promise has been forced or not or you might get odd results. A
slightly more sophisticated version is:

(defstruct (promise
(:print-function
(lambda (p s k)
(declare (ignore k))
(print-unreadable-object (p s :type t :identity t)))))
thing
forced-p)

(defmacro delay (thing)
`(make-promise
:thing #'(lambda ()
,thing)
:forced-p nil))

(defun force (thing)
(typecase thing
(promise
(if (promise-forced-p thing)
(promise-thing thing)
(prog1
(setf (promise-thing thing)
(funcall (promise-thing thing)))
(setf (promise-forced-p thing) t))))
(t thing)))

--tim

Gareth McCaughan

unread,
Dec 1, 1998, 3:00:00 AM12/1/98
to
Dave Vandervies wrote:

> This looks interesting. Would you be willing to send me a copy?

Yes, I expect so. It's rather nasty at present, though. You have
been warned. :-)

> The way I did digits of Pi in C was to store the numbers as large
> (currently working on 500000 digits) arrays of integers, and perform
> the math (using the arctangent formula to calculate Pi, addition and
> subtraction of these big numbers and multiplying/dividing them by
> 32-bit integers is all that is needed) using them as fixed-point
> numbers. The major disadvantage to this (at least as I see it) is
> that if I want more digits I have to start over at the beginning, and
> that I can't get a partial value before it's done the n digits (though
> with an evening of work I *might* be able to get it to store its
> progress digit-by-digit as it works it out).

I also have to start at the beginning every time I compute pi
(although I believe there's an algorithm that avoids this).
I'm using the AGM algorithm, which converges rather fast.
Because Lisp has bignums, of course, I don't have all the
pain of working with arrays of 32-bit integers for everything.
I think my definition of "pi" is under 10 lines.

> That `infinite lists' looks interesting... is that a basic construct
> of the language, or just something that the semantics of the language
> makes easy?

The latter. I see that other people have already posted explanations,
so I shan't bother.

>> - we have garbage collection, so it's safe to construct
>> complicated things on the fly and use them without the
>> need to keep a pointer around so we can free them
>
> In general, I like to know where my memory is going, but I can see how
> that would be a pain for something like this. >:]

Yes. The point is that if you say something like x=f(g(y),h(z))
you need to have some way of freeing the intermediate stuff produced
by g,h. In Lisp you write

(setq x (f (g y) (h z)))

and that's it. In C you have to do something like

{ Foo * gy = g(y), hz=h(z);
x = f(gy,hz);
free(gy); free(hz);
}

which is a real nuisance. Of course, it becomes even more
unpleasant if you are doing more complicated operations than
this.

Dave Vandervies

unread,
Dec 1, 1998, 3:00:00 AM12/1/98
to
On 1 Dec 1998 02:28:19 -0500, ly...@cc.gatech.edu (Lyman S. Taylor)
wrote:

My opinions on style put (caadadar foo) ahead of a symbolic name for
that particular node of a tree, especially for (probably a bad
example, since I can't come up with a real-world situation where it
would be relevant) code where I have to access a significant
proportion of the 2^6 nodes 6 layers deep in a tree structure. Can
you imagine typing (first (first (rest (first (rest (first foo))))))
50 times? Of course, code that does something like that would have to
be documented well enough for the average programmer to easily
understand what it's doing and why, since at the moment I'm more
interested in maintainability than job security. >:]

Erik Naggum

unread,
Dec 2, 1998, 3:00:00 AM12/2/98
to
* ly...@cc.gatech.edu (Lyman S. Taylor)

| I think the standard says that it is "depreciated".

the word is "deprecated", but it is certainly not used about CAR/CDR/etc.

BTW, never "think" a standard says something. consult it and _know_.
<URL:http://www.harlequin.com/books/HyperSpec/>

Erik Naggum

unread,
Dec 2, 1998, 3:00:00 AM12/2/98
to
* daveva#earthling.net (Dave Vandervies)

| Can you imagine typing (first (first (rest (first (rest (first foo))))))
| 50 times?

how about (first (second (second (first foo))))?

in case you're referring to a tree, how about (tree-node foo 0 1 1 0)?
note that the order of the arguments is reversed from the above code.
the location in a tree is specified with the number of CDRs before a CAR.
other location specifications may be more useful in a given case.

| Of course, code that does something like that would have to be documented
| well enough for the average programmer to easily understand what it's
| doing and why, since at the moment I'm more interested in maintainability
| than job security. >:]

funny how that actually improves job security... :)

David Thornley

unread,
Dec 2, 1998, 3:00:00 AM12/2/98
to
In article <36646cc4...@news.tct.net>,

Dave Vandervies <daveva#earthling.net> wrote:
>On 1 Dec 1998 02:28:19 -0500, ly...@cc.gatech.edu (Lyman S. Taylor)
>wrote:
>
>>In article <3663049...@news.tct.net>,
>>Dave Vandervies <daveva#earthling.net> wrote:
>>>only lets me stack them four deep without needing to do something like
>>>(caadar (cdar foo)).) Perhaps the reason why I think this is an
>>
>> It is highly likely that the cdar of any data structure you might
>> be using to solve a problem has a much more meaningful name than
>> CDAR. However, that is a question of style.
>
>My opinions on style put (caadadar foo) ahead of a symbolic name for
>that particular node of a tree, especially for (probably a bad
>example, since I can't come up with a real-world situation where it
>would be relevant) code where I have to access a significant
>proportion of the 2^6 nodes 6 layers deep in a tree structure. Can

>you imagine typing (first (first (rest (first (rest (first foo))))))
>50 times? Of course, code that does something like that would have to

>be documented well enough for the average programmer to easily
>understand what it's doing and why, since at the moment I'm more
>interested in maintainability than job security. >:]
>
You're talking about 64 nodes of a tree, which obviously has
a certain defined structure. How stable is this structure going to
be? If you decide that the structure has to change, how easy is
it going to be to change it? You can of course search for all
examples of "caadadar" in your code. Are all of them applicable to
that data structure? Given a "caadar", does that make an abbreviated
reference to something that's a "cdar" of a base tree?

My opinion is that either you're doing a quick-and-dirty or you aren't.
If you're doing a quick-and-dirty with six-layer trees, you're in
trouble already, particularly if you're accessing a sample node 50
times. If you're not, then you should keep it maintainable and
changeable.

Of course, as you say, you could document it. My preferred method
of documentation would be something like

(defun foo-value (x)
(declaim inline)
(caadr (cadar x)))

and using foo-value, rather than write caadaadr (note the typo -
are you going to notice this any time you make it?) fifty times.


--
David H. Thornley | These opinions are mine. I
da...@thornley.net | do give them freely to those
http://www.thornley.net/~thornley/david/ | who run too slowly. O-

Gareth McCaughan

unread,
Dec 3, 1998, 3:00:00 AM12/3/98
to
I wrote:

> I think my definition of "pi" is under 10 lines.

I checked. It's actually 28, plus comments etc.

Steve Gonedes

unread,
Dec 8, 1998, 3:00:00 AM12/8/98
to

Marco Antoniotti <mar...@copernico.parades.rm.cnr.it> writes:

< Are you sure this is "allowed" by the ANSI standard? The third
< argument to ACONS should be an alist (association list).

It works in emacs lisp... my mistake.

< Everything. Could you post them?

Here's the binary type1 to ascii type1 converter. It takes a binary
font (pfb) and dumps an ascii font (pfa) suitable for sending to a
postscript printer. I'm still working on the other stuff though.

Would love to hear some comments of improvemnts and such.


#|
Some commentary...

Binary type1 fonts have three sections, an ascii section for the font
dictionary (fontname, encoding, etc.), a binary section for the
encoded charstrings (font program), and a final ascii section that is
usually just a bunch of zeros for faster decoding on printers.

A section header is six 8-bit bytes:
1 #x80 magic header
2 section type, 1 for ascii or 2 for binary
3-6 used to find the section size in 8-bit bytes (read-section-size).

|#

;;;; November 30, 1998 Monday 5:30 PM
;;;; Convert a binary type1 to an ascii type1 font

(in-package :user)

(defmacro t1-read-byte (stream)
`(ldb (byte 8 0)
(the fixnum (read-byte ,stream))))

(defmacro with-open-file-stream (direction type file/options &body forms)
`(with-open-file
(,@file/options :direction ,direction :element-type ,type)
,@forms))

(defmacro with-binary-input-stream (file/options &body forms)
`(with-open-file-stream :input '(unsigned-byte 8) ,file/options ,@forms))

(defmacro with-binary-output-stream (file/options &body forms)
`(with-open-file-stream :output '(unsigned-byte 8) ,file/options ,@forms))

(define-condition t1-error (file-error)
((file :initarg :file
:initform (error ":FILE required")
:reader t1-error-file)
(position :initarg :position
:initform (error ":POSITION required")
:reader t1-error-position)
(section :initarg :section
:initform (error ":SECTION required")
:reader t1-error-section))
(:report (lambda (condition stream)
(format stream
"~@<Invalid ~[header~;ascii~;binary~:;, unidentifiable~] ~
section reading file ~a at position ~d.~:@>"
(t1-error-section condition)
(t1-error-file condition)
(t1-error-position condition)))))

(defconstant +marker+ 128)

(defconstant +hex-vector+
;; `binary' equivalent of "0123456789ABCDEF"
#(48 49 50 51 52 53 54 55 56 57 65 66 67 68 69 70))

(defun read-section-size (stream)
"Return following section size as a 32-bit byte
Destructively advance binary-input-stream STREAM four 8-bit bytes"
(macrolet ((%ash (x y) `(ldb (byte 32 0) (ash ,x ,y))))
(ldb (byte 32 0)
(logior
(t1-read-byte stream)
(%ash (t1-read-byte stream) 8)
(%ash (t1-read-byte stream) 16)
(%ash (t1-read-byte stream) 24)))))

(defun section-length (stream)
;; values: section-size section-type
(let ((section-type (t1-read-byte stream)))
(values (read-section-size stream) section-type)))

(defun dump-ascii-section (size input output)
(loop repeat size
for byte of-type fixnum = (t1-read-byte input)
if (= byte 13) do
(write-byte 10 output)
else do (write-byte byte output)))

(declaim (inline write-hex-char))

(defun write-hex-char (byte output)
(declare (optimize speed (safety 0) (debug 0) (space 0)) (fixnum byte))
(macrolet ((%hex-byte (byte)
`(svref +hex-vector+ (the fixnum ,byte))))

(write-byte (%hex-byte (ldb (byte 8 4) byte)) output)
(write-byte (%hex-byte (ldb (byte 4 0) byte)) output)))

(defun dump-binary-section (size input output)
(let ((lines (floor size 32))
(extra-chars (mod size 32)))
(declare (optimize speed (space 0) (debug 0) (safety 0)))
(loop repeat lines
do (loop repeat 32
do (write-hex-char (t1-read-byte input) output))
do (write-byte 10 output))
(loop repeat extra-chars do
(write-hex-char (t1-read-byte input) output)
finally (write-byte 10 output))))

(defmacro section-assertion-bind ((size-var) section-type input-stream)
"Bind the variable SIZE-VAR to the length of the following section.
Check that the following section has the proper marker and is the type
indicated by SECTION-TYPE. Destructively modify the binary-input
input-stream by five 8-bit bytes."

(let ((local-size (gensym "SIZE"))
(local-section (gensym "SECTION"))
(section (gensym "SECTION"))
(stream (gensym "STREAM"))
let-bindings)
(assert (symbolp size-var) (size-var)
"SIZE-VAR must be a setf-able symbol, not ~s" size-var)

;; If the variables are constants then we can evaluate them more
;; than one time - so check for it. Is this bad idea?
(cond ((integerp section-type)
(setq section section-type))
(t (push `(,section ,section-type) let-bindings)))

(cond ((symbolp input-stream)
(setq stream input-stream))
(t (push `(,stream ,input-stream) let-bindings)))

`(let ,let-bindings
(if (/= (t1-read-byte ,stream) +marker+)
(error 't1-error
:file (file-namestring ,stream)
:section ,section :position (file-position ,stream))
(multiple-value-bind (,local-size ,local-section)
(section-length ,stream)
(if (/= ,local-section ,section)
(error 't1-error
:section ,section
:file (file-namestring ,stream)
:position (file-position ,stream))
(setq ,size-var ,local-size)))))))

(defun t1-ascii (font outfile)
(with-binary-input-stream (input font)
(with-binary-output-stream (output outfile :if-exists :supersede)
(let (section-size)
;; section 1
(section-assertion-bind (section-size) 1 input)
(dump-ascii-section section-size input output)
;; section 2
(section-assertion-bind (section-size) 2 input)
(dump-binary-section section-size input output)
;; section 3
(section-assertion-bind (section-size) 1 input)
(dump-ascii-section section-size input output)))))

;;; EOF

0 new messages