[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
An interesting puzzle, or bug in CLISP?
- To: CLISP List <clisp-list@ma2s2.mathematik.uni-karlsruhe.de>
- Subject: An interesting puzzle, or bug in CLISP?
- From: "M. Thomas" <thommark@access.digex.net>
- Date: Wed, 12 Jul 1995 12:35:04 -0400 (EDT)
Here is an interesting puzzle involving the CLISP compiler,
PRINT-OBJECT, and reader-macros:
Assume from now on that the current package is COMMON-LISP-USER.
The following code does nothing useful, but demonstrates the problem.
We simply reprogram the Lisp reader so that a form such as !(a b c)
will be read in as a structure containing the list (a b c). In
addition, we add a PRINT-OBJECT method to ensure that a foo-structure
is printed so that it can be read back in.
-----BEGIN FILE good.lsp --------------------------
(defclass foo ()
((char :initarg :char :reader foo-char)
(form :initarg :form :reader foo-form)))
(set-macro-character #\!
#'(lambda (s c) (make-instance 'foo :char c :form (read s T NIL T))))
(defmethod print-object ((x foo) stream)
(format stream "~C~A" (foo-char x) (foo-form x)))
(setq test '!(a b c))
-----END FILE good.lsp --------------------------
This code works fine, as you can see ...
-----BEGIN good CLISP session -------
> (load "good.lsp")
;; Loading file good.lsp ...
WARNING:
The generic function #<GENERIC-FUNCTION PRINT-OBJECT> is being modified, but has already been called.
;; Loading of file good.lsp is finished.
T
> test
!(A B C)
> (type-of test)
FOO
-----END good CLISP session -------
If good.lsp is loaded then compiled, the resulting `good.fas' also
works fine.
Now suppose I modify the PRINT-OBJECT method very slightly, replacing
the format directive `~C' by the format directive `~A'. The result is
a file I'll call `bad.lsp'.
The punch line is that the _interpreted_ versions `good.lsp' and
`bad.lsp' both work, but the _compiled_ version `bad.fas' is
incorrect. Apparently the Lisp constant object denoted by !(a b c) in
the source code `bad.lsp' is stored incorrectly in `bad.fas',
resulting in the following misbehavior:
-----BEGIN bad CLISP session -------
> (load "bad.fas")
;; Loading file bad.fas ...
WARNING:
The generic function #<GENERIC-FUNCTION PRINT-OBJECT> is being modified, but has already been called.
;; Loading of file bad.fas is finished.
T
> test
*** - EVAL: variable TEST has no value
1. Break>
-----END bad CLISP session -------
The form (setq test '!(a b c)) is never evaluated, presumably because
the reader expands this form into erroneous byte-code.
Here's a clue to the puzzle (edited):
-----BEGIN clue -------
diff bad.fas good.fas
38c38,39
< #17Y( ... byte codes omitted .....)
---
> #20Y( ... byte codes omitted .....)
> SYSTEM::DO-FORMAT-CHARACTER
47c48
< #Y(#:TOP-LEVEL-FORM-4 #11Y( ... byte codes ...) #\!(A B C) TEST)
---
> #Y(#:TOP-LEVEL-FORM-4 #11Y( ... byte codes ...) !(A B C) TEST)
-----END clue ---------
You can see how the source constant denoted by !(a b c) is stored
differently in bad.fas and good.fas. But since, for example, the
format directives `~A' and `~C' behave the same way when applied to
characters, I expected the choice of format directive to make no
difference, in the intrepreted _or_ compiled versions.
BTW, `bad.fas' behaves properly if I change #\!(A B C) to !(A B C)
(I know it's not a good idea to edit *.fas).
What is going on here? Thanks for your insights!
Mark A. Thomas
thommark@access.digex.net