Yellow Rabbit

Комментарии как второй по важности элемент HTML

Браузер на Lisp: разбираем комментарии

После того, как удалось разобрать текстовый элемент HTML, переходим к более сложному элементу - комментариям.

Комментарии

Комментарии в HTML интересны тем, что заканчиваются аж тремя символами: -->. То есть, если бы мы читали по-символьно из потока вместо строки, то нам предстояло бы редкое веселье с заглядыванием вперёд на два символа. Сейчас же можно просто восстановить указатель на текущий символ.

Создание узла комментария очень простое:


(defun make-comment-node ()
  (make-instance 'comment-node))

Итак для разбора комментария первым делом, что первым делом? Пианину! Шутка. Первым делом запоминаем позицию index в строке чтобы потом откатится (oldindex).

Начало комментария определить легко — просто последовательность: ["!--" что-то-дальше]1, а вот с финальным --> всё не так хорошо.

Мы не можем использовать последовательность [$@(any-text ch) "-->"], потому что повторяющееся сравнение с любым символом $@(any-text ch) попросту поглотит всю строку, не дав шанса обнаружить -->.

Повторяющаяся альтернатива ${"-->" @(any-text ch)} так же не вариант: хотя мы и способны теперь обнаружить конец комментария, но мы не можем выйти из повторения.

Чтобы сработать сравнение с --> должно не сработать:smile: То есть, найдя --> мы запоминаем факт обнаружения в переменной eoc-found2 и говорим, что сравнение не удалось !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 парсер правильно поглотил всю внутренность комментария.

Дерево после разбора

  1. Здесь нет < по той причине, что этот символ уже будет использован чтобы отличить комментарий или элемент от простого текста. 

  2. eoc означает End Of Comment