понедельник, 22 декабря 2008 г.

Расширение REPL

Возникла тут идея реализовать возможность расширения возможностей 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.


1Эта переменная пока не используется и была создана в надежде, что в будущем интерпретатор Emacs сможет её использовать.

ReST source Скачать оригинал

Комментариев нет: