;; 2023-06-25 (defvar xah-html-selfclose-tags nil "List of HTML5 self-closing tag name. " ) (setq xah-html-selfclose-tags '( "!doctype" "area" "base" "br" "col" "command" "embed" "hr" "img" "input" "keygen" "link" "meta" "param" "source" "track" "wbr" )) (defun xah-html--tag-self-closing-p (TagName) "Return t if the tag is a self-closing tag such as img, hr, br." (member TagName xah-html-selfclose-tags)) (defun xah-html--get-tag-name (TagBegin) "Return the tag name. TagBegin must be on the left of angle bracket: ▮< , can be opening or closing tag. The function returns the element name immediately to the right. Version: 2021-06-22 2022-01-20 2023-06-27" (when (not (char-equal (char-after TagBegin) ?<)) (error "TagBegin not on the left of <")) (save-excursion (let (xp1 xp2) (goto-char TagBegin) (forward-char 1) (when (looking-at "/") (forward-char 1)) (setq xp1 (point)) (skip-chars-forward "!A-Za-z0-9") (setq xp2 (point)) (buffer-substring-no-properties xp1 xp2)))) (defun xah-html--opening-tag-p (TagBegin) "Return t if tag is opening tag or self closing, else nil. TagBegin must be on the left of angle bracket: ▮< , can be opening or closing tag. Version: 2021-06-22 2021-08-16 2021-08-30" (when (not (char-equal (char-after TagBegin) ?<)) (error "TagBegin not on the left of <" )) (not (char-equal (char-after (1+ TagBegin)) ?/))) ;; HHHH--------------------------------------------------- ;; each tag must have a opening tag and a closing tag. ;; except if the tag ends in /> then it is a self closing. in xml. ;; tag sequence occure in 2 ways ;; one is nested, like so ;; ( ( 3c9de ( 530 ) toh ) ) ;; the other situation is sequential ;; ( ) () () ;; algorithm outline ;; move cursor to beginning, then, search for the next occurance of angle bracket. then, grab it, it is a tag, may be opening tag or closing, push it onto stack. (defun xah-validate-xml-buffer () "validator for xml, check well-formedness Version 2023-06-25 2023-06-27" (interactive) (let (xp1 xp2 xtag xisOpen xtagname (xstack nil) ;; xstack is a list, each element is [xp1 xp2 xtagname xisOpen] ) ;; get current pos, find next angle brack pos, grab text in between, that's one tag, push it to stack (goto-char (point-min)) (while (re-search-forward "[<>]" nil t) (progn (if (eq (char-before) ?<) (setq xp1 (1- (point))) (error "error found at position %s" (point))) (re-search-forward "[<>]" nil t) (if (eq (char-before) ?>) (setq xp2 (point)) (error "error found at position %s" (point))) (setq xtagname (xah-html--get-tag-name xp1)) (if (xah-html--tag-self-closing-p xtagname) nil (progn (setq xisOpen (xah-html--opening-tag-p xp1)) (if xisOpen (push (vector xp1 xp2 xtagname xisOpen) xstack) (progn ;; if the current tag is closing, then, check the last item on the stack, if it's opening, and tagname match, then remove that on stack (if (and (string-equal xtagname (aref (car xstack) 2)) (aref (car xstack) 3)) (pop xstack) (push (vector xp1 xp2 xtagname xisOpen) xstack)))))))) (if (eq 0 (length xstack)) (progn (message "done. You code is PERFECT!") t ) (progn (goto-char (aref (car xstack) 0)) (message "error. here's the stack:") (print xstack) nil ))))