0

请先查看#7755661。我正在使用 ECL,基本上是想执行一些代码,捕获任何可能发生的条件,然后继续执行,而不提示或进入调试器。使用以下处理程序案例宏很容易实现这一点:

(handler-case
  (load "code.lisp") ; this may raise a condition

  (error (condition)
    (print condition))) ; this prints sth like #<a UNBOUND-VARIABLE>

我唯一的问题是我找不到一种通用的方法来为用户打印更有意义的错误。事实上,我的应用程序是一个 HTTP 服务器,输出到一个网页。code.lisp 是由用户编写的,它可以引发任何类型的条件,我现在想在我的代码中列出它们。当我不使用处理程序案例时,我只想打印在 REPL 上看到的相同错误消息,但在 HTML 页面中,例如对于“未绑定变量”错误,类似“变量 VAR 未绑定”的字符串。

通过检查类型的条件对象,UNBOUND-VARIABLE我看到它有两个插槽:SI:REPORT-FUNCTION,这是一个编译函数,并且SI:NAME在这种情况下设置为变量的名称。我想SI:REPORT-FUNCTION可能是我需要调用的,但我该如何调用它?如果我尝试:

(handler-case foo (error (condition) (SI::REPORT-FUNCTION condition)))

它告诉我 SI:REPORT-FUNCTION 未定义。ECL 中的 SI 或 SYS 是实现内部函数和变量的包,但我不担心我的代码是否不可移植,只要它可以工作。

顺便说一句,在其他类型的条件对象中,还有其他显然有用的插槽,命名为SI:FORMAT-CONTROLand SI:FORMAT-ARGUMENT,但我也无法从我的代码中访问它们中的任何一个。

我一直在寻找与getMessage()Lisp 中 Java 异常对象的方法类似的东西,但我的资料中没有一个提到过类似的东西。

此外,是否有希望能够在 code.lisp 中获得错误发生的行号?否则,用户将很难在他的 code.lisp 源文件中找到问题所在。我真的很想提供这些信息,并且在第一个错误处停止对我来说是可以接受的。

4

2 回答 2

3

在 Common Lisp 中,当禁用打印转义时,会打印错误消息。

 CL-USER > (handler-case
               a       
             (error (condition)
               (write condition :escape nil)))

The variable A is unbound.
#<UNBOUND-VARIABLE 4020059743>

请注意,PRINT绑定*print-escape*T.

使用PRINC作品 - 它绑定*print-escape*NIL.

CL-USER > (handler-case
              a                
            (error (condition)
              (princ condition)))

The variable A is unbound.
#<UNBOUND-VARIABLE 4020175C0B>

这在CLHS 9.1.3 打印条件中进行了描述。

还要注意,当你有一个对象,它有一个槽并且这个槽的值是一个函数,那么你需要使用函数获取槽值SLOT-VALUE,然后使用FUNCALLorAPPLY并使用正确的参数调用函数。

如果你有一个类型的条件,simple-condition那么它有一个格式控制和一个格式参数信息。这将通过一个示例描述如何FORMATCLHS 函数 SIMPLE-CONDITION-FORMAT-CONTROL、SIMPLE-CONDITION-FORMAT-ARGUMENTS中使用它

于 2012-06-06T16:16:37.060 回答
2

我下面的答案是基于我在 ECL 邮件列表中给出的答案。实际上,我会声称这不是嵌入问题,而是 Lisp 问题。您想在导致错误的表单的文件位置获取一些信息。这与条件无关,因为条件的发生与评估的表单是被解释、编译还是已安装在 Lisp 映像中的函数的一部分无关。换句话说,您可以知道正在读取的文件的位置并进行一些包装以添加信息。

以下是非标准的并且容易更改: ECL 通过在源文件上使用 LOAD 时定义变量 ext:: source-location来帮助您。此变量包含一个用户永远不应更改或存储的 CONS,但您可以获得文件 as(CAR EXT:*SOURCE-LOCATION*)和文件位置为(CDR EXT:*SOURCE-LOCATION*). 然后计划将您的 LOAD 表单嵌入到 HANDLER-BIND 中

(defparameter *error-message* nil)
(defparameter *error-tag* (cons))

(defun capture-error (condition)
   (setf *error*
      (format nil "At character ~S in file ~S an error was found:~%~A"
         (cdr ext:*source-location*)
         (car ext:*source-location*)
         condition)))
  (throw *error-tag* *error-message*))

(defun safely-load (file)
  (handler-bind ((serious-condition #'capture-error))
      (catch *error-tag*
        (load file)
        nil)))

(SAFELY-LOAD "myfile.lisp")将返回 NIL 或格式化错误。

无论如何,我坚信依靠 LOAD 来实现这一点注定要失败。您应该创建自己的 LOAD 版本,从这里开始

(defun my-load (userfile)
  (with-open-file (stream userfile :direction :input :external-format ....whateverformat...)
     (loop for form = (read stream nil nil nil)
        while form
        do (eval-form-with-error-catching form))))

其中 EVAL-FORM-.... 实现了类似上面的代码。这个函数可以做得更复杂,你可以跟踪文件位置、行号等。这样你的代码也将更便携。

因此,请阅读 ANSI 规范并学习该语言。您不知道如何以可读的方式打印条件,而是尝试使用 ECL 内部,这表明您将来可能会面临更多问题,尝试使用非便携式解决方案(隐藏插槽名称、报告功能等)而不是首先尝试标准方式。

于 2012-06-08T08:07:38.120 回答