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

Lisp indenting tool

6 views
Skip to first unread message

Jeremy Smith

unread,
Jan 26, 2008, 2:16:47 PM1/26/08
to
Hi all,

I just wanted to talk about a pleasant Lisp experience.

Last week I spent 2 hours writing a source code indenter for Lisp. It would
indent each line with a tab for every parenthesis opened since the start of
the file.

It works perfectly, and deals with cases of quotes in strings, and escaped
quotes (in or out of strings), escaped slashes, comments, and escaped
parenthesis.

It's just 52 lines of source code!

I can't think of any other language I've used in which this would be
possible (or as pleasant to develop).

I won't bother posting the code because you can do this in EMACS - just
that I don't use EMACS. I might put the code on a Lisp code snippets
website.

Cheers,

Jeremy Smith BSc (Hons)

Xah Lee

unread,
Jan 26, 2008, 2:40:06 PM1/26/08
to
Jeremy Smith wrote:
> Last week I spent 2 hours writing a source code indenter for Lisp. It would
> indent each line with a tab for every parenthesis opened since the start of
> the file.
>
> It works perfectly, and deals with cases of quotes in strings, and escaped
> quotes (in or out of strings), escaped slashes, comments, and escaped
> parenthesis.
>
> ...

> I won't bother posting the code because you can do this in EMACS

Actually emacs can't do that. I hope it does but it won't. I tried to
complain here:

• A Simple Lisp Code Formatter
http://xahlee.org/emacs/lisp_formatter.html

If you post your code, i'd be happy to translate to emacs lisp for at
least my own use.

Xah
x...@xahlee.org
http://xahlee.org/

Jeremy Smith

unread,
Jan 26, 2008, 6:34:22 PM1/26/08
to
Xah Lee <x...@xahlee.org> wrote in
news:779175ef-2b39-42e3...@d70g2000hsb.googlegroups.com:

> Jeremy Smith wrote:
>> Last week I spent 2 hours writing a source code indenter for Lisp. It
>> woul
> d
>> indent each line with a tab for every parenthesis opened since the
>> start o
> f
>> the file.
>>
>> It works perfectly, and deals with cases of quotes in strings, and
>> escaped
>
>> quotes (in or out of strings), escaped slashes, comments, and escaped
>> parenthesis.
>>
>> ...
>> I won't bother posting the code because you can do this in EMACS
>
> Actually emacs can't do that. I hope it does but it won't. I tried to
> complain here:
>
> • A Simple Lisp Code Formatter
> http://xahlee.org/emacs/lisp_formatter.html
>
> If you post your code, i'd be happy to translate to emacs lisp for at
> least my own use.
>
> Xah
> x...@xahlee.org
> ∑ http://xahlee.org/
>
> ☄

Here it is. It's below.

It's indented itself! The perfect test...

Comments on this:

*To use, (reformat filename) will output to filename.lisp.
*Comments preceded by a semicolon are supported, and will be
indented to the same as the line before.
*#| style comments are not yet supported.
*The code below was indented using this code, to show you what it
can do
*I think it can cope with an escaped semicolon, but I'm not sure
*The indent character(s) is a tab, in this line: (append-format out-
filename "~a" "indent character goes here"))
*I think parenthesis 'trail', which means when they appear on a line
of their own, they are indented 1 less than they close.
*If this can be considered non-trivial, then I must be a good
programmer.
*It cannot cope with escaped #'s - the case here would be \\#) for
instance. \#) is a closed bracket character in a string, and \\#) is a #
followed by an unescaped ). I'll try and fix this.

Tell me if this code is useful.

All the best,

Jeremy.

(defun get-file-lines(filename)
(let ((in (open filename :if-does-not-exist nil)))
(get-stream-lines in)))

(defun get-stream-lines(in)
(let ((retval (list)))
(when in
(loop for line = (read-line in nil)
while line do
(push line retval))(close in))
(reverse retval)))

(defun quick-write-file(filename data &optional append)
(if append
(with-open-file (stream filename :direction :output :if-exists
:append :if-does-not-exist :create)
(write-sequence data stream))
(with-open-file (stream filename :direction :output)
(write-sequence data stream))))

;From On Lisp
(defmacro for ((var start stop) &body body)
(let ((gstop (gensym)))
`(do ((,var ,start (1+ ,var))
(,gstop ,stop))
((> ,var ,gstop))
,@body)))

(defun append-format(filename format data)
(quick-write-file filename (format NIL format data) T))

(defun reformat(filename)
(let ((lines(get-file-lines filename)))
(reformat-lines lines (format NIL "~a.lisp" filename))))

(defun reformat-lines(lines out-filename)
(let ((indent 0))
(dolist (line lines)
(setf indent (reformat-line line indent out-filename)))))

(defun reformat-line(line indent out-filename)
(dotimes (i indent)
(append-format out-filename "~a" " "))
;Go along the line
(dotimes (i (length line))
;Ignore #\( and #\)
(when (and (< (+ i 3) (length line)) (equal(subseq line i (+ i
3)) "#\\("))
(decf indent))
(when (and (< (+ i 3) (length line)) (equal(subseq line i (+ i
3)) "#\\)"))
(incf indent))
;If we find a quote, follow it until the next quote comes
along (a slash quote is the exception)
(when (equal (elt line i) #\")
(for (j (+ i 1) (-(length line)1))
;A quote is where we stop
(when (equal (elt line j) #\")
(unless (and (> j 3 ) (equal (subseq line (- j
2) (+ j 1)) "\\\\\""))
(setf i j)
(return)))
;When we get a slash, check for quote
(when (equal (elt line j) #\\)
(when (equal (elt line (+ j 1)) #\")
(incf j)))))
;If we find a bracket, indent goes up
(when (equal (elt line i) #\()
(incf indent))
(when (equal (elt line i) #\))
(decf indent))
(when (equal (elt line i) #\;)
(unless (and (> i 3 ) (equal (subseq line (- i 2) (+ i
1)) "#\\;"))
(return)))
)
(let ((count 0))
(dotimes (i (length line))
(if (equal (elt line i) #\Space)
(incf count)
(if (equal (elt line i) #\Tab)
(incf count)
(return))))
(append-format out-filename "~a" (subseq line count (length
line)))
(append-format out-filename "~%" "")
)
indent
);Should be indented by 1 if this indenting code works properly


Rainer Joswig

unread,
Jan 26, 2008, 7:49:23 PM1/26/08
to
In article <Xns9A31F037DF73Bje...@62.253.170.163>,
Jeremy Smith <nospam...@decompiler.org> wrote:

What you do is not 'reformat'. It is usually called 'reindent'.
Reformat would fill the code across the lines.

use (defun foo (bar) ... -> extra space between the function name
and the arglist

Check the use of comments: http://www.lispworks.com/documentation/HyperSpec/Body/02_ddb.htm
Single semicolon vs. double semicolon.

>
> (defun get-file-lines(filename)
> (let ((in (open filename :if-does-not-exist nil)))
> (get-stream-lines in)))

use WITH-OPEN-FILE

>
> (defun get-stream-lines(in)
> (let ((retval (list)))
> (when in
> (loop for line = (read-line in nil)
> while line do
> (push line retval))(close in))
> (reverse retval)))

Use COLLECT instead of do (push

So you read all lines and then write all lines?
Fortunately we have quick garbage collectors and large
memory, today.

>
> (defun quick-write-file(filename data &optional append)
> (if append
> (with-open-file (stream filename :direction :output :if-exists
> :append :if-does-not-exist :create)
> (write-sequence data stream))
> (with-open-file (stream filename :direction :output)
> (write-sequence data stream))))

What is quick about that?

>
> ;From On Lisp
> (defmacro for ((var start stop) &body body)
> (let ((gstop (gensym)))
> `(do ((,var ,start (1+ ,var))
> (,gstop ,stop))
> ((> ,var ,gstop))
> ,@body)))
>
> (defun append-format(filename format data)
> (quick-write-file filename (format NIL format data) T))

