哪个是在评估中通过小修正来扩展Lisp的最简单方法?

xea*_*its 3 lisp dsl eval common-lisp racket

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

; 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
Run Code Online (Sandbox Code Playgroud)

我只想运行它,无需从lisp的数据结构到字符串或处理退出代码和命令输出的正确转换stdout/stderr.

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

哪个Lisp是最好的扩展它,它是如何完成的?也许已经有一个项目执行类似的事情?

Rai*_*wig 9

Common Lisp有一个标准的错误系统,可用于实现它.

在Common Lisp实现中,它为类型的错误提供use-valuestore-value重新启动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
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,该函数this-function-does-not-exist不存在.如您所见,处理错误并调用另一个函数,然后执行一些输出.

如果我们自己调用undefined函数,我们会收到一个错误:

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 > 
Run Code Online (Sandbox Code Playgroud)

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

它绑定一个处理程序,call-use-value-restart当类型错误undefined-function发生时调用该函数.

call-use-value-restart然后use-value,该函数使用它提供的函数调用重启.在这里,您可以提供一个函数,该函数调用由其指定的名称的外部程序(cell-error-name c).然后use-value重启只调用提供的函数并像往常一样继续执行程序.

提示解决方案

通常,人们会编写一个小的顶级循环,其中提供了这样的处理程序.

另一种调用重启的方法

在这个例子中,我们使用一个钩子来添加一个处理程序,以防发生错误.这里我们使用全局变量*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
Run Code Online (Sandbox Code Playgroud)


Syl*_*ter 7

在球拍中你可以覆盖#%top:

#lang racket

(provide
 (combine-out
  (except-out (all-from-out racket) #%top)
  (rename-out [shell-curry #%top])))

(require racket/system)

(define (stringify a)
  (~a (if (cmd? a) (cmd-name a) a)))

(struct cmd (name proc)
  #:property prop:procedure
  (struct-field-index proc)
  #:transparent
  #:methods gen:custom-write
  [(define (write-proc x port mode)
     (display (string-append "#<cmd:" (stringify x) ">") port))])

(define (shell name)
  (define (cmd-proxy . args)
    (define cmd
      (string-join (map stringify (cons name args))
                   " "))
    (system cmd))
  cmd-proxy)

(define-syntax shell-curry
  (syntax-rules ()
    ((_ . id)
     (cmd 'id (shell 'id)))))
Run Code Online (Sandbox Code Playgroud)

保存为shell.rkt并将此runner.rkt放在同一目录中:

#lang s-exp "shell.rkt"

(define test (list /bin/ls /usr/bin/file))
(second test) ; ==> #<cmd:/usr/bin/file>
(first test)  ; ==> #<cmd:/bin/ls>
((second test) (first test)) 
; ==> t (prints that /bin/ls is an executable on my system)
Run Code Online (Sandbox Code Playgroud)

现在从这里开始,使它成为一个#lang myshell或类似的东西是非常容易的.