stringtranslate.com

Emacs Лисп

Emacs Lisp — это диалект Lisp , созданный для Emacs . Он используется для реализации большинства функций редактирования, встроенных в Emacs, остальная часть написана на C , как и интерпретатор Lisp .

Код Emacs Lisp используется для изменения, расширения и настройки Emacs. Те, кто не хочет писать код самостоятельно, могут использовать функцию Customize. Она предоставляет набор страниц настроек , позволяющих пользователю устанавливать параметры и просматривать их эффект в запущенном сеансе Emacs. Когда пользователь сохраняет свои изменения, Customize просто записывает необходимый код Emacs Lisp в файл конфигурации пользователя , который может быть установлен в специальный файл, используемый только Customize, чтобы избежать возможности изменения собственного файла пользователя.

Помимо того, что Emacs Lisp — это язык программирования , который можно скомпилировать в байт-код [1] и транскомпилировать в машинный код [2] , Emacs Lisp также может функционировать как интерпретируемый язык сценариев , подобно оболочке Bourne или Perl в Unix , вызывая Emacs в пакетном режиме . Таким образом, его можно вызывать из командной строки или через исполняемый файл, а его функции редактирования, такие как буферы и команды перемещения, доступны программе так же, как и в обычном режиме. Пользовательский интерфейс не отображается, когда Emacs запускается в пакетном режиме; он просто выполняет переданный сценарий и завершает работу, отображая любой вывод сценария.

Emacs Lisp также называется Elisp , хотя существуют и более старые, не связанные с ним диалекты Lisp с таким же названием. [3] [4]

По сравнению с другими диалектами Лиспа

Emacs Lisp наиболее тесно связан с Maclisp , с некоторым более поздним влиянием Common Lisp . [5] Он поддерживает императивные и функциональные методы программирования. Lisp был языком расширения по умолчанию для производных Emacs, таких как EINE и ZWEI . Когда Ричард Столлман разделил Gosling Emacs на GNU Emacs, он также выбрал Lisp в качестве языка расширения из-за его мощных возможностей, включая возможность обрабатывать функции как данные. Хотя стандарт Common Lisp еще не был сформулирован, Scheme уже существовал в то время, но Столлман решил не использовать его из-за его сравнительно низкой производительности на рабочих станциях (в отличие от мини-компьютеров , которые были традиционным домом Emacs), и он хотел разработать диалект, который, по его мнению, было бы легче оптимизировать. [6]

Диалект Lisp, используемый в Emacs, существенно отличается от более современных диалектов Common Lisp и Scheme, используемых для программирования приложений. Отличительной чертой Emacs Lisp является использование динамической, а не лексической области видимости по умолчанию. То есть функция может ссылаться на локальные переменные в области видимости, из которой она вызвана, но не в области видимости, где она была определена. В последнее время предпринимаются постоянные усилия по обновлению кода для использования лексической области видимости по причинам, изложенным ниже.

Пример

Разработка Emacs Lisp была направлена ​​на предоставление структур данных и функций, специфичных для создания универсального текстового редактора, а не для реализации языка программирования общего назначения. Например, Emacs Lisp не может легко читать файл построчно — весь файл должен быть прочитан в буфер Emacs. Однако Emacs Lisp предоставляет множество функций для навигации и изменения текста буфера на уровне предложения, абзаца или более высокого синтаксического уровня, как определено режимами.

Далее следует простой пример расширения Emacs, написанного на Emacs Lisp. В Emacs область редактирования может быть разделена на отдельные области, называемые окнами , каждая из которых отображает свой буфер . Буфер — это область текста, загруженная в память Emacs (возможно, из файла), которую можно сохранить в текстовом документе.

Пользователи могут нажать C-x 2 сочетание клавиш по умолчанию , чтобы открыть новое окно. Это запускает функцию Emacs Lisp split-window-below. Обычно, когда появляется новое окно, оно отображает тот же буфер, что и предыдущее. Предположим, мы хотим, чтобы оно отображало следующий доступный буфер. Чтобы сделать это, пользователь пишет следующий код Emacs Lisp либо в существующем исходном файле Emacs Lisp, либо в пустом буфере Emacs:

