challenge 改行をBRタグに置き換える

一部のHTMLタグを通すフィルタ どう書く?の続編です。 前回の条件を満たしつつ、入力中の改行を<br/>に置き換えてください。ただし、たとえば"<a\nhref=...>"といったようにタグの中に改行がある場合、単純に置換するわけには行かないことに注意してください。

また、ユーザの入力注の<br>は<br/>に変換してください。

このお題はperezvonさんの提案を元にした三部作の二問目です。ご協力ありがとうございました。

Posted feedbacks - Common Lisp

今回の改行の処理の対応と、
前回のものが、属性のなかのタグの途中で改行が入った
"<foo clear='<scr
   ipt>foo>'>foo</foo>"
のようなものを上手く処理できていないことに気付いたので直し…
…たつもりですが、私には問題が難し過ぎたようで、
何が何だが分からないものになり果てました…。

タグのエスケープ処理のため2種の文字を予約で消費してしまいます。
下記では、ここへの投稿のために"【"と"】"を使いました。

また、前回はライブラリが見付けられずAllegroのparse-htmlを
使用して作成しましたが、Clikiにポータブル版のpxmlutilsが
ありました。
http://www.cliki.net/pxmlutils
今回はそれを導入してsbclで動作確認しています。
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
(use-package :net.html.parser)
(use-package :cl-ppcre)
(use-package :url-rewrite)

(defun html-filter-2 (str)
  (regex-replace-all-lis 
   '(("】" . "&gt;") ("【" . "&lt;") ("\\n" . "<br />"))
   (html-filter-1 
    (prop-filter-2 (pickup-tag (<>-to-escape (prop-filter-1 str)))))))

(defun regex-replace-all-lis (repl-alist str 
			      &optional &key case-insensitive-mode)
  (reduce
   (lambda (res item)
     (let ((scan (create-scanner 
		  (car item)
		  :case-insensitive-mode case-insensitive-mode)))
       (regex-replace-all scan res (cdr item))))
   repl-alist :initial-value str))

(defun <>-to-escape (str)
  (regex-replace-all-lis '((">" . "】")("<" . "【")) str))

(defun <>-to-ltgt (str)
  (regex-replace-all-lis '((">" . "&gt;")("<" . "&lt;")) str))

(defun pickup-tag (str)
  (regex-replace-all-lis 
   '(("【(a\\s*[^\\s】]*)】" .  "<\\1>")
     ("【(/a\\s*)】" .  "</a>")
     ("【(strong\\s*[^\\s】]*)】" .  "<\\1>")
     ("【(/strong\\s*)】" .  "</strong>")
     ("【(br\\s*[^\\s】]*)】" .  "<br />"))
   str))

(defun prop-filter-1 (str)
  (prop-filter-* str "('[^']*')" #'url-encode))

(defun prop-filter-2 (str)
  (prop-filter-* str "(【[^】]*】)" #'<>-to-ltgt))

(defun prop-filter-* (str scan-pat repl-func)
  (let ((scan (create-scanner scan-pat :case-insensitive-mode t)))
    (regex-replace-all scan str 
		       (lambda (match &rest rest)
			 (declare (ignore rest))
			 (if (scan "[<>]" match)
			     (funcall repl-func match)
			     match))
		       :simple-calls t)))

(defun html-filter-1 (string)
  (let ((form (sanitize-html (parse-html string))))
    (apply #'concatenate 'string (build-html form))))

(defun sanitize-html (form)
  (mapcar 
   (lambda (item)
     (if (atom item)
	 item
	 (let ((keys `(,(car item) ,(and (consp (car item)) (caar item)))))
	   (cond ((member :a keys) 
		  `(,(car item) ,@(sanitize-html (cdr item))))
		 ((member :strong keys)
		  `(:strong ,@(sanitize-html (cdr item))))
		 ((member :br keys) :br)
		 ('T "")))))
   form))

(defun build-html (form)
  (if (atom form) 
      form
      (cond ((and (atom (car form)) (eq :br (car form)))
	     `(,(br) ,@(build-html (cdr form))))
	    ((consp (car form))
	     `(,(let* ((top (car form))
		       (keys `(,(car top) ,(and (consp (car top)) (caar top)))))
		      (cond ((member :a keys) (build-a top))
			    ((member :strong keys) (build-strong top))
			    ('T top)))
		,@(build-html (cdr form))))
	    ('T `(,(build-html (car form))
		   ,@(build-html (cdr form)))))))

(defun br () "<br />")

(defun build-strong (form)
  (format nil "<strong>~{~A~}</strong>" (build-html (cdr form))))

(defun build-a (form)
  (let ((tag (car form))
	(body (build-html (cdr form))))
    (if (and (consp (car form)) 
	     (member (second tag) '(:href :name)))
	(format nil "<a ~(~A~)='~A'>~{~A~}</a>" 
		(second tag) 
		(put-dotslash (third tag)) body)
	(format nil "<a>~{~A~}</a>" (build-html (cdr form))))))

(defun put-dotslash (str)
  (let ((s (create-scanner "^([Hh][Tt][Tt][Pp][Ss]*://|^/|\./)")))
    (if (scan s str)
	str
	(concatenate 'string "./" str))))

(defun prop-maker (lst)
  (do ((l lst (cddr l))
       result)
      ((endp l) (nreverse result))
  (push (format nil "~A=\"~A\"" 
		(car l) (<>-to-ltgt (cadr l))) result)))

Index

Feed

Other

Link

Pathtraq

loading...