20.2 Потоки ввода

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

buffer

Символы ввода считываются из buffer, начиная с символа, непосредственно следующего за точкой. Точка продвигается по мере считывания символов.

marker

Символы ввода считываются из буфера, в котором находится marker, начиная с символа, непосредственно следующего за маркером. Позиция маркера продвигается по мере считывания символов. Значение точки в буфере не имеет значения, когда поток является маркером.

string

Символы ввода берутся из string, начиная с первого символа в строке и используя столько символов, сколько требуется.

function

Символы ввода генерируются function, которая должна поддерживать два типа вызовов:

  • Когда она вызывается без аргументов, она должна вернуть следующий символ.
  • Когда она вызывается с одним аргументом (всегда символом), function должна сохранить аргумент и подготовиться к его возврату при следующем вызове. Это называется откатом чтения символа; это происходит, когда парсер Lisp считывает один символ слишком много и хочет вернуть его на место. В этом случае не имеет значения, какое значение возвращает function.
t

t, используемый как поток, означает, что ввод считывается из минибуфера. Фактически, минибуфер вызывается один раз, и текст, предоставленный пользователем, преобразуется в строку, которая затем используется в качестве потока ввода. Если Emacs работает в пакетном режиме (см. Пактовый режим), вместо минибуфера используется стандартный ввод. Например,

(message "%s" (read t))

в пакетном режиме прочитает выражение Lisp из стандартного ввода и выведет результат в стандартный вывод.

nil

nil, переданный как поток ввода, означает использование значения standard-input; это значение является потоком ввода по умолчанию, и оно должно быть потоком ввода, не равным nil.

symbol

Символ в качестве потока ввода эквивалентен определению функции этого символа (если таковое имеется).

Вот пример чтения из потока, который является буфером, показывающий, где находится точка до и после:

---------- Buffer: foo ----------
This∗ is the contents of foo.
---------- Buffer: foo ----------

(read (get-buffer "foo"))
     ⇒ is
(read (get-buffer "foo"))
     ⇒ the

---------- Buffer: foo ----------
This is the∗ contents of foo.
---------- Buffer: foo ----------

Обратите внимание, что первое чтение пропускает пробел. Чтение пропускает любое количество пробельных символов перед значимым текстом.

Вот пример чтения из потока, который является маркером, изначально расположенным в начале показанного буфера. Прочитанное значение — это символ This.


---------- Buffer: foo ----------
This is the contents of foo.
---------- Buffer: foo ----------

(setq m (set-marker (make-marker) 1 (get-buffer "foo")))
     ⇒ #<marker at 1 in foo>
(read m)
     ⇒ This
m
     ⇒ #<marker at 5 in foo>   ;; Перед первым пробелом.

Здесь мы читаем из содержимого строки:

(read "(When in) the course")
     ⇒ (When in)

Следующий пример считывает из минибуфера. Подсказка: ‘Lisp expression: ’. (Это всегда подсказка, используемая при чтении из потока t.) Ввод пользователя показан после подсказки.

(read t)
     ⇒ 23
---------- Buffer: Minibuffer ----------
Lisp expression: 23 RET
---------- Buffer: Minibuffer ----------

Наконец, вот пример потока, который является функцией, с именем useless-stream. Прежде чем использовать поток, мы инициализируем переменную useless-list списком символов. Затем каждый вызов функции useless-stream получает следующий символ в списке или откатывает символ, добавляя его в начало списка.

(setq useless-list (append "XY()" nil))
     ⇒ (88 89 40 41)

(defun useless-stream (&optional unread)
  (if unread
      (setq useless-list (cons unread useless-list))
    (prog1 (car useless-list)
           (setq useless-list (cdr useless-list)))))
     ⇒ useless-stream

Теперь мы читаем, используя таким образом сконструированный поток:

(read 'useless-stream)
     ⇒ XY

useless-list
     ⇒ (40 41)

Обратите внимание, что открывающая и закрывающая скобки остаются в списке. Парсер Lisp обнаружил открывающую скобку, решил, что она завершает ввод, и откатил ее. Попытка чтения из потока в этой точке прочитает ‘()’ и вернет nil.