Как показать сообщение об ошибке от eval в схеме?

Я пытаюсь создать код, который оценивает выражение и возвращает ошибку как строку для ошибки:

(cond-expand
  (gambit)
  (gauche)
  (kawa)
  (guile
   (import (rnrs base)
           (rnrs exceptions)
           (rnrs conditions))
   (define (error-object-message cond)
     (condition-message cond))))

(define (evaluate expr env)
  (call-with-current-continuation
   (lambda (exit)
     (with-exception-handler
      (lambda (e)
        (exit (error-object-message e)))
      (lambda ()
        (eval expr env))))))

;; trigger error
(display (evaluate 'xxx (interaction-environment)))
(newline)

у меня есть

  • Сообщение Guile Unbound variable: ~S как получить фактическое сообщение об ошибке, а не шаблон?
  • Кава исключение: Argument #1 'unbound location: xxx' to 'error-object-message' has wrong type (gnu.mapping.UnboundLocationException) (gnu.mapping.UnboundLocationException cannot be cast to kawa.lang.NamedException)
  • Дамп ядра Гоша
  • Гамбит зависает

ПРИМЕЧАНИЕ: это часть REPL, которую я тестирую во всех реализациях Scheme, которые есть в моей системе. Он почти работает, он может работать сам по себе, но я хотел бы показывать правильное сообщение об ошибке, когда происходит исключение, вместо выхода из REPL.


person jcubic    schedule 11.01.2021    source источник


Ответы (3)


Причина, по которой вы получаете бесконечный цикл с Gambit, заключается в том, что переменная xxx не привязана, поэтому обработчик исключений (lambda (e) (exit (error-object-message e))) вызывается с объектом unbound-global-exception, и это вызывает вызов error-object-message, но параметр не является error-object (что характерно для исключений, вызванных вызов процедуры error), так что это порождает объект type-exception, вызывающий тот же обработчик исключений, и так далее до бесконечности.

Если вы хотите, чтобы обработка исключений вызывала текущий обработчик исключений, используйте with-exception-catcher вместо with-exception-handler. Это позволит избежать бесконечного цикла.

Преобразование объекта исключения в строку можно сделать в Gambit следующим образом:

(define (exception->string exc)
  (with-output-to-string
    (lambda ()
      (display-exception exc))))

Он работает для error-objects и других видов исключений, а также для любого объекта, не являющегося исключением.

Это более портативное решение (поверхностно протестировано):

(import (scheme base)
        (scheme r5rs))

(cond-expand
  ((or lips kawa gauche)
   (define (exception->string exc)
     (error-object-message exc)))
  (gambit
   (define (exception->string exc)
     (with-output-to-string
       (lambda ()
         (display-exception exc)))))
  (guile
   (import (rnrs base)
           (rnrs exceptions)
           (rnrs conditions))
   (define (exception->string exc)
     (condition-message exc))))

(define (evaluate expr env)
  (call-with-current-continuation
   (lambda (exit)
     (with-exception-handler
      (lambda (e)
        (exit (exception->string e)))
      (lambda ()
        (eval expr env))))))

(display (evaluate 'xxx (interaction-environment)))
(newline)
person feeley    schedule 12.01.2021

Дамп ядра Гоша

Упс. Конечно, это не идеально, но это можно объяснить.

  • По умолчанию with-exception-handler Гоша - это SRFI-18, а не R7RS, по исторической причине. Это означает, что обработчик исключений вызывается в динамической среде, в которой возникает исключение, включая настройки обработчика исключений. Если в обработчике исключений возникает исключение, вызывается тот же обработчик исключений, что вызывает неограниченную рекурсию. По-видимому, среда выполнения Gauche съедает стек C или что-то в этом роде.
  • error-object-message не определено в пространстве имен Gauche по умолчанию. Так что это вызывает исключение в первую очередь.

Добавлять

(import (scheme base) (scheme write) (scheme r5rs))

в начале кода запускает программу в привязках R7RS. Тогда вы получите:

unbound variable: xxx 

На самом деле ваш код не является допустимой программой R7RS (которая должна начинаться хотя бы с одного объявления import), так что может случиться что угодно, в зависимости от интерпретации по умолчанию несоответствующего кода в реализации.

[Изменить] ИМХО, with-exception-handler следует рассматривать как конструкцию самого низкого уровня, на которой построены простые в использовании утилиты, и поэтому ее следует использовать с особой осторожностью. В общем случае guard обеспечивает хорошую абстракцию.

person shirok    schedule 11.01.2021

Для Кавы:

   (define (exception->string exc)
     (call-with-port (open-output-string)
                     (lambda (port)
                       (display exc port)
                       (get-output-string port)))))

Это преобразует исключение в строку и получит сообщение об ошибке

person jcubic    schedule 14.01.2021