On Sat, Dec 16, 2006 at 02:44:13PM -0500, Lynn Winebarger wrote:
> nash e. foster wrote:
> >On Sat, Dec 16, 2006 at 12:17:08AM -0500, Lynn Winebarger wrote:
> >I'm not sure I understand exactly what you're trying to do, but I'm
> >supposing that you want to track what line of a source file a comment is
> That is certainly one part. This is my first CL program (as opposed
> to Scheme program, of which I've many). I want to produce a
> summary/index of the packages, functions, macros, where they're located,
> and the source code comments that "attach" to them.
Cool. That'd be dead useful for lots of things, especially understanding
a CL compiler like SBCL. ;-)
> The best approach I can see (that doesn't involve writing a complete
> CL interpreter) is to capture the comments in the reader and track the
> forms to which they attach.
The nice thing about CL is that the compiler internals are all right
there for you to play with, so we ought to be able to hook your logic
into the existing compiler without having to rewrite anything. Anyway, I
like the basic idea.
> Then I have a macroexpand hook to check for expansions into the core
> defining forms. After a form has been expanded and evaluated, any
> comments attached to a form that didnt yield a definition can be moved
> "up" the form to the closest parent that yields a definition or other
> cataloged operator.
I'm not sure I understand the specifics, but that's okay. I'm still
relatively new to CL, as well. Sounds good, though. :-)
> >The setq/unwind-protect stuff is a good thought,
> Yes, I managed to completely disable my sbcl sessions while
> debugging before I put the unwind-protect in there...
> >So, the below uses this to count the lines of the source file and then
> >emit messages about the comments it sees:
> > (defun cataloging-read (stream)
> > (let ((*readtable* (copy-readtable nil))
> > (lines 0))
> > (set-macro-character
> > #\Newline (lambda (stream char)
> > (declare (ignore char) (ignore stream))
> > (incf lines)))
> Does the compiler recognize the lambda here as a constant and avoid
> creating it more than once?
I doubt it, but it shouldn't slow things down, much, as the cost to
compile that one lambda expression isn't that high. However, using a
closure would force it to get compiled just once, I believe:
(let* ((lines 0)
(lambda (stream car)
(declare (ignore char) (ignore (stream))
(defun cataloging-read (stream)
(let ((*readtable* (copy-readtable nil)))
You probably know this, but since CL (unlike Scheme) has a separate
namespace for functions, you can't call newline-counter by writing
simply (newline-counter ...) -- unless you change the enclosing let to
an flet. But, you can pass the newline-counter symbol to
set-macro-character without the #' quoting, which is what we're doing
> > (set-syntax-from-char #\Linefeed #\Newline)
> The hyperspec is a little terse on this (at least to a newbie like me)
> - the way I first read it, set-syntax-from-char would change the type
> of a character (from whitespace to terminating macro character) but
> not necessarily set the associated macro or set it to the same thing.
set-syntax-from-char to-char from-char &optional to-readtable from-readtable
This makes the syntax of to-char in to-readtable be the same as the
syntax of from-char in from-readtable. The to-readtable defaults to
the current readtable (the value of the global variable *readtable*),
and from-readtable defaults to nil, meaning to use the syntaxes from
the standard Lisp readtable.
So, I think the short answer is the the syntax function for #\Newline
gets copied to the slot for #\Linefeed in the default read table.
The Hyperspec is official, I guess, but I tend to use "Common Lisp the
Language, 2nd Ed" as it's a little easier to read and browse (has a
great index). Also, the Gigamonkeys book and PG's Lisp books are both
good, for various things.
> >You could use something similar to build annotations of the s-exps by
> >capturing comments and attaching them to the s-exps that span the line
> >the comment was on, which sounds like it might be what you're after.
> Yes, I cut the code down to just the amount necessary to show what
> was happening.
Seems reasonable; when you get something working be sure to post it here
so that the rest of us can use it. :-)
> >The nice thing about defining the cataloging-read function as above is
> >that you don't have to initialize it first, you just call it whenever
> >you're ready to process a source file.
> Yes, but I guess I'm conditioned to consider creating objects on
> every read as inefficient.
Extremely good point. After fiddling with it awhile, this is what I came
(let* ((lines 0)
(semi-colon-handler (lambda (stream char)
(declare (ignore char))
(read-line stream nil nil nil)))
(format t "~a: ~a~%" lines rest-of-line)
(eol-handler (lambda (stream char)
(declare (ignore char) (ignore stream))
(set-macro-character #\Newline eol-handler)
(set-syntax-from-char #\Linefeed #\Newline)
(set-macro-character #\; semi-colon-handler)
(defun cataloging-read (stream)
(let ((*readtable* new-readtable))
(read stream nil nil nil))))
Your mileage may vary. :-)
Please do not mock other religons
in your quest for the Spaghetti god.