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

时间:2019-02-07 21:32:37

标签: lisp common-lisp racket eval dsl

我想尝试扩展一些Lisp(方案,球拍,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最适合这样扩展它,它是如何完成的?也许已经存在一个执行类似任务的项目?

2 个答案:

答案 0 :(得分:9)

Common Lisp具有标准的错误系统,可以用来实现该目的。

在提供use-valuestore-value类型错误的undefined-functionCL-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 重新启动的Common Lisp实现中。

示例

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 > 不存在。如您所见,错误已得到处理,而是调用了另一个函数,该函数随后进行一些输出。

如果我们单独调用未定义的函数,则会收到错误消息:

call-use-value-restart

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

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

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

提示解决方案

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

调用重新启动的另一种方式

在此示例中,我们使用 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 时,它将调用一个新函数。

CREATE TABLE SALES_AUG_t (
   `Country` VARCHAR(50),
   `GL Category` VARCHAR(30),
   `Measure Names` VARCHAR(30),
   `P3 BFS Description` VARCHAR(50),
   `P4 Sub Brand Description` VARCHAR(50),
   `P6 Sub Franchise` VARCHAR(20),
   `P7 Franchise` VARCHAR(20),
   `Product Number` VARCHAR(50),
   `Region` VARCHAR(13),
   `Measure Values` DECIMAL(10,3)
);

LOAD DATA LOCAL INFILE 'L:/JNJCHLL/DEPARTEMENT/MEDOS INTL - GSC/SPINE/01. S&OP & BP/01. Demand Planning/04. KPI - MAPE & Bias/2018/08. August/Extract - Source/Extract Tableau/Lag1_SKU_Level_data_Tableau_August.csv' REPLACE INTO TABLE SALES_AUG_t
CHARACTER SET Latin1 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES

 (
   @`Country` ,
   @`GL Category`,
   @`Measure Names`,
   @`P3 BFS Description`,
   @`P4 Sub Brand Description`,
   @`P6 Sub Franchise`,
   @`P7 Franchise`,
   @`Product Number`,
   @`Region`,
   @`Measure Values`
  )

  set
  `Country` = @'Country Description',
  `Measure Names` = @'Profile',
  `P3 BFS Description` = @'Planning Hierarchy 3 Description',
  `P4 Sub Brand Description` = @'Planning Hierarchy 4 Description',
  `P6 Sub Franchise` = @'Planning Hierarchy 6 Description',
  `Product Number` = @'Planning Hierarchy 2',
  `Region` = @'Region Description',
  `Measure Values` = @'Quantity'
  ;

答案 1 :(得分: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)))))

将其另存为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)

现在从这里开始使其成为#lang myshell或类似的东西非常容易。