嵌入式 ECL Lisp 错误处理获取默认错误字符串和可能的行号



请先看#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 是由用户编写的,它可以引发任何类型的条件,我现在想在我的代码中将它们全部列出。当我不使用处理程序大小写但在 HTML 页面中时,我只想打印我在 REPL 上看到的相同错误消息,例如对于"未绑定变量"错误,像"变量 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-CONTROLSI:FORMAT-ARGUMENT ,但我也无法从我的代码中访问它们中的任何一个。

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

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

在 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获取插槽值,然后使用FUNCALLAPPLY并使用正确的参数调用该函数。

如果您有类型 simple-condition 的条件,则它具有格式控件和格式参数信息。这是通过一个示例描述的,如何在CLHS函数中将其用于FORMAT 简单条件格式控制,简单条件格式参数

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

以下内容是非标准的,容易更改:ECL 通过在源文件上使用 LOAD 时定义变量 ext::source-location 来帮助您。此变量包含一个用户永远不应更改或存储的 CONS,但您可以获取文件作为(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内部结构,这表明您将来可能会面临更多问题,尝试使用非可移植解决方案(隐藏的插槽名称,报告功能等),而不是首先尝试标准方式。

相关内容

  • 没有找到相关文章

最新更新