[这应该是评论,但太长了。]
这介于困难和不可能之间。考虑下面的表格,这里在一行中给出:
(with-collectors (odd even) (iterate next ((i 0)) (when (< i 100) (if (evenp i) (even i) (odd i)) (next (1+ i)))))
这应该如何缩进?好吧,这是一个完全支持 lisp 的编辑器可能会如何缩进它:
(with-collectors (odd even)
(iterate next ((i 0))
(when (< i 100)
(if (evenp i)
(even i)
(odd i))
(next (1+ i)))))
那是……大错特错。以下是同一编辑器稍后将如何缩进:
(with-collectors (odd even)
(iterate next ((i 0))
(when (< i 100)
(if (evenp i)
(even i)
(odd i))
(next (1+ i)))))
这次做对了。
发生了什么变化?好吧,改变的是语言:特别是第二个示例中的语言已扩展为包括编辑器现在知道如何处理的with-collectors 表单以及它也可以理解的iterate 表单。
所以这似乎是一个晦涩的问题,但事实并非如此。因为 Lisp 的全部意义(可以说)是,为了解决问题,你可以逐步无缝地将语言从你开始使用的基础语言扩展到你想用来解决问题的语言。
这意味着许多 Lisp 程序由一系列对该语言的扩展组成,然后是用这种新的扩展语言编写的程序,在其中解决了问题。 Lisp 是一种面向语言的编程语言。
那个的意思是,了解如何缩进 Lisp 程序的唯一真正可靠的方法是询问程序。在上面的例子中,最初系统认为with-collectors 是一个函数,它就这样缩进了它。后来,当它知道定义时,它意识到这是一个let 样式的构造,并正确缩进了它。 iterate 也是如此。
所有这一切的意思是,一个独立的工具确实没有希望很好地缩进一个实质性的 Lisp 程序,因为要做到这一点,它需要比没有成为程序的情况下更多地了解该程序.当然,这就是为什么 Lisp 鼓励“驻留”开发环境,将正在开发的程序加载到开发环境中,而不是开发环境或多或少与正在开发的程序完全分离的“分离”环境.通过解析程序中的定义并找出扩展语言的定义,一个独立的工具可能会获得大部分的成功。但要做到这一点,再次,需要你成为程序。
作为一种面向语言的编程语言有显着的好处,但也有成本,不幸的是,这就是其中之一。
如果您的任务非常有限,并且如果您真的想在一行上使用一些大表达式(因此,可能没有 cmets),那么下面将尝试执行此操作。你需要把它包装成一个程序。
警告购买者。此代码肯定不安全,可以根据其输入执行任意代码。 不要使用它,除非你确定你输入的输入是安全的。所以,实际上不要使用它。
;;;; Note horrid code, This is *certainly* unsafe
;;;
;;; This uses EVAL which I think is necessary here, but is what makes
;;; it unsafe.
;;;
(in-package :cl-user)
(eval-when (:compile-toplevel :load-toplevel :execute)
(warn "UNSAFE CODE, USE AT YOUR OWN RISK."))
(defvar *tlf-handlers* (make-hash-table))
(defmacro define-tlf-handler (name ds-arglist &body forms)
(let ((formn (make-symbol "FORM")))
`(progn
(setf (gethash ',name *tlf-handlers*)
(lambda (,formn)
(destructuring-bind ,ds-arglist (rest ,formn)
,@forms)))
',name)))
(define-tlf-handler in-package (package)
(let ((p (find-package package)))
(if p
(progn
(format *debug-io* "~&Setting package ~S~%" package)
(setf *package* p))
(warn "no package ~S" package))))
(define-tlf-handler defpackage (package &body clauses)
(format *debug-io* "~&Defining package ~S~%" package)
(eval `(defpackage ,package ,@clauses)))
(define-tlf-handler defmacro (name arglist &body forms)
(format *debug-io* "~&Defining macro ~S~%" name)
(eval `(defmacro ,name ,arglist ,@forms)))
(define-tlf-handler eval-when (times &body forms)
(declare (ignore times forms))
(warn "Failing to handle eval-when"))
(define-condition pps-reader-error (reader-error simple-error)
())
(defparameter *pps-readtable* (copy-readtable nil))
(set-dispatch-macro-character
#\# #\+
(lambda (s c n)
(declare (ignore c n))
(error 'pps-reader-error
:stream s
:format-control "Can't handle #+"))
*pps-readtable*)
(set-dispatch-macro-character
#\# #\-
(lambda (s c n)
(declare (ignore c n))
(error 'pps-reader-error
:stream s
:format-control "Can't handle #-"))
*pps-readtable*)
(defun pp-stream (s &optional (to *standard-output*))
(with-standard-io-syntax ;note binds *package*
(let ((*readtable* *pps-readtable*)
(*read-eval* nil)
(*print-case* :downcase))
(do ((form (read s nil s) (read s nil s)))
((eq form s) (values))
(format to "~&")
(pprint form to)
(when (and (consp form) (symbolp (car form)))
(let ((handler (gethash (car form) *tlf-handlers*)))
(when handler (funcall handler form))))))))
(defun pp-file (f &optional (to *standard-output*))
(with-open-file (in f)
(pp-stream in to)))