Возникла тут идея реализовать возможность расширения возможностей REPL в SXEmacs, а особенно его Read части. Но сначала про Print часть.
Пользовательская печать
В XEmacs уже давно были предпосылки к созданию пользовательской печати elisp-объектов. Так, например, при задании структуры с помощью defstruct можно было указать :print-function — процедуру, которая осуществляет печать объекта данной структуры. Уже давно существовала магическая переменная custom-print-functions со следующим напутствием:
This variable is not used at present, but it is defined in hopes that a future Emacs interpreter will be able to use it.1
—custom-print-functions (cl.el)
В SXEmacs это напутствие было выполнено и появилась возможность пользовательской печати. Это моментально решило проблему печати сложно-рекурсивных структур, которая до этого решалась с помощью изменения переменных print-level и print-length.
Уже существуют пакеты, которые используют возможность пользовательской печати. Использовать эту возможность крайне легко:
(defstruct (test-prs
(:print-function
(lambda (tp s pl)
(princ (format "#<test-prs %s>"
(test-prs-name tp)) s))))
type ; type of struct: `frs', `snd', ..
(name "default") ; name of object
state ; state of object
plist ; user defined properties list
)
(make-test-prs :name "Done")
==> #<test-prs Done>
Пользовательское чтение
Раз у нас есть пользовательская печать, то почему бы нам не иметь пользовательского чтения. В SXEmacs была реализована возможность пользовательского чтения под кодовым названием ureaders (от user readers). В новой встроенной (built-in) переменной ureaders хранится список пользовательских читалок. Пользовательская читалка это пара, состоящая из имени и функции-читалки от одного аргумента, которая возвращает elisp-объект или порождает ошибку. Приведу пример. Допустим, мы хотим чтобы следующая конструкция работала:
(setq mm '#<test ao -- 1
test -- 2
val -- "test">)
==> ((val . "test") (test . 2) (ao . 1))
реализуем:
(defun my-test-reader (input)
(flet ((string-trim (s)
(replace-in-string s "\\(^[ \t]+\\|[ \t]+$\\)" "")))
(let ((ss (mapcar 'string-trim (split-string input "\n")))
(rv nil))
(dolist (s ss)
(let ((rw (mapcar 'string-trim (split-string s "--"))))
(push (cons (intern (car rw)) (read (cadr rw)))
rv)))
rv)))
(push '("test" . my-test-reader) ureaders)
На первый взгляд эта возможность кажется не совсем нужной, ведь у нас и так есть мощнейшее средство — макросы. Это так, конечно, но в связке с пользовательской печатью почти прямой доступ к кишкам чтения выглядит интересно. Позже я продемонстрирую захватывающие вещи, которые можно реализовать с помощью ureaders.
Комментариев нет:
Отправить комментарий