You are opening/closing the file all the time??? why?

Stuff like (format stream "~a" "foo") is sick.
Then you are again writing it, but this time with
WRITE-SEQUENCE. Opening and closing the file
all the time?

Jeremy Smith

unread,
Jan 26, 2008, 9:08:38 PM1/26/08
to
Rainer Joswig <jos...@lisp.de> wrote in
news:joswig-0B1E83....@news-europe.giganews.com:

> What you do is not 'reformat'. It is usually called 'reindent'.

I could make it reformat too.

> Reformat would fill the code across the lines.
>
> use (defun foo (bar) ... -> extra space between the function name
> and the arglist

Okay, I'll make it do this automatically.

I should point out that it's not far off being able to parse code trees,
which I've adapted it for in another bit of software.

> Check the use of comments:
> http://www.lispworks.com/documentation/HyperSpec/Body/02_ddb.htm
> Single semicolon vs. double semicolon.
>
>>
>> (defun get-file-lines(filename)
>> (let ((in (open filename :if-does-not-exist nil)))
>> (get-stream-lines in)))
>
> use WITH-OPEN-FILE

This stuff at the top is pretty slow, but won't be needed in the EMACS
version.

> Stuff like (format stream "~a" "foo") is sick.
> Then you are again writing it, but this time with
> WRITE-SEQUENCE. Opening and closing the file
> all the time?

It's called quick-write-file because it's quick to use (eg, no messing
around with file handles). I must admit in my other software where I use
it, there isn't a lot of file output (~3mb perhaps).

As for the double format, that's pretty bad, I'll try to fix it.

Jeremy.

Rainer Joswig

unread,
Jan 26, 2008, 9:32:55 PM1/26/08
to
In article <Xns9A3215C33C01Eje...@80.5.182.99>,
Jeremy Smith <nospam...@decompiler.org> wrote:

> Rainer Joswig <jos...@lisp.de> wrote in
> news:joswig-0B1E83....@news-europe.giganews.com:
>
> > In article <Xns9A31F037DF73Bje...@62.253.170.163>,
> > Jeremy Smith <nospam...@decompiler.org> wrote:
> >
> >> Xah Lee <x...@xahlee.org> wrote in
> >> news:779175ef-2b39-42e3...@d70g2000hsb.googlegroups.com
> >> :
> >>
> > What you do is not 'reformat'. It is usually called 'reindent'.
>
> I could make it reformat too.
>
> > Reformat would fill the code across the lines.
> >
> > use (defun foo (bar) ... -> extra space between the function name
> > and the arglist
>
> Okay, I'll make it do this automatically.

Indentation is already not that simple:

(destructuring-bind (a b) c
d)

(destructuring-bind (a
b) c
d)

(destructuring-bind (a b) (foo c
d)
d)

(destructuring-bind (a b)
(foo c
d)
d)

(loop for i ...
do ...)

(defun foo (a b &key c d
e f)
...)

If a parameter is declared as &body, then it is indented differently
If there is a macro, then the use of that macro later
in the file will be indented differently based on the parameter
list of the macro (and even based on the prefix (defsomething,
with-something, ...).
Then Lisp has a few special forms
http://www.lisp.org/HyperSpec/Body/sec_3-1-2-1-2-1.html#clspecialops
which have their own layout rules...


Reformatting is even more complex.
Especially since comments need to stay
at the right places.

Jeremy Smith

unread,
Jan 26, 2008, 10:42:30 PM1/26/08
to
Rainer Joswig <jos...@lisp.de> wrote in
news:joswig-43204C....@news-europe.giganews.com:

But this is just a tool to re-indent code that has already been formatted
by the author. In other words, useful for me, and good for doing some of
the 'heavy lifting' in code where stuff has been added at the wrong
indent.

Expecting a parenthesis-based indenter to handle arbitrary non-
syntactical forms is, I think, unreasonable.

Cheers,

Jeremy.

Patrick May

unread,
Jan 27, 2008, 11:17:53 AM1/27/08
to
Jeremy Smith <nospam...@decompiler.org> writes:
>> If you post your code, i'd be happy to translate to emacs lisp for
>> at least my own use.
>
> Here it is. It's below.
>
> It's indented itself! The perfect test...

I find this somewhat hard to read because of the non-idiomatic
formatting. Common Lisp, much moreso than most languages, has a
standard style. Emacs formats source according to this common style.
Running your source through Emacs and applying some minimal cleanup by
hand yields this:

(defun get-file-lines (filename)


(let ((in (open filename :if-does-not-exist nil)))
(get-stream-lines in)))

(defun get-stream-lines (in)
(let ((retval (list)))
(when in
(loop for line = (read-line in nil)
while line do
(push line retval))(close in))
(reverse retval)))

(defun quick-write-file (filename data &optional append)


(if append
(with-open-file (stream filename :direction :output :if-exists
:append :if-does-not-exist :create)
(write-sequence data stream))
(with-open-file (stream filename :direction :output)
(write-sequence data stream))))

;; From On Lisp
(defmacro for ((var start stop) &body body)
(let ((gstop (gensym)))
`(do ((,var ,start (1+ ,var))
(,gstop ,stop))
((> ,var ,gstop))
,@body)))

(defun append-format (filename format data)


(quick-write-file filename (format NIL format data) T))

(defun reformat (filename)
(let ((lines(get-file-lines filename)))
(reformat-lines lines (format NIL "~a.lisp" filename))))

(defun reformat-lines (lines out-filename)


(let ((indent 0))
(dolist (line lines)
(setf indent (reformat-line line indent out-filename)))))

(defun reformat-line (line indent out-filename)


(dotimes (i indent)
(append-format out-filename "~a" " "))

(dotimes (i (length line)) ; Go along the line


(when (and (< (+ i 3) (length line))

(equal(subseq line i (+ i 3)) "#\\(")) ; Ignore #\( and #\)


(decf indent))
(when (and (< (+ i 3) (length line))
(equal(subseq line i (+ i 3)) "#\\)"))
(incf indent))
; If we find a quote, follow it until the next quote comes along
; (a slash quote is the exception)
(when (equal (elt line i) #\")
(for (j (+ i 1) (-(length line)1))

(when (equal (elt line j) #\") ; A quote is where we stop


(unless (and (> j 3 )
(equal (subseq line (- j 2) (+ j 1)) "\\\\\""))
(setf i j)
(return)))
; When we get a slash, check for quote
(when (equal (elt line j) #\\)
(when (equal (elt line (+ j 1)) #\")
(incf j)))))

(when (equal (elt line i) #\() ; If we find a bracket, indent goes up


(incf indent))
(when (equal (elt line i) #\))
(decf indent))
(when (equal (elt line i) #\;)
(unless (and (> i 3 )
(equal (subseq line (- i 2) (+ i 1)) "#\\;"))
(return))))
(let ((count 0))
(dotimes (i (length line))
(if (equal (elt line i) #\Space)
(incf count)
(if (equal (elt line i) #\Tab)
(incf count)
(return))))
(append-format out-filename "~a" (subseq line count (length line)))
(append-format out-filename "~%" ""))
indent)

This is much more idiomatic than the output of your function.

Regards,

Patrick

------------------------------------------------------------------------
S P Engineering, Inc. | Large scale, mission-critical, distributed OO
| systems design and implementation.
p...@spe.com | (C++, Java, Common Lisp, Jini, middleware, SOA)

Xah Lee

unread,
Jan 27, 2008, 2:33:01 PM1/27/08
to
Patrick May <p...@spe.com> wrote:
> I find this somewhat hard to read because of the non-idiomatic
> formatting. Common Lisp, much moreso than most languages, has a
> standard style. Emacs formats source according to this common style.

Providing a automatic formating tool is important, because:

On the whole, a simple formatting by lexical analysis in not going to
be as pretty as manual formatting. However, it is my opinion, if
lispers adapts to such a uniform, simple, machine-produced
auto-formatting, the impact on lisp community considered as whole,
will be tremendous. It would get rid of the “source code formatting
style” literature and debates for good, because all coders will be
accustomed to this machine-produced, uniform, style, when they begin
to learn lisp. (each coder can set some personal preferences to the
auto-formatter if she so wishes, and re-format entire source code on
the fly) Once a language's source code are presented in a uniform
style universally, it would fundamentally influence the idioms and
program constructs lisp coders actually produce. (this is a advantage
the Python language offers transparently.)

• A Simple Lisp Code Formatter
http://xahlee.org/emacs/lisp_formatter.html

Xah
x...@xahlee.org
http://xahlee.org/

Patrick May

unread,
Jan 27, 2008, 2:43:18 PM1/27/08
to
Xah Lee <x...@xahlee.org> writes:
> Patrick May <p...@spe.com> wrote:
>> I find this somewhat hard to read because of the non-idiomatic
>> formatting. Common Lisp, much moreso than most languages, has a
>> standard style. Emacs formats source according to this common
>> style.
>
> Providing a automatic formating tool is important, because:
>
> On the whole, a simple formatting by lexical analysis in not going
> to be as pretty as manual formatting. However, it is my opinion, if
> lispers adapts to such a uniform, simple, machine-produced
> auto-formatting, the impact on lisp community considered as whole,
> will be tremendous.

Any strategy that starts with "First, we change the world." is
doomed to failure. The current Lisp standard formatting evolved over
many years and is remarkably usable and readable with a minimum
investment in time. The suggestion that every existing Lisp
programmer should change his or her style is a non-starter.

Sincerely,

Xah Lee

unread,
Jan 27, 2008, 3:23:03 PM1/27/08
to
Patrick May <p...@spe.com> wrote:
> Any strategy that starts with "First, we change the world." is
> doomed to failure. The current Lisp standard formatting evolved over
> many years and is remarkably usable and readable with a minimum
> investment in time. The suggestion that every existing Lisp
> programmer should change his or her style is a non-starter.

We don't have to view it as declaring a revolution. We can just let
new ideas live.

The lisp's lack of a formatting tool, for example is personally a
problem for me. My opinion is that there should be some automatic
formatting tool for language like lisp that is well designed and has
regular syntax, but to my GREAT SURPRISE, i found out there is not
when i started to get serious with emacs lisp in 2005. Apparently
Jeremy Smith is running into the same problem, and he spend time to
fix it at least for himself.

With so many lisp gurus here, can someone take this problem and write
a parser that does formatting?

For example, i want to be able to, for example in emacs, press a
button while cursor is on this code:

(defun previous-user-buffer () "Switch to the next user buffer in
cyclic order." (interactive) (previous-buffer) (let ((i 0)) (while
(and (string-match "^*" (buffer-name)) (< i 10)) (setq i (1+ i))
(previous-buffer) )))

then it turns into this:

(defun previous-user-buffer
() "Switch to the next user buffer in cyclic order."
(interactive)
(previous-buffer)
(let
((i 0))
(while
(and
(string-match "^*" (buffer-name))
(< i 10))
(setq i (1+ i))
(previous-buffer))))

and vice versa.
(the above formatting has 2 simple rules:
1. start new line and indent n times for each opening paren that's has
n levels nesting.
2. The only exception is don't start new line if the complete sexp is
less than 70 chars.
)

I don't think it is a good idea: but the automatic formatting tool can
produce format in lisp's traditional style too. If lispers like the
traditional formatting, it will still be great benefit to have such a
formatting tool.

Xah
x...@xahlee.org
http://xahlee.org/

Damien Kick

unread,
Jan 27, 2008, 3:45:57 PM1/27/08
to
Jeremy Smith wrote:

> Here [the code to indent lisp] is. It's below.

The lisp pretty printer is still one of the biggest wholes in my
knowledge of lisp. To approach the problem of indenting lisp code
without using the pretty printer seems to be missing out on yet another
one of the big powerhouses of the language. To just use some silly
chunk of lisp code I've written as an example, the pretty printer
already does a good job of indenting it, straight out of the box.

PG-USER> (setq *print-pretty* nil)
NIL
PG-USER> *form*
(DEFUN RECV-OCTET (FN TELNET-STREAM &OPTIONAL EOF-ERROR-P EOF-VALUE)
(WITH-ACCESSORS ((TCP-STREAM TELNET-TCP-STREAM) (SUPPRESS-GO-AHEAD
TELNET-SUPPRESS-GO-AHEAD) (WAIT-FOR-TERMINAL-TYPE-SUBOPTION
TELNET-WAIT-FOR-TERMINAL-TYPE-SUBOPTION)) TELNET-STREAM (LABELS
((%READ-BYTE NIL (READ-BYTE TCP-STREAM EOF-ERROR-P EOF-VALUE))
(%RECV-WILL (OPTION-BYTE-CODE) (COND ((= OPTION-BYTE-CODE
+SUPPRESS-GO-AHEAD/BYTE-CODE+) (SETQ SUPPRESS-GO-AHEAD T)) (T (WARN
"Received unknown option ~S." OPTION-BYTE-CODE)))) (%RECV-DO
(OPTION-BYTE-CODE) (COND ((= OPTION-BYTE-CODE +TERMINAL-TYPE/BYTE-CODE+)
(SEND-WILL +TERMINAL-TYPE/BYTE-CODE+ TELNET-STREAM) (SETQ
WAIT-FOR-TERMINAL-TYPE-SUBOPTION T)) (T (SEND-WILL-NOT OPTION-BYTE-CODE
TELNET-STREAM)))) (%RECV-DO-NOT (OPTION-BYTE-CODE) OPTION-BYTE-CODE)
(%RECV-SB (OPTION-BYTE-CODE) (COND ((= OPTION-BYTE-CODE
+TERMINAL-TYPE/BYTE-CODE+) (UNLESS WAIT-FOR-TERMINAL-TYPE-SUBOPTION
(WARN "Received SB ~S before having sent a ~
WILL TERMINAL-TYPE" OPTION-BYTE-CODE)) (LET
((SEND/BYTE-CODE 1)) (WHEN (= (%READ-BYTE) SEND/BYTE-CODE) (ASSERT (AND
(= (%READ-BYTE) +IAC/BYTE-CODE+) (= (%READ-BYTE) +SE/BYTE-CODE+)))
(SEND-SUB-TERMINAL-TYPE-IS TELNET-STREAM)))) (T (WARN "Received unknown
SB option ~S." OPTION-BYTE-CODE) (LOOP FOR OCTET = (%READ-BYTE) UNTIL
(OR (NOT OCTET) (= OCTET +SE/BYTE-CODE+))))))) (LOOP FOR BYTE =
(%READ-BYTE) WHILE BYTE IF (= BYTE +IAC/BYTE-CODE+) DO (LET
((COMMAND-BYTE-CODE (%READ-BYTE))) (COND ((= COMMAND-BYTE-CODE
+IAC/BYTE-CODE+) (RETURN (FUNCALL FN COMMAND-BYTE-CODE))) ((=
COMMAND-BYTE-CODE +WILL/BYTE-CODE+) (%RECV-WILL (%READ-BYTE))) ((=
COMMAND-BYTE-CODE +DO/BYTE-CODE+) (%RECV-DO (%READ-BYTE))) ((=
COMMAND-BYTE-CODE +DO-NOT/BYTE-CODE+) (%RECV-DO-NOT (%READ-BYTE))) ((=
COMMAND-BYTE-CODE +SB/BYTE-CODE+) (%RECV-SB (%READ-BYTE))) (T (LET
((UNKNOWN (%READ-BYTE))) (WARN "Received command-byte-code ~S and ~
unknown byte-code ~S."
COMMAND-BYTE-CODE UNKNOWN))))) ELSE DO (UNLESS SUPPRESS-GO-AHEAD (WARN
"Have received data before WILL SUPPRESS-GO-AHEAD.")) (RETURN (FUNCALL
FN BYTE))))))
PG-USER> (setq *print-pretty* t)
T
PG-USER> *form*
(DEFUN RECV-OCTET (FN TELNET-STREAM &OPTIONAL EOF-ERROR-P EOF-VALUE)
(WITH-ACCESSORS ((TCP-STREAM TELNET-TCP-STREAM)
(SUPPRESS-GO-AHEAD TELNET-SUPPRESS-GO-AHEAD)
(WAIT-FOR-TERMINAL-TYPE-SUBOPTION
TELNET-WAIT-FOR-TERMINAL-TYPE-SUBOPTION))
TELNET-STREAM
(LABELS ((%READ-BYTE NIL
(READ-BYTE TCP-STREAM EOF-ERROR-P EOF-VALUE))
(%RECV-WILL (OPTION-BYTE-CODE)
(COND ((= OPTION-BYTE-CODE
+SUPPRESS-GO-AHEAD/BYTE-CODE+)
(SETQ SUPPRESS-GO-AHEAD T))
(T
(WARN "Received unknown option ~S."
OPTION-BYTE-CODE))))
(%RECV-DO (OPTION-BYTE-CODE)
(COND ((= OPTION-BYTE-CODE +TERMINAL-TYPE/BYTE-CODE+)
(SEND-WILL +TERMINAL-TYPE/BYTE-CODE+
TELNET-STREAM)
(SETQ WAIT-FOR-TERMINAL-TYPE-SUBOPTION T))
(T
(SEND-WILL-NOT OPTION-BYTE-CODE TELNET-STREAM))))
(%RECV-DO-NOT (OPTION-BYTE-CODE) OPTION-BYTE-CODE)
(%RECV-SB (OPTION-BYTE-CODE)
(COND ((= OPTION-BYTE-CODE +TERMINAL-TYPE/BYTE-CODE+)
(UNLESS WAIT-FOR-TERMINAL-TYPE-SUBOPTION
(WARN "Received SB ~S before having sent a ~
WILL TERMINAL-TYPE" OPTION-BYTE-CODE))
(LET ((SEND/BYTE-CODE 1))
(WHEN (= (%READ-BYTE) SEND/BYTE-CODE)
(ASSERT (AND
(= (%READ-BYTE) +IAC/BYTE-CODE+)
(= (%READ-BYTE) +SE/BYTE-CODE+)))
(SEND-SUB-TERMINAL-TYPE-IS TELNET-STREAM))))
(T
(WARN "Received unknown SB option ~S."
OPTION-BYTE-CODE)
(LOOP FOR OCTET = (%READ-BYTE) UNTIL
(OR (NOT OCTET)
(= OCTET +SE/BYTE-CODE+)))))))
(LOOP FOR BYTE = (%READ-BYTE) WHILE BYTE IF
(= BYTE +IAC/BYTE-CODE+) DO
(LET ((COMMAND-BYTE-CODE (%READ-BYTE)))
(COND ((= COMMAND-BYTE-CODE +IAC/BYTE-CODE+)
(RETURN (FUNCALL FN COMMAND-BYTE-CODE)))
((= COMMAND-BYTE-CODE +WILL/BYTE-CODE+)
(%RECV-WILL (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO/BYTE-CODE+)
(%RECV-DO (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO-NOT/BYTE-CODE+)
(%RECV-DO-NOT (%READ-BYTE)))
((= COMMAND-BYTE-CODE +SB/BYTE-CODE+)
(%RECV-SB (%READ-BYTE)))
(T
(LET ((UNKNOWN (%READ-BYTE)))
(WARN "Received command-byte-code ~S and ~
unknown byte-code ~S."
COMMAND-BYTE-CODE UNKNOWN)))))
ELSE DO
(UNLESS SUPPRESS-GO-AHEAD
(WARN "Have received data before WILL SUPPRESS-GO-AHEAD."))
(RETURN (FUNCALL FN BYTE))))))
PG-USER>

What I would like to know is how to customize it to get it to indent my
own macros or short snippets of code like I would want them to be
indented. For example,

PG-USER> (pprint '(defun foo (n) (+ n 10)))

(DEFUN FOO (N) (+ N 10))
; No value
PG-USER>

How would you go about having the pretty printer always produce

(defun foo (n)
(+ n 10))

Actually, I'm surprised to find that the pretty printer already does
what I want with one of my own silly little macros.

(defmacro with-open-files ((&rest args) &body forms)
"Multiple WITH-OPEN-FILE in a LETish syntax."
(reduce (lambda (arg accumulator)
`(with-open-file ,arg ,accumulator))
args :from-end t :initial-value `(progn ,@forms)))

PG-USER> (pprint '(with-open-files ((in "input.txt") (out "output.txt"
:direction :output :if-exists :supersede)) (loop for line = (read-line
in nil) while line do (write-line (transform-line line) out))))

(WITH-OPEN-FILES ((IN "input.txt")
(OUT "output.txt" :DIRECTION :OUTPUT :IF-EXISTS
:SUPERSEDE))
(LOOP FOR LINE = (READ-LINE IN NIL) WHILE LINE DO
(WRITE-LINE (TRANSFORM-LINE LINE) OUT)))
; No value
PG-USER>

It must key off of the fact that the form starts with WITH? <laugh>
Okay, I can't find a quick example of the pretty printer failing to
already just do what I would want it to do.

No, wait. Actually, it doesn't quite automagically do what I would do
"by hand" with LOOP forms. When the pretty printer produces

(LOOP FOR BYTE = (%READ-BYTE) WHILE BYTE IF
(= BYTE +IAC/BYTE-CODE+) DO
(LET ((COMMAND-BYTE-CODE (%READ-BYTE)))
(COND ((= COMMAND-BYTE-CODE +IAC/BYTE-CODE+)
(RETURN (FUNCALL FN COMMAND-BYTE-CODE)))
((= COMMAND-BYTE-CODE +WILL/BYTE-CODE+)
(%RECV-WILL (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO/BYTE-CODE+)
(%RECV-DO (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO-NOT/BYTE-CODE+)
(%RECV-DO-NOT (%READ-BYTE)))
((= COMMAND-BYTE-CODE +SB/BYTE-CODE+)
(%RECV-SB (%READ-BYTE)))
(T
(LET ((UNKNOWN (%READ-BYTE)))
(WARN "Received command-byte-code ~S and ~
unknown byte-code ~S."
COMMAND-BYTE-CODE UNKNOWN))))))

I would have written this "by hand" as

(LOOP FOR BYTE = (%READ-BYTE)
WHILE BYTE
IF (= BYTE +IAC/BYTE-CODE+)
DO (LET ((COMMAND-BYTE-CODE (%READ-BYTE)))
(COND ((= COMMAND-BYTE-CODE +IAC/BYTE-CODE+)
(RETURN (FUNCALL FN COMMAND-BYTE-CODE)))
((= COMMAND-BYTE-CODE +WILL/BYTE-CODE+)
(%RECV-WILL (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO/BYTE-CODE+)
(%RECV-DO (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO-NOT/BYTE-CODE+)
(%RECV-DO-NOT (%READ-BYTE)))
((= COMMAND-BYTE-CODE +SB/BYTE-CODE+)
(%RECV-SB (%READ-BYTE)))
(T
(LET ((UNKNOWN (%READ-BYTE)))
(WARN "Received command-byte-code ~S and ~
unknown byte-code ~S."
COMMAND-BYTE-CODE UNKNOWN))))))

With the LOOP keywords on there own line, as much as possible. Would
anyone be willing to learn me how to get the pretty printer to do the
same thing? I have a feeling, though, that the non-sexp nature of LOOP
might make this a difficult task.

Rainer Joswig

unread,
Jan 27, 2008, 3:48:56 PM1/27/08
to
In article
<8e6f0680-5815-4057...@t1g2000pra.googlegroups.com>,
Xah Lee <x...@xahlee.org> wrote:

But Common Lisp has that. The function is called PPRINT.
It is based on a huge machinery defined in ANSI Common Lisp.

See here:
http://www.lispworks.com/documentation/HyperSpec/Body/22_b.htm

See this paper: XP, a Common Lisp pretty printing system.

http://citeseer.ist.psu.edu/waters89xp.html


CL-USER> (pprint '(defun previous-user-buffer () "Switch to the next user buffer in


cyclic order." (interactive) (previous-buffer) (let ((i 0)) (while
(and (string-match "^*" (buffer-name)) (< i 10)) (setq i (1+ i))

(previous-buffer) ))))

(DEFUN PREVIOUS-USER-BUFFER ()


"Switch to the next user buffer in
cyclic order."

(INTERACTIVE)
(PREVIOUS-BUFFER)
(LET ((I 0))
(WHILE (AND (STRING-MATCH "^*" (BUFFER-NAME)) (< I 10))
(SETQ I (1+ I)) (PREVIOUS-BUFFER))))

The Pretty Printer has also be used to 'pretty print'
other languages, since the machinery is available
to the developer.

You want it downcased?


CL-USER> (let ((*print-case* :downcase)) (pprint '(defun previous-user-buffer () "Switch to the next user buffer in


cyclic order." (interactive) (previous-buffer) (let ((i 0)) (while
(and (string-match "^*" (buffer-name)) (< i 10)) (setq i (1+ i))

(previous-buffer) )))))

(defun previous-user-buffer ()
"Switch to the next user buffer in
cyclic order."
(interactive)
(previous-buffer)
(let ((i 0))
(while (and (string-match "^*" (buffer-name)) (< i 10))
(setq i (1+ i)) (previous-buffer))))


This is standard ANSI CL. It deals with Lisp code as data.
It does not deal with Lisp code as text. Though as a
trivial exercise you can do:

CL-USER> (defparameter *source-code-string* "(defun previous-user-buffer () \"Switch to the next user buffer in


cyclic order.\" (interactive) (previous-buffer) (let ((i 0)) (while
(and (string-match \"^*\" (buffer-name)) (< i 10)) (setq i (1+ i))

(previous-buffer) )))")
*SOURCE-CODE-STRING*

CL-USER> (type-of *source-code-string*)
(SIMPLE-BASE-STRING 220)

CL-USER> (let ((*print-case* :downcase)) (pprint (read-from-string *source-code-string*)))


(defun previous-user-buffer ()
"Switch to the next user buffer in
cyclic order."
(interactive)
(previous-buffer)
(let ((i 0))
(while (and (string-match "^*" (buffer-name)) (< i 10))

(setq i (1+ i)) (previous-buffer)))); No value

>
> and vice versa.
> (the above formatting has 2 simple rules:
> 1. start new line and indent n times for each opening paren that's has
> n levels nesting.
> 2. The only exception is don't start new line if the complete sexp is
> less than 70 chars.
> )
>
> I don't think it is a good idea: but the automatic formatting tool can
> produce format in lisp's traditional style too. If lispers like the
> traditional formatting, it will still be great benefit to have such a
> formatting tool.
>
> Xah
> x...@xahlee.org
> ∑ http://xahlee.org/
>

> ?

John Thingstad

unread,
Jan 27, 2008, 4:17:59 PM1/27/08
to
På Sun, 27 Jan 2008 21:23:03 +0100, skrev Xah Lee <x...@xahlee.org>:

> (defun previous-user-buffer () "Switch to the next user buffer in
> cyclic order." (interactive) (previous-buffer) (let ((i 0)) (while
> (and (string-match "^*" (buffer-name)) (< i 10)) (setq i (1+ i))
> (previous-buffer) )))

like this perhaps?

CL-USER 1 > (pprint '(defun previous-user-buffer () "Switch to the next

user buffer in cyclic order." (interactive) (previous-buffer) (let ((i
0)) (while (and (string-match "^*" (buffer-name)) (< i 10)) (setq i

(1+ i)) (previous-buffer) ))))

(DEFUN PREVIOUS-USER-BUFFER ()


"Switch to the next user buffer in cyclic order."

(INTERACTIVE)
(PREVIOUS-BUFFER)
(LET ((I 0))
(WHILE
(AND (STRING-MATCH "^*" (BUFFER-NAME)) (< I 10))
(SETQ I (1+ I))
(PREVIOUS-BUFFER))))

Or in Emacs <meta>-x indent-form

--------------
John Thingstad

John Thingstad

unread,
Jan 27, 2008, 4:42:43 PM1/27/08
to
På Sun, 27 Jan 2008 21:45:57 +0100, skrev Damien Kick
<dk...@earthlink.net>:

>
> With the LOOP keywords on there own line, as much as possible. Would
> anyone be willing to learn me how to get the pretty printer to do the
> same thing? I have a feeling, though, that the non-sexp nature of LOOP
> might make this a difficult task.

Indeed. Of cource the problem goes away if you use iterate package instead.

--------------
John Thingstad

philip....@gmail.com

unread,
Jan 27, 2008, 4:48:43 PM1/27/08
to
On Jan 27, 8:48 pm, Rainer Joswig <jos...@lisp.de> wrote:

> But Common Lisp has that. The function is called PPRINT.
> It is based on a huge machinery defined in ANSI Common Lisp.

This is possibly CLISP specific: do any of the many options to control
PPRINT force it to leave an empty lambda list alone rather than
outputting it as NIL? I see that by default CLISP turns:

(pprint '(defun hello () (print "hello")))

into:

(DEFUN HELLO NIL (PRINT "hello"))

which isn't ideal in an editor or similar. Apologies if I'm missing
something obvious in the Hyperspec.

Thanks,

--
Phil Armitage
http://phil.nullable.eu/

Juho Snellman

unread,
Jan 27, 2008, 4:52:31 PM1/27/08
to
Damien Kick <dk...@earthlink.net> writes:
> How would you go about having the pretty printer always produce
>
> (defun foo (n)
> (+ n 10))

Something like:

(set-pprint-dispatch
'(cons (eql defun))
(lambda (stream list &rest ignore)
(declare (ignore ignore))
(format stream "~:<~^~W~^ ~@_~:I~W~^ ~:_~A~1I~@{ ~:@_~W~}~:>" list)))

Modulo proper formatting of the lambda-list, would be a lot more
complicated (and for which your implementation's pretty printer
probably already has some existing code).



> It must key off of the fact that the form starts with WITH? <laugh>

More probably on the macro using &BODY instead of &REST.



> With the LOOP keywords on there own line, as much as possible. Would
> anyone be willing to learn me how to get the pretty printer to do the
> same thing? I have a feeling, though, that the non-sexp nature of LOOP
> might make this a difficult task.

There is a pprinter for LOOP in CMUCL (code/pprint-loop.lisp) which is
originally from Water's XP pretty printer. You should be able to use
it with any other implementation too. It'd format the loop like this:

(LOOP FOR BYTE = (%READ-BYTE)
WHILE BYTE
IF (= BYTE +IAC/BYTE-CODE+)
DO (LET ((COMMAND-BYTE-CODE (%READ-BYTE)))
(COND
((= COMMAND-BYTE-CODE +IAC/BYTE-CODE+)
(RETURN (FUNCALL FN COMMAND-BYTE-CODE)))
((= COMMAND-BYTE-CODE +WILL/BYTE-CODE+)
(%RECV-WILL (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO/BYTE-CODE+)
(%RECV-DO (%READ-BYTE)))
((= COMMAND-BYTE-CODE +DO-NOT/BYTE-CODE+)
(%RECV-DO-NOT (%READ-BYTE)))
((= COMMAND-BYTE-CODE +SB/BYTE-CODE+)
(%RECV-SB (%READ-BYTE)))
(T
(LET ((UNKNOWN (%READ-BYTE)))
(WARN "Received command-byte-code ~S and ~
unknown byte-code ~S."
COMMAND-BYTE-CODE UNKNOWN)))))

--
Juho Snellman

Xah Lee

unread,
Jan 27, 2008, 8:36:15 PM1/27/08
to
"John Thingstad" <jpth...@online.no> wrote:
> like this perhaps?
>
> CL-USER 1 > (pprint '(defun previous-user-buffer () "Switch to the next

> user buffer in cyclic order." (interactive) (previous-buffer) (let ((i
> 0)) (while (and (string-match "^*" (buffer-name)) (< i 10)) (setq i
> (1+ i)) (previous-buffer) ))))
>
> (DEFUN PREVIOUS-USER-BUFFER ()
> "Switch to the next user buffer in cyclic order."
> (INTERACTIVE)
> (PREVIOUS-BUFFER)
> (LET ((I 0))
> (WHILE
> (AND (STRING-MATCH "^*" (BUFFER-NAME)) (< I 10))
> (SETQ I (1+ I))
> (PREVIOUS-BUFFER))))
>
> Or in Emacs <meta>-x indent-form

Thanks John Thingstad and Rainer Joswig.

How can i have something similar in emacs lisp though? I couldn't find
“indent-form” in elisp.

If there's none, how can i write one?

Xah
x...@xahlee.org
http://xahlee.org/


Jeremy Smith

unread,
Jan 28, 2008, 1:01:34 PM1/28/08
to
Rainer Joswig <jos...@lisp.de> wrote in
news:joswig-3BC417....@news-europe.giganews.com:

This looks like a good indenting solution. The only problem is that it
doesn't seem to keep comments, which is a problem on a big source file.

Maybe there would be some way to keep comments after pretty-printing the
source, by re-inserting them afterwards.

But for now, I'll stick to my indent tool for personal use.

Cheers,

Jeremy.

Xah Lee

unread,
Jan 28, 2008, 1:41:21 PM1/28/08
to
Rainer Joswig wrote:
> But Common Lisp has that. The function is called PPRINT.
> It is based on a huge machinery defined in ANSI Common Lisp.

Jeremy Smith wrote:
> This looks like a good indenting solution. The only problem is that it
> doesn't seem to keep comments, which is a problem on a big source file.
>
> Maybe there would be some way to keep comments after pretty-printing the
> source, by re-inserting them afterwards.

Good point.

From my experience with Mathematica, this is not possible when the
solution is based on the compiler taking source code as input (since
it discards comments).

Maybe CL has a way to tell the compiler to keep the comments...

If some lisper writes a simple parser in elisp, that'd be great.
This'll solve problems in Common/Scheme/Emacs lisps. I just want this
to work in emacs lisp.

------------------------------

(Mathematica has this build-in, so that when you press a keyboard
shortcut, it reformats your code in any one of the predefined ways,
namely InputForm (plain source text), StandardForm (displaying it as
typeset 2D math), TraditionalForm (like StandardForm, but follows
traditional math notation much like LaTeX's default) (you can write
code to process whole source code exactly like lisp because of the
“source = data” property, and this is done practically often. One
example is a transparent built-in converter for opening older
Mathematica notebooks (i.e. the source code file) in a newer
Mathematica version. (it reads the source code, add extra info or
structure for the newer Mathematica version if necessary (such as
adding a version declaration), then display it in the “word-processor
like” integrated editor)))

------------------------------

Btw, i've always thougth that lisp's notation are not as regular as
Mathematica. For example, there's the “'(1 2 3)”. But that is
syntactically the same as “(quote (1 2 3))”, so it doesn't count as a
exception. But i think there are few others involving other chars like
“#”, “,”, but i'm not sure which of these are just syntax sugars and
which are genuine exception to the nested paren syntax.

My question is, could anyone list all these, and show exactly which
are just syntax equivalent on top of sexp and which are true
exceptions?

Xah
x...@xahlee.org
http://xahlee.org/

Rainer Joswig

unread,
Jan 28, 2008, 2:14:30 PM1/28/08
to
In article
<dfb17946-bf0c-40d6...@k2g2000hse.googlegroups.com>,
Xah Lee <x...@xahlee.org> wrote:

> Rainer Joswig wrote:
> > But Common Lisp has that. The function is called PPRINT.
> > It is based on a huge machinery defined in ANSI Common Lisp.
>
> Jeremy Smith wrote:
> > This looks like a good indenting solution. The only problem is that it
> > doesn't seem to keep comments, which is a problem on a big source file.
> >
> > Maybe there would be some way to keep comments after pretty-printing the
> > source, by re-inserting them afterwards.
>
> Good point.
>
> From my experience with Mathematica, this is not possible when the
> solution is based on the compiler taking source code as input (since
> it discards comments).
>
> Maybe CL has a way to tell the compiler to keep the comments...

The compiler is not involved. Expressions are read by READ,
not COMPILE. The reader ignores characters following a semicolon
(up to the next newline or the end of file). Keeping
comments is not easy. One could attempt to use a modified
reader to do so.

>
> If some lisper writes a simple parser in elisp, that'd be great.
> This'll solve problems in Common/Scheme/Emacs lisps. I just want this
> to work in emacs lisp.
>
> ------------------------------
>
> (Mathematica has this build-in, so that when you press a keyboard
> shortcut, it reformats your code in any one of the predefined ways,
> namely InputForm (plain source text), StandardForm (displaying it as
> typeset 2D math), TraditionalForm (like StandardForm, but follows
> traditional math notation much like LaTeX's default) (you can write
> code to process whole source code exactly like lisp because of the
> “source = data” property, and this is done practically often. One
> example is a transparent built-in converter for opening older
> Mathematica notebooks (i.e. the source code file) in a newer
> Mathematica version. (it reads the source code, add extra info or
> structure for the newer Mathematica version if necessary (such as
> adding a version declaration), then display it in the “word-processor
> like” integrated editor)))
>
> ------------------------------
>
> Btw, i've always thougth that lisp's notation are not as regular as
> Mathematica. For example, there's the “'(1 2 3)”. But that is
> syntactically the same as “(quote (1 2 3))”,

It's obviously syntactically not the same. The Lisp 'reader' will
just read both to the same result.

> so it doesn't count as a
> exception. But i think there are few others involving other chars like
> “#”, “,”, but i'm not sure which of these are just syntax sugars and
> which are genuine exception to the nested paren syntax.
>
> My question is, could anyone list all these, and show exactly which
> are just syntax equivalent on top of sexp and which are true
> exceptions?

CLHS 2.4 Standard Macro Characters
http://www.lispworks.com/documentation/HyperSpec/Body/02_d.htm

If you would just read the documentation, half of your
posts would be not necessary. How do you try to learn Lisp?
By posting many questions to comp.lang.lisp ? How
about taking a Lisp book (Touretzky, Winston/Horn, Seibel, Emacs
Lisp intro, ...) and just read it? If all you want to
do is Emacs customization using Emacs Lisp, then learn
just Emacs Lisp. Common Lisp will only puzzle you. It
has many concepts not found in Emacs Lisp. Much you
hear here on comp.lang.lisp is not applicable to Emacs Lisp.
Emacs Lisp and Common Lisp (which mostly discussed here)
are very different languages.
I think O'REILLY has a book about Emacs Lisp. There are
some Emacs tutorials on the net.

http://www.gnu.org/software/emacs/emacs-lisp-intro/

Xah Lee

unread,
Jan 28, 2008, 3:01:05 PM1/28/08
to

Xah Lee wrote:
> My question is, could anyone list all these, and show exactly which
> are just syntax equivalent on top of sexp and which are true
> exceptions?

Rainer Joswig wrote:
> CLHS 2.4 Standard Macro Characters
> http://www.lispworks.com/documentation/HyperSpec/Body/02_d.htm
> If you would just read the documentation, half of your
> posts would be not necessary. How do you try to learn Lisp?
> By posting many questions to comp.lang.lisp ? How
> about taking a Lisp book (Touretzky, Winston/Horn, Seibel, Emacs
> Lisp intro, ...) and just read it? If all you want to
> do is Emacs customization using Emacs Lisp, then learn
> just Emacs Lisp. Common Lisp will only puzzle you. It
> has many concepts not found in Emacs Lisp. Much you
> hear here on comp.lang.lisp is not applicable to Emacs Lisp.
> Emacs Lisp and Common Lisp (which mostly discussed here)
> are very different languages.
> I think O'REILLY has a book about Emacs Lisp. There are
> some Emacs tutorials on the net.
> http://www.gnu.org/software/emacs/emacs-lisp-intro/

Dear Rainer,

why do you always respond to messages in the most idiotic tech geeking
fashion?

Why can't you learn to appreciate the overall meaning of the poster,
and respond to that, instead of spelling picking?

The topic of this thread, is about a source code reformatting tool.
Yeah, Common Lisp's pprint facility is great, but the problem is still
there, that there is not a tool so programers can use to format his
code in a day-to-day coding.

As has been illustrated, the CL's pprint deletes comments, and that's
a problem making this facility not practical. Further, it's specific
to CL not lisp in general, so i can't use it emacs lisp. (since all
these lisps shares a uniform syntax, so a lexical parser can solve the
problem, and is relevant)

Further, i expressed the idea that a uniform, standard, universally
adopted formatting that is produced by simple mechanical rule can have
significant impact on idioms the programmers produces for the whole
community, and i indicated that this is one practical advantage
offered by Python lang transparently. It is fine that you do not
address this in this particular thread, but in many discussions in the
past months here, you and other Common Lisp advocating morons
persistently and consistently ignored anything that's not within your
expertise in CL yet kept expressing dogma. That is a problem.

If i'm learning CL, and have CL specific questions, i can read the
docs and i'll ask as such. But often in my questions or discussions
posted here in the last few months, it is in general related to ideas
in all Common/Scheme/Emacs lisps. Burying your head in Common Lisp and
antagonistic to all other lisps and other advanced langs such as
Haskell etc is not healthy.

And, stop the habit of throwing out a flotilla of citations and urls.
Should i start to throw out urls and citations and books in logic,
mathematics, Mathematica tangentially, whenever you showed a ignorance
in everything that's not Common Lisp technicalities?

I do appreciate some CL technicalities that you gave sometimes, but
stop acting like a CL priest.

---------------------------------

The question i posted in previous message, is about a list of all
exception to lisp's nested form. You threw out some URL to the CL doc.
I'm not particularly interested in CL to read the tens of pages and
spend days to understand it exactly. I'm simply here interested in a
simple answer in the context of regularity of syntaxes shared by all
lisps and Mathematica. Can you see this?

If Common Lisp, actually do not have syntax irregularities, just say
so. Abusing citations and being a CL priest is not helpful. You are
not a college student, and i'm not a college student. Get over with
citations.

Xah
x...@xahlee.org
http://xahlee.org/

Rainer Joswig

unread,
Jan 28, 2008, 4:11:37 PM1/28/08
to
In article
<c65747fa-430a-4a0d...@e4g2000hsg.googlegroups.com>,
Xah Lee <x...@xahlee.org> wrote:

> The question i posted in previous message, is about a list of all
> exception to lisp's nested form. You threw out some URL to the CL doc.
> I'm not particularly interested in CL to read the tens of pages and
> spend days to understand it exactly. I'm simply here interested in a
> simple answer in the context of regularity of syntaxes shared by all
> lisps and Mathematica. Can you see this?

Lisps don't share the same syntax.

Syntax talks about the structure of the language.
Lisp uses simple s-expression syntax for data.

(a b c d e f) is a simple list. So how do you format it? When
you print such a list? Simple:

* print all elements on on line, if the line is not long
enough print the remaining elements on the next line
and indent by one:

Example:

(a b c
d e f)

But that is not Lisp code. That's list data.

Lisp code has syntax on top of s-expressions.

DEFUN in Common Lisp has the following syntax:

defun function-name lambda-list [[declaration* | documentation]] form*

Now we have list of again six elements, but not a b c d e f:

(defun foo (bar) "documentation" (declare (integer bar)) (1+ bar))

Now how do you indent that? By our simple list-based
formatter above? No. We want it to be formatted like:

(defun foo (bar)
"documentation"
(declare (integer bar))
(1+ bar))

So the rules for formatting this six element list is quite different:

* if it is a defun form indent as follows
** put the name after the defun
** then format the arguments behind the name, if the arguments
are longer than one line, use the next line for the remaining
elements and indent up to the first argument of the preceding line
** next indent two characters
** put the documentation string on its own line if there is some
** next comes the declaration on their own line
use special formatting for the declarations
** next comes the body of the function

Define this for LET, LET*, UNWIND-PROTECT, PROGN, DEFCLASS, and so on.
Each have their own syntax. Then define a generic macro
formatter that takes some hints from the macro parameter list.

The syntax and the rules to print Lisp code is different for
each Lisp. Emacs Lisp is to Common Lisp, like Pascal to Ada.


Learn Emacs Lisp. Write the formatter yourself. Since
you are the world's best Mathematica expert it
should not be difficult for you.

> If Common Lisp, actually do not have syntax irregularities, just say
> so. Abusing citations and being a CL priest is not helpful. You are
> not a college student, and i'm not a college student. Get over with
> citations.

Learn Emacs Lisp. Write your program. Done. You are the world's
best Mathematica expert it should be easy. If that's too difficult,
write a formatter for Lisp in Mathematica and hook it up to Emacs.
That should be easy for you.

Damien Kick

unread,
Jan 29, 2008, 12:39:30 AM1/29/08
to
Jeremy Smith wrote:

> This looks like a good indenting solution. The only problem is that it
> doesn't seem to keep comments, which is a problem on a big source file.
>
> Maybe there would be some way to keep comments after pretty-printing the
> source, by re-inserting them afterwards.

I was half-pondering this at various points today. The problem is that
the comments are discarded by the reader before one ever gets to the
pretty printer. I was thinking that changing the dispatch table for ';'
and '#||#' to have it produce something sexp for comments, i.e. (|;|
"This is the text of the comment") (but (|#\|\|#| "Another comment") is
far too ugly), and then setting up a printer dispatch for this form to
restore things to the original textual representation would work okay.
#||# style comments would be more difficult, though, I would think.

0 new messages