Написание кода Common Lisp, который выполняется из командной строки, но не внутри интерпретатора - PullRequest
10 голосов
/ 21 марта 2012

При написании кода Common Lisp я использую SLIME. В частности, я компилирую буфер, содержащий определения функций, используя C-C C-k, а затем переключаюсь на REPL для запуска этих функций. Помещение исполняемого кода для запуска этих функций в буфере, похоже, работает не так хорошо. Если в коде есть ошибки, это может привести к путанице.

Было бы удобно иметь способ включить код, который не компилируется в буфер, но запускается из командной строки, например при выполнении

sbcl --script foo.lisp

Если бы это было так, мне не пришлось бы продолжать добавлять и удалять код каждый раз, когда я хотел запустить код из командной строки. Существует ли такое условие?

Это аналог условия Python.

if __name__=='__main__':

, который равен false, если файл Python импортирован как модуль, но равен true, если он запускается как скрипт.

Это сообщение в блоге под названием "Использование SBCL для сценариев оболочки Common Lisp" , найденное случайным поиском в Google, имеет

;; If run from command line, the following if-test will succeed

(if (> (length sb-ext:*posix-argv*) 1)

    ;; Code that is executed from the command line only goes here

)

Включенный код действительно не запускается компилятором внутри SLIME, но он также не запускается sbcl --script.

ОБНОВЛЕНИЕ: Спасибо Всеволоду Демькину за его полезный ответ и продолжение. Далее следуют некоторые заметки об этом ответе, составленные из комментариев к этому ответу. @ Всеволод, если ты добавишь это в свой ответ, я их удалю.

  1. Сначала я попросил способ запуска кода из командной строки, но не из интерпретатора. Поставленное решение делает больше; он также позволяет запускать код из интерпретатора, но не из командной строки.

  2. Первым шагом является определение функции макроса для символа макроса #!. Как указано в ссылке «При встрече с макро-символом читатель Lisp вызывает свою макро-функцию читателя». Функция считывателя определяется вызовом set-dispatch-macro-character. Итак, когда символ #! виден, set-dispatch-macro-character вызывает лямбда-функцию, определенную в теле, для вызова. Затем эта функция добавляет ключевое слово :noscript в переменную *features*. Смотрите также обсуждение того, что макросы читателя хороши в вопросе SO Чтение макросов: для чего вы их используете? .

  3. Обратите внимание, что ключевое слово :noscript добавляется к *features* точно, когда присутствует символ #!. Кроме того, #! символ присутствует, когда код выполняется внутри интерпретатора, например при использовании slime, но, по-видимому, отсутствует (удален) из
    программы текст sbcl --script выполняется. Поэтому :noscript добавляется к *features*, когда код запускается в интерпретаторе, но не при запуске как
    сценарий.

  4. Теперь мы используем макросы встроенного считывателя #-/#+, которые, как сказал Всеволод, ведут себя подобно C #IFDEF/#IFNDEF. Они проверяют на символ
    в *features*. В этом случае #-:noscript проверяет отсутствие :noscript, а #+:noscript проверяет наличие :noscript.
    Если эти условия выполняются, запускается соответствующий код. Чтобы обернуть блок кода, можно использовать progn вот так: #-:noscript (progn <your code here>).

  5. Наконец, необходимо вызвать set-dispatch-macro-character перед запуском кода, который использует эту функциональность. В случае sbcl можно поставить это в файле инициализации ~/.sbclrc. Обратите внимание, что этот подход не зависит от реализации Common Lisp, являющейся SBCL.

  6. Более простая альтернатива, как упомянуто в списке sbcl-devel, состоит в том, чтобы использовать тот факт, что ключевое слово :SWANK появляется, когда вводится *features* в
    REPL внутри Emacs, используя SLIME. SWANK является серверной стороной SLIME. SLIME, вероятно, должен быть более точно назван SLIME / SWANK, так как эти два клиент-серверные компоненты клиент-серверной архитектуры. Я нашел этот пост под названием Понимание SLIME , что было полезно.

    Таким образом, можно использовать #-:swank и #+:swank точно так же, как #-:noscript и #+:noscript, за исключением того, что не нужно писать какой-либо код. Конечно, это не сработает, если, например, используется интерпретатор командной строки sbcl,с тех пор :SWANK не появится в *features*.

Ответы [ 3 ]

13 голосов
/ 21 марта 2012

Вы можете использовать следующий трюк:

  1. Определить функцию отправки для shebang:

    (set-dispatch-macro-character #\# #\!
        (lambda (stream c n)
          (declare (ignore c n))
          (read-line stream)
          `(eval-when (:compile-toplevel :load-toplevel :execute)
             (pushnew :noscript *features*))))
    
  2. В вашем файле сценария используйте #-:noscript:

    #!/usr/local/bin/sbcl --script
    
    (defun test (a) (print a))
    
    (test 1)
    #-:noscript (test 2)
    #+:noscript (test 3)
    

    При выполнении ./test.lisp будут напечатаны 1 и 2, а Cc Ck выведет 1 и 3.

РЕДАКТИРОВАТЬ

Этот прием должен работать, потому что строка shebang полностью удаляется на sbcl --script, но не удаляется, когда файл загружается через SLIME или другие механизмы.

Недостаток этого подхода заключается в том, что мы предусматриваем отсутствие :noscript в элементах, а не наличие :script.Чтобы исправить это, нажатие соответствующей функции должно быть сделано в sbcl --script самой обработке.

4 голосов
/ 06 февраля 2015

Fare Rideau написал изящную Unix-утилиту CL-Launch, которая позволяет запускать программное обеспечение lisp из командной строки. Он имеет встроенную поддержку Quicklisp и работает с большинством реализаций Common Lisp.

Пример скрипта может выглядеть так:

#!/usr/bin/cl -sp "hunchentoot" -Q -E main

(defun main (argv)
  (format t "~A: ~{~A ~}~%" (truename ".") argv)
  (hunchentoot:start
   (make-instance 'hunchentoot:acceptor
                  :document-root (truename ".")
                  :port 8080))
  (do ((x #\s (read-char)))
      ((char-equal x #\q) nil)))

После добавления + x разрешений к скрипту и его запуска он запустит http-сервер в текущем каталоге. Флаг "-sp" указывает на пакет, который вы хотите загрузить, так что это довольно чистый способ абстрагирования сценария оболочки от пакета.

Для получения более подробной информации см .: http://cliki.net/CL-Launch

1 голос
/ 03 октября 2016

У меня был тот же вопрос, и я только что наткнулся на это обсуждение.По крайней мере, с sbcl кажется, я могу использовать (sb-ext:posix-getenv "_").При запуске в slime он возвращает /usr/bin/emacs (или любой другой путь к emacs), а в противном случае команду, которую я использую для вызова скрипта.Таким образом, всегда можно различить слизь и вызов скрипта, пока вы не станете участником emacs:)

Если вы хотите получить полный путь к скрипту, который вы вызываете, вы можете использовать (truename (sb-ext:posix-getenv "_")).Однако при запуске из slime он вернет эффективное имя пути emacs, например, /usr/bin/emacs-24.5, так что это может быть менее удобно.

...