Написание кода Common Lisp, который выполняется из командной строки, но не внутри интерпретатора

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

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

sbcl --script foo.lisp

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

Это аналогично условию Питона.

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. Теперь мы используем макросы встроенного считывателя #-/#+ которые, как сказал Всеволод, ведут себя так же, как и С #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 ответа

Решение

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

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

    (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.

редактирует

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

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

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

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

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

Другие вопросы по тегам