После того, как удалось разобрать текстовый элемент HTML, переходим к более сложному элементу - комментариям.
Комментарии в HTML интересны тем, что заканчиваются аж тремя символами: -->
. То есть, если бы мы читали по-символьно из потока вместо строки, то нам предстояло бы редкое веселье с заглядыванием вперёд на два символа. Сейчас же можно просто восстановить указатель на текущий символ.
Создание узла комментария очень простое:
(defun make-comment-node ()
(make-instance 'comment-node))
Итак для разбора комментария первым делом, что первым делом? Пианину! Шутка. Первым делом запоминаем позицию index
в строке чтобы потом откатится (oldindex
).
Начало комментария определить легко — просто последовательность: ["!--" что-то-дальше]
1, а вот с финальным -->
всё не так хорошо.
Мы не можем использовать последовательность [$@(any-text ch) "-->"]
, потому что повторяющееся сравнение с любым символом $@(any-text ch)
попросту поглотит всю строку, не дав шанса обнаружить -->
.
Повторяющаяся альтернатива ${"-->" @(any-text ch)}
так же не вариант: хотя мы и способны теперь обнаружить конец комментария, но мы не можем выйти из повторения.
Чтобы сработать сравнение с -->
должно не сработать То есть, найдя -->
мы запоминаем факт обнаружения в переменной eoc-found
2 и говорим, что сравнение не удалось !nil
. Далее мы будем поглощать все символы подряд только, если -->
не была найдена.
(parse-comment ()
"<!-- ??? -->"
(let (ch eoc-found (oldindex index))
(or (and (matchit
["!--"
{${ ["-->" !(setf eoc-found t) !nil]
[!(not eoc-found) @(any-text ch)]
} !eoc-found}])
(make-comment-node))
(progn (setf index oldindex) nil))))
Проверяем, заменив вызов parse-tex
в parse-html
на вызов (cons (parse-comment) (princ index)))
:
* (ql:quickload 'toy-engine)
To load "toy-engine":
Load 1 ASDF system:
toy-engine
; Loading "toy-engine"
(TOY-ENGINE)
* (in-package :toy-engine)
#<PACKAGE "TOY-ENGINE">
* (defparameter *str* "!-- ''' This is a text< kj-- -> --> 123")
*STR*
* (length *str*)
42
* (parse-html *str*)
38
(#<COMMENT-NODE {1005025E03}> . 38)
* (pp->dot "comment-node.dot" (lambda () (pp-dom (car *))))
"}"
*
Как видно из index=38
парсер правильно поглотил всю внутренность комментария.