lisp - 在评估中进行小的修正来扩展 Lisp 的最简单方法是什么?

标签 lisp common-lisp racket eval dsl

我想尝试扩展一些 Lisp(Scheme、Racket、Clojure 等)来运行外部命令,如下所示:

; having
(define foo ...)
(define bar ...)
; on command
(ls (foo bar) baz)
; this lisp should evaluate (foo bar) as usual, with result "foobar", then
(ls foobar baz)
; here "ls" is not defined
; instead of rising "undefined identifier" exception
; it must look for "ls" command in the directories
; in the "PATH" environment variable
; and launch the first found "ls" command
; with strings "foobar" and "baz" on input

我只是想以任何方式运行它,而不进行从 lisp 的数据结构到字符串的正确转换或处理退出代码和 stdout/stderr 中命令的输出。

我认为没有办法在正常环境中扩展它(比如一直捕获“未定义”异常)。必须更改解释器本身的 eval 过程。

哪个 Lisp 最适合这样扩展它,它是如何完成的?也许已经存在执行类似操作的项目?

最佳答案

Common Lisp 有一个标准错误系统,可以用来实现它。

在提供 use-valuestore-value 的 Common Lisp 实现中,对于 undefined-function 类型的错误重新启动。

示例

CL-USER 69 > (flet ((call-use-value-restart (c)
                      (use-value (lambda (arg)
                                   (format t "~%dummy function with arg ~a~%" arg))
                                 c)))
               (handler-bind ((undefined-function #'call-use-value-restart))
                 (this-function-does-not-exist "foo")))

dummy function with arg foo
NIL

在上面的示例中,函数 this-function-does-not-exist 不存在。如您所见,错误已得到处理,而是调用了另一个函数,该函数随后会执行一些输出。

如果我们自己调用未定义的函数,我们会得到一个错误:

CL-USER 70 > (this-function-does-not-exist "foo")

Error: Undefined operator THIS-FUNCTION-DOES-NOT-EXIST in form (THIS-FUNCTION-DOES-NOT-EXIST "foo").
  1 (continue) Try invoking THIS-FUNCTION-DOES-NOT-EXIST again.
  2 Return some values from the form (THIS-FUNCTION-DOES-NOT-EXIST "foo").
  3 Try invoking something other than THIS-FUNCTION-DOES-NOT-EXIST with the same arguments.
  4 Set the symbol-function of THIS-FUNCTION-DOES-NOT-EXIST to another function.
  5 Set the macro-function of THIS-FUNCTION-DOES-NOT-EXIST to another function.
  6 (abort) Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.

CL-USER 71 : 1 > 

我们的示例基本上以编程方式调用第 3 次重启:

undefined-function 类型的错误发生时,它绑定(bind)一个调用函数 call-use-value-restart 的处理程序。

函数 call-use-value-restart 然后调用 use-value restart 并使用它提供的函数。在这里,您可以提供一个函数,该函数调用由 (cell-error-name c) 给出的名称的外部程序。 use-value 重新启动然后仅调用提供的函数并继续照常执行程序。

解决方案提示

通常,人们会在提供此类处理程序的地方编写一个小型顶层循环。

调用重启的另一种方式

在这个例子中,我们使用一个钩子(Hook)来添加一个处理程序,以防发生错误。这里我们使用全局变量*debugger-hook*。这应该是一个函数,在我们的例子中,当条件 c 的类型为 undefined-function 时,它会调用一个新函数。

* (defun provide-a-function-hook (c hook)
    (declare (ignore hook))
    (typecase c
      (undefined-function (use-value (lambda (arg)
                                       (format t "~%dummy function with arg ~a~%" arg))
                                     c))))
PROVIDE-A-FUNCTION-HOOK

* (setf *debugger-hook* #'provide-a-function-hook)
#<FUNCTION PROVIDE-A-FUNCTION-HOOK>

* (this-function-does-not-exist "foo")
; in: THIS-FUNCTION-DOES-NOT-EXIST "foo"
;     (THIS-FUNCTION-DOES-NOT-EXIST "foo")
; 
; caught STYLE-WARNING:
;   undefined function: THIS-FUNCTION-DOES-NOT-EXIST
; 
; compilation unit finished
;   Undefined function:
;     THIS-FUNCTION-DOES-NOT-EXIST
;   caught 1 STYLE-WARNING condition

dummy function with arg foo
NIL

关于lisp - 在评估中进行小的修正来扩展 Lisp 的最简单方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54582630/

相关文章:

scheme - Racket 追加意外的列表逆转

java - 斯坦福解析器

function - LISP 动态定义函数

lisp - 我如何检查一个项目是否是 Common Lisp 列表的成员?

common-lisp - Common Lisp 中分数的相等性检查

racket - 启动 Hacker News Clone - 后续步骤?

haskell - Haskell 是 lisp 方言吗

algorithm - 逆向算法LISP的实现

lisp - 在 Lisp 中编程的正确方法?

c - Racket/C FFI 中的变量函数