Ребята, практикуюсь в проектировании и реализации CL макросов. В стиле do-макросов (dolist, dotimes, ...) сделал вот такую штуку:
(defmacro do-stream ((var stream &key result (read-function '#'read-line) (eof nil eof-supplied-p)) &body body)
(once-only ((stream stream)
(read-function read-function))
(with-gensyms (start)
`(block nil
(tagbody
,start
(let ((,var
,(if eof-supplied-p
(with-gensyms (%)
`(let ((,% (funcall ,read-function ,stream nil ,eof)))
(if (equal ,% ,eof)
(return ,result)
,%)))
`(handler-case
(funcall ,read-function ,stream t)
(end-of-file () (return ,result))))))
,@body
(go ,start)))))))
Раскрывается следующим образом. С eof так:
(with-open-file (s "input")
(let (result)
(do-stream (line s :result result :eof "Hello")
(push line result))))
(WITH-OPEN-STREAM (S (OPEN "input"))
(LET (RESULT)
(LET ((#:STREAM953 S) (#:READ-FUNCTION954 #'READ-LINE))
(BLOCK NIL
(TAGBODY
#:START955
(LET ((LINE
(LET ((#:%956
(FUNCALL #:READ-FUNCTION954 #:STREAM953 NIL "Hello")))
(IF (EQUAL #:%956 "Hello")
(RETURN RESULT)
#:%956))))
(PUSH LINE RESULT)
(GO #:START955)))))))
(with-open-file (s "input")
(let (result)
(do-stream (item s :result result :read-function #'read)
(declare (type (or symbol fixnum) item))
(push item result))))
(WITH-OPEN-STREAM (S (OPEN "input"))
(LET (RESULT)
(LET ((#:STREAM944 S) (#:READ-FUNCTION945 #'READ))
(BLOCK NIL
(TAGBODY
#:START946
(LET ((ITEM
(HANDLER-CASE (FUNCALL #:READ-FUNCTION945 #:STREAM944 T)
(END-OF-FILE NIL (RETURN RESULT)))))
(DECLARE (TYPE (OR SYMBOL FIXNUM) ITEM))
(PUSH ITEM RESULT)
(GO #:START946)))))))
Интересует мнение экспертов общелелиспа. С ходу вопросы такие:
- Полезен ли такой макрос или в CL принято подобную логику обхода файла делать по другому?
- Нормален ли макрос в плане дизайна? (вместе &optional аргумента result, сделал &key ввиду необходимости задания read-function и eof)
- Нормален ли макрос в плане реализации? Какие есть косяки?
- Есть ли подводные камни в обработке условия end-of-file при отсутствии eof?
- Временную переменную (при наличии eof) задал как %. Это норм или в сообществе принято как-то по другому именовать?
Любая конструктивная критика горячо приветствуется. Обсуждение решения подобного обхода файла на других диалектах Lisp также приветствуются, но всё же в первую очередь интересуют мнения общелисперов. Спасибо большое!