我怎样才能发现微妙的Lisp语法错误?

时间:2009-05-22 14:53:56

标签: emacs syntax lisp elisp indentation

我是一个玩弄Lisp的新手(实际上是Emacs Lisp)。这很有趣,除非我似乎一次又一次地遇到相同的语法错误。

例如,这是我多次遇到过的事情。我有一些cond形式,比如

(cond
 ((foo bar)
  (qux quux))
 ((or corge
      (grault warg))
  (fred)
  (t
   xyzzy)))

并且永远不会执行返回xyzzy的default子句,因为它实际上嵌套在前一个子句中:

(cond
 ((foo bar)
  (qux quux))
 ((or corge
      (grault warg))
  (fred))
 (t
  xyzzy))

当缩进的差异只有一个空格时,我很难看到这样的错误。随着时间的推移,这会变得更容易吗?

当(错误的)缩进线与应该缩进的线之间有很大的距离时,我也会遇到问题。例如,let形成了许多复杂的绑定,或者带有长条件的unless形式:

(defun test ()
  (unless (foo bar
               (qux quux)
               (or corge
                   (grault warg)
                   (fred))))
  xyzzy)

事实证明xyzzy永远不会出现在unless表格中:

(defun test ()
  (unless (foo bar
               (qux quux)
               (or corge
                   (grault warg)
                   (fred)))
    xyzzy))

我习惯性地自动缩进并使用括号突出显示以避免计算括号。在大多数情况下,它像微风一样,但偶尔,我只通过调试发现我的语法错误。我该怎么办?

7 个答案:

答案 0 :(得分:28)

突出性别

首先,如果还没有这样做,请打开内置的paren匹配突出显示(show-paren-mode)。它总能让您了解缩进级别。

还有一些更复杂的包。例如,请参阅TJ's answer on mic-paren。或者虽然我没有发现它适合我,但有Schumacher's highlight-parentheses mode突出显示每个具有不同颜色的表达块。甚至Edward O'Connor has a mode突出了当前的性别。

Paredit

使用paredit-mode帮助您编写色情内容。您可以轻松地在sexps之间导航并重新构造代码。此外,它确保括号始终平衡。当我第一次尝试时,对我来说非常讨厌Paredit如何限制编码方式,但从那时起我就习惯了它,我的工作效率更高,永远不会对开括号和右括号感到困惑。 / p>

自动缩进

使用Emacs Starter Kit,默认情况下,它允许很多有用的帮助程序在Elisp中编码,就像在换行符上重新缩进一样。

ElDoc

emacs-lisp-mode有几个有用的扩展。例如,我总是使用eldoc-mode在echo区域中显示当前键入函数的参数列表或变量的docstring。它还可以帮助您轻松识别您是否在错误的区域。

Edebug

我几乎忘记提及Edebug - 作为最后一次机会 - 总能帮助您弄清楚代码中发生了什么。

答案 1 :(得分:7)

您可以采取以下三个具体方法来帮助发现Lisp语法问题。随着时间的推移,它将成为第二天性。但在那之前:

括号匹配

括号匹配是检查分组的最简单方法。我最喜欢的包是mic-paren。我喜欢这种特殊的配置:

(setq paren-dont-touch-blink t)
(require 'mic-paren)
(paren-activate)
(setq paren-match-face 'highlight)
(setq paren-sexp-mode t)

当该点位于性别的开始/结束时,这会导致性别(匹配的括号)突出显示。如果括号不匹配,则突出显示颜色不同 - 并且更亮。当匹配的括号在屏幕外时,它会显示迷你缓冲区中的那个结尾(并告诉你它有多少行)。

汇编

对于稍微复杂一点的方法,可以使用M-x compile-defun运行Elisp编译器。例如,当我编译这个简单的例子时:

(defun mytestfun ()
  (let ((cur (current-buffer)))
    )
  (set-buffer cur))

我有一个名为*Compile-Log*的缓冲区,其中说:

  

警告:引用自由变量   `CUR'

这使我明白我在定义它的cur语句之外使用let这一事实。

更改缩进级别

如果您希望缩进更加突出,可以自定义变量listp-body-indent

(setq lisp-body-indent 4) ;# default is 2

你也可以自定义各种结构的缩进方式,但我不建议这样做,因为它是非标准的,在查看大多数Lisp代码时可能会引起混淆。

答案 2 :(得分:2)

要做的一件简单的事情就是将光标移动到每个cond条件的开头,并查看关闭窗口的位置。

答案 3 :(得分:2)

编写lisp的“最佳”方式是emacs + slime组合 它提供括号突出显示,tabcompletion,你可以直接跳转到lisp hyperspec文档,它提供函数的变量名(见下文)等等。

(defun foo(bar)    ...)

当你开始输入时(foo,它会告诉你foo想要一个名为bar的参数。这样你就可以轻松地“猜测”函数所采用的参数。这对于不遵循Lisp约定的函数尤其方便。 / p>

答案 4 :(得分:1)

  

我很难看到这样的   错误时的差异   缩进只是一个空间。是否   随着时间的推移这会变得更容易吗?

不适合我,至少..它在某种程度上取决于您使用的特定xterm字体,但我发现我需要一个四空缩进才能有效工作(是的,我使用那个旧的原始xterm字体 - 责备SunOS 4),甚至两个都是有问题的。

我也使用括号突出显示,%中的“vi”键很多。

不幸的是,这不是一个非常有用的答案。

答案 5 :(得分:1)

我看到你可以采取的两种方法可以帮助你完成这两个例子。

第一种方法是首先绘制一张大图来编写代码,然后再填写详细信息。对于您的第一个示例,您可以通过编写第一个

开始
(cond
 (--
  --)
 (--
  --)
 (t
  --))
然后你就可以开始填写细节了。 --就像TODO填充物。

你的第二个例子。你可以从

开始
(defun test ()
  --)

然后填写更多这样的内容:

(defun test ()
  (unless (--)
    --))

然后填写更多。

另一种方法是自定义彩虹分隔符,以便突出显示一种颜色的均匀水平,以及另一种颜色的奇数级别的parens。这将有助于cond形式。它对您的第二个示例没有帮助,但如果它是(xyzzy)而不是xyzzy,则会有。{/ p>

答案 6 :(得分:0)

要添加其他人的宝贵指示,我会说“使用基于结构的移动命令”,backward-sexpforward-sexp等。这允许你导航。