( defun my-split-window-func () ( interactive ) ( split-window-below ) ( set-window-buffer ( next-window ) ( other-buffer )))       ( global-set-key ( kbd "Cx 2" ) #' my-split-window-func )   

Первый оператор, (defun ...), определяет новую функцию, my-split-window-func, которая вызывает split-window-below(старую функцию разделения окна), затем сообщает новому окну, что нужно отобразить другой (новый) буфер. Второй оператор (global-set-key ...)повторно привязывает последовательность клавиш "Cx 2" к новой функции.

Это также можно написать с помощью функции, называемой advice , которая позволяет пользователю создавать оболочки вокруг существующих функций вместо определения своих собственных. Это имеет то преимущество, что не требует изменения сочетаний клавиш и работает везде, где вызывается исходная функция, а также это проще в написании, но недостатком является то, что отладка становится более сложной. По этой причине advice не допускается в исходном коде GNU Emacs [7] , но если пользователь пожелает, функция advice может быть использована в его коде для повторной реализации приведенного выше кода следующим образом:

( defadvice split-window-below ( after my-window-splitting-advice first () activate ) ( set-window-buffer ( next-window ) ( other-buffer )))         

Это предписывает split-window-belowвыполнять предоставленный пользователем код всякий раз, когда он вызывается, после выполнения остальной части функции. Advice также может быть указан для выполнения перед исходной функцией, вокруг нее (буквально оборачивая оригинал) или для условного выполнения исходной функции на основе результатов advice.

Emacs 24.4 заменяет [8] этот defadviceмеханизм на advice-add, который, как утверждается, более гибкий и простой. [9] Совет выше можно реализовать повторно с использованием новой системы следующим образом:

( defun switch-to-next-window-in-split () ( set-window-buffer ( next-window ) ( other-buffer )))     ( совет-добавить 'разделить-окно-ниже :перед #' переключить-на-следующее-окно-в-разделе )   

Эти изменения вступают в силу сразу после оценки кода . Нет необходимости перекомпилировать, перезапускать Emacs или даже перехешировать файл конфигурации. Если код сохранен в файле инициализации Emacs, то Emacs загрузит расширение при следующем запуске. В противном случае изменения необходимо переоценить вручную при перезапуске Emacs.

Исходный код

Код Emacs Lisp хранится в файловых системах как простые текстовые файлы, по соглашению с суффиксом имени файла " .el". Файл init пользователя является исключением, часто появляясь как " .emacs", несмотря на то, что оценивается как любой код Emacs Lisp. С середины 1990-х годов Emacs также загружает ~/.emacs.elи ~/.emacs.d/init.el. Кроме того, пользователи могут указать любой файл для загрузки как файл конфигурации в командной строке или явно указать, что файл конфигурации не должен быть загружен. Когда файлы загружены, компонент интерпретатора программы Emacs считывает и анализирует функции и переменные, сохраняя их в памяти. Затем они становятся доступны другим функциям редактирования и командам пользователя. Функции и переменные можно свободно изменять и переопределять без перезапуска редактора или перезагрузки файла конфигурации.

Чтобы сэкономить время и память, большая часть функциональности Emacs загружается только при необходимости. Каждый набор дополнительных функций, поставляемых с Emacs, реализуется набором кода Emacs, называемым пакетом или библиотекой . Например, есть библиотека для подсветки ключевых слов в исходном коде программы и библиотека для игры в тетрис . Каждая библиотека реализуется с использованием одного или нескольких исходных файлов Emacs Lisp. Библиотеки могут определять один или несколько основных режимов для активации и управления их функциями.

Разработчики Emacs пишут некоторые функции на языке C. Это примитивы , также называемые встроенными функциями или subrs . Хотя примитивы можно вызывать из кода Lisp, их можно изменять только путем редактирования исходных файлов C и перекомпиляции. В GNU Emacs примитивы недоступны в виде внешних библиотек; они являются частью исполняемого файла Emacs. В XEmacs возможна загрузка таких примитивов во время выполнения с использованием поддержки операционной системы для динамического связывания . Функции могут быть написаны как примитивы, поскольку им требуется доступ к внешним данным и библиотекам, которые в противном случае не были бы доступны из Emacs Lisp, или поскольку они вызываются достаточно часто, так что сравнительная скорость C по сравнению с Emacs Lisp имеет смысл.

Однако, поскольку ошибки в коде на языке C могут легко привести к нарушениям сегментации или к более тонким ошибкам, которые приводят к сбою редактора, а также поскольку написание кода на языке C, который правильно взаимодействует со сборщиком мусора Emacs Lisp , подвержено ошибкам, количество функций, реализованных в виде примитивов, сведено к необходимому минимуму.

Байт-код

Байт-компиляция может ускорить выполнение кода Emacs Lisp. Emacs содержит компилятор , который может транслировать исходные файлы Emacs Lisp в специальное представление, называемое байт-кодом . Файлы байт-кода Emacs Lisp имеют суффикс имени файла " .elc". По сравнению с исходными файлами файлы байт-кода загружаются и работают быстрее, занимают меньше места на диске и используют меньше памяти при загрузке.

Байт-код все еще выполняется медленнее примитивов, но функции, загруженные как байт-код, можно легко изменять и перезагружать. Кроме того, файлы байт-кода не зависят от платформы. Стандартный код Emacs Lisp, распространяемый с Emacs, загружается как байт-код, хотя соответствующие исходные файлы обычно также предоставляются для справки пользователя. Расширения, предоставляемые пользователем, обычно не скомпилированы побайтно, поскольку они не такие большие и не такие вычислительно интенсивные.

Особенности языка

Примечательно, что пакет "cl-lib" реализует довольно большое подмножество Common Lisp . Этот пакет заменяет более ранний пакет "cl", который перезаписывал существующие определения функций Emacs Lisp на определения, более похожие на те, что есть в Common Lisp. С другой стороны, пакет "cl-lib" более точно следует принципам стиля Emacs Lisp и добавляет к каждой определяемой им функции и макросу префикс "cl-" (например, cl-defun, что не конфликтует с именем встроенного defun), избегая неожиданных изменений в поведении, которые могли произойти при загрузке пакета "cl".

Emacs Lisp (в отличие от некоторых других реализаций Lisp) не выполняет оптимизацию хвостовых вызовов . [10] Без этого хвостовые рекурсии могут в конечном итоге привести к переполнению стека .

Библиотека apel помогает писать переносимый код Emacs Lisp с помощью моста платформы polysylabi.

Emacs Lisp — это язык, подобный Common Lisp-2 , что означает, что он имеет пространство имен функций, которое отделено от пространства имен, используемого им для других переменных. [11]

От динамического к лексическому охвату

Как и MacLisp, Emacs Lisp использует динамическую область видимости , предлагая статическую (или лексическую) в качестве опции, начиная с версии 24. [12] Ее можно активировать, установив локальную переменную файла lexical-binding. [13] [14] До того, как эта опция была добавлена, можно было использовать lexical-letмакрос из (ныне устаревшего) пакета "cl" для предоставления эффективной лексической области видимости. [15]

В динамической области видимости, если программист объявляет переменную в области видимости функции, она становится доступной для подпрограмм, вызываемых из этой функции. Первоначально это было задумано как оптимизация ; лексическая область видимости все еще была непопулярна и имела неопределенную производительность. По воспоминаниям ученого-компьютерщика Олина Шиверса, «я спросил RMS, когда он реализовывал emacs lisp, почему она имеет динамическую область видимости, и его точный ответ был, что лексическая область видимости слишком неэффективна». [16] Динамическая область видимости также была предназначена для обеспечения большей гибкости для пользовательских настроек. Однако динамическая область видимости имеет несколько недостатков. Во-первых, она может легко привести к ошибкам в больших программах из-за непреднамеренных взаимодействий между переменными в разных функциях. Во-вторых, доступ к переменным в динамической области видимости, как правило, медленнее, чем в лексической области видимости. [17]

Смотрите также

Ссылки

  1. ^ "Byte compileing Emacs Lisp". Руководство GNU Emacs . Получено 2024-06-14 .
  2. ^ "Компиляция Emacs Lisp в машинный код". Руководство GNU Emacs . Получено 2024-06-14 .
  3. ^ "HEDRICK at RUTGERS (Mngr DEC-20's/Dir LCSR Comp Facility" (1981-12-18). ""информация о реализации Common Lisp"". Письмо "rpg at SU-AI, jonl at MIT-AI". Архивировано из оригинала 20-09-2016 . Получено 28-07-2019 . У нас есть некоторый опыт в реализации Lisp сейчас, так как Elisp (расширенная реализация Rutgers/UCI Lisp) по сути завершена.{{cite press release}}: CS1 maint: числовые имена: список авторов ( ссылка )
  4. ^ "Реклама CCA EMACS". Unix Review . Декабрь 1984. С. 16. CCA EMACS и Elisp являются торговыми марками CCA Uniworks, Inc.
  5. ^ "GNU Emacs Lisp во многом вдохновлен Maclisp и немного Common Lisp. Если вы знаете Common Lisp, вы заметите много сходств. Однако многие функции Common Lisp были опущены или упрощены, чтобы уменьшить требования GNU Emacs к памяти. Иногда упрощения настолько радикальны, что пользователь Common Lisp может быть очень сбит с толку. Мы будем время от времени указывать, чем GNU Emacs Lisp отличается от Common Lisp." – из раздела "История" "Введения" в Руководство по Emacs Lisp, по состоянию на Emacs 21
  6. ^ "Итак, разработка этой операционной системы, операционной системы GNU, привела меня к написанию GNU Emacs. При этом я стремился сделать абсолютно минимально возможную реализацию Lisp. Размер программ был огромной проблемой. В те дни, в 1985 году, были люди, у которых были машины на один мегабайт без виртуальной памяти. Они хотели иметь возможность использовать GNU Emacs. Это означало, что мне нужно было сделать программу как можно меньше". – из "My Lisp Experiences and the Development of GNU Emacs"
  7. ^ "Re: [Emacs-diffs] /srv/bzr/emacs/trunk r111086: gmm-utils.el (gmm-flet". Lists.gnu.org. 2012-12-05 . Получено 2013-08-18 .
  8. ^ "НОВОСТИ.24.4".
  9. ^ «Перенос старого совета».
  10. ^ "Приложение C. Портирование Common Lisp". Gnu.org . Получено 28.10.2019 . Программисты Lisp захотят отметить, что текущий компилятор Emacs Lisp не оптимизирует хвостовую рекурсию.
  11. ^ "Группы Google". groups.google.com .
  12. ^ "Emacs 24.1 выпущен". Lists.gnu.org . Получено 2013-08-18 .
  13. ^ "Лексическое связывание". Lists.gnu.org. 2011-04-01 . Получено 2013-08-18 .
  14. ^ "Динамическое связывание против лексического связывания". EmacsWiki. 2013-05-17 . Получено 2013-08-18 .
  15. ^ "Устаревшее лексическое связывание". GNU Emacs Common Lisp Emulation . GNU Press . Получено 27 мая 2021 г.
  16. ^ "T". People.csail.mit.edu . Получено 2013-08-18 .
  17. ^ Фезерстон, Сэм; Винклер, Сюзанна (2 июня 2009 г.). Процесс. Вальтер де Грютер. ISBN 978-3-11-021614-1.

Внешние ссылки