Общий синтаксический анализатор Lisp, который сохраняет комментарии
Common Lisp поставляется с синтаксическим анализатором (читателем), который преобразует его текстовый синтаксис в s-выражения. Тем не менее, он отбрасывает комментарии, что делает его непригодным для инструментов, которые используют код Lisp в оба конца.
Есть ли существующий парсер для Common Lisp, который сохраняет комментарии?
Как отмечено в комментарии, вы должны иметь возможность модифицировать readtable для привязки символа макроса ;
к другой функции макроса читателя. Например, если вы определяете:
(defun semicolon-reader (stream char)
(list 'my-comment
(concatenate 'string (string char)
(read-line stream nil #\Newline t))))
а затем при запуске верхнего уровня:
> (set-macro-character #\; #'semicolon-reader)
> (read)
пользовательский ввод:
(a b ; is b
c ; is c
)
будет генерировать:
(A B (MY-COMMENT "; is b") C (MY-COMMENT "; is c"))
Тем не менее, реальная обработка в оба конца также потребует сохранения пробелов. Я не знаю достаточно, чтобы читатель узнал, можете ли вы уйти с определением некоторых умных макрофункций для пробельных символов, или если вам придется написать какую-то функцию предварительной обработки, чтобы предварительно процитировать пробеги пробела с другим символом макроса а затем обрабатывать его аналогично semicolon-reader
выше.
Поскольку вы создаете форматировщик кода, вы можете уйти с поддержкой только стандартного синтаксиса Common Lisp (with-standard-io-syntax
) и изменения стандартного readtable для сохранения комментариев и пробелов (set-macro-character
). Чтение является основной структурой данных CL-считывателя и сообщает, какую функцию вызывать для чтения различных объектов, когда он встречает определенный символ в исходном коде (например, как читать список, когда он встречает открывающую скобку).
Вы должны использовать либо gensyms, либо structs/classes для представления комментариев и пробелов, поскольку другие типы объектов (например, списки и не-gensym символы) могут быть прочитаны из исходного файла с помощью читателя Lisp с использованием стандартного синтаксиса IO.
Ниже приведено быстрое доказательство концепции. Читатель прекрасно работает, но я не мог заставить принтер работать (то есть перепечатать материал, который мы получили от читателя, чтобы создать исходный файл, достаточно близко к вводу) - он печатает лишние пробелы вокруг наших пробелов, возможно, потому, что он думает наши пробельные объекты похожи на обычные объекты Lisp (символы, списки и т.д.) и должны быть разделены пробелами при печати нескольких из них в строке (например, если вы печатаете 1
и 2
и 3
она должна печатать 1 2 3
не 123
), Дайвинг в кишках принтера Common Lisp, чтобы выяснить, как переопределить это поведение, остается как упражнение для читателя: p
Кроме того, прочтите раздел 2.4 "Стандартные макросимволы общего Lisp HyperSpec". Раздел 2.4.8 Sharpsign перечисляет весь синтаксис, начинающийся с #
. Остерегайтесь особенно #+
и #-
и #.
Если вы когда-нибудь получите это, чтобы хорошо работать на реальном коде, рассмотрите его публикацию как пакет с открытым исходным кодом.
(defstruct comment style string)
(defstruct whitespace string)
(defconstant +whitespace-chars+ '(#\Space #\Tab #\Return #\Newline))
(defconstant +eof+ (gensym "EOF"))
(defun read-semicolon-comment (stream semicolon)
(declare (ignore semicolon))
(make-comment :style :semicolon :string
(with-output-to-string (comment)
(loop (let ((char (read-char stream nil +eof+ t)))
(cond ((equal char +eof+) (return))
((equal char #\Newline)
(unread-char char stream)
(return))
(t (write-char char comment))))))))
(defun read-whitespace (stream first-char)
(make-whitespace :string
(with-output-to-string (whitespace)
(write-char first-char whitespace)
(loop (let ((char (read-char stream nil +eof+ t)))
(unless (member char +whitespace-chars+)
(unless (equal char +eof+) (unread-char char stream))
(return))
(write-char char whitespace))))))
(defun read-stream (stream)
(with-standard-io-syntax ; Here a comment, for example.
(let ((*readtable* (copy-readtable)))
(set-macro-character #\; #'read-semicolon-comment)
(dolist (char +whitespace-chars+)
(set-macro-character char #'read-whitespace))
(loop for x = (read stream nil +eof+) until (equal x +eof+)
collect x))))
(defmethod print-object ((x comment) stream)
(assert (equal :semicolon (comment-style x)))
(write-char #\; stream)
(write-string (comment-string x) stream)
x)
(defmethod print-object ((x whitespace) stream)
(write-string (whitespace-string x) stream)
x)
(mapc #'prin1 (read-stream *standard-input*))
- Вопросы
- Common-lisp
- Общий синтаксический анализатор Lisp, который сохраняет комментарии