3 (defparameter *line-parsers* nil)
4 (defparameter *inline-parsers* nil)
11 STR: a string, representing the markdown
12 HTML: a weave style html form
16 Parses the actual markdown, returing the html. This is the main
17 function for the honey package."
19 ((lines (cl-ppcre:split "\\n" str)))
22 (parse-lines (append lines (list ""))))))
24 (defun textp (x) (and (listp x) (eql 'text (car x))))
25 (defun as-text (x) (list 'text x))
27 (defun parse-lines (lines &optional prev)
33 (parser (getf (cadr (find-if #1'(funcall (getf $1 :checker) line) (reverse *line-parsers*) :key #'cadr)) :parser)))
34 (when (not parser) (error "Weird! Couldn't find a match for ~A" line))
35 (multiple-value-bind (parsed-line squash-prev) (funcall (funcall parser line) prev)
37 (parse-lines (cdr lines) parsed-line)
38 (cons prev (parse-lines (cdr lines) parsed-line)))))))
40 (defun parse-texts (line)
42 ((textp line) (parse-inline (cadr line)))
43 ((listp line) (mapcar #'parse-texts line))
46 (defun parse-inline (str)
48 ((parser (getf (cadr (find-if #1'(funcall (getf $1 :checker) str) (reverse *inline-parsers*) :key #'cadr)) :parser)))
49 (if parser (funcall parser str) str)))
51 (defmacro defline-parser (regex handler)
53 ((regex (format nil "^~A$" regex)))
55 (when (not (utils:strassoc ,regex *line-parsers*)) (push (list ,regex nil) *line-parsers*))
56 (setf (utils:strassoc ,regex *line-parsers*)
58 :checker (lambda (str) (cl-ppcre:scan ,regex str))
64 (coerce (second (multiple-value-list (cl-ppcre:scan-to-strings ,regex str))) 'list)))))))))
66 (defmacro definline-parser (regex handler)
68 ((regex (format nil "^~A$" regex)))
70 (when (not (utils:strassoc ,regex *inline-parsers*)) (push (list ,regex nil) *inline-parsers*))
71 (setf (utils:strassoc ,regex *inline-parsers*)
73 :checker (lambda (str) (cl-ppcre:scan ,regex str))
75 (apply (function ,handler)
78 (coerce (second (multiple-value-list (cl-ppcre:scan-to-strings ,regex str))) 'list)))))))))
80 ; each parser function needs to return a function that takes the previous line and returns either
82 ; a single element being the new element, and optionally a second values option about whether
83 ; the previous line should be consumed
87 (if (textp prev) (values (h2 prev) t) (hr))))
91 (if (textp prev) (values (h1 prev) t) (hr))))
93 (defun list-item (text)
97 (if (and prev (listp prev) (eql 'ul (car prev)))
98 (values (append prev (list li)) t)
104 ((textp prev) (values (p prev) t)))))
106 (defun default (text)
107 (lambda (prev) (declare (ignore prev)) text))
109 (defline-parser "-+" prev-h2)
110 (defline-parser "=+" prev-h1)
111 (defline-parser " *\\* *(.*)" list-item)
112 (defline-parser " *" emptiness)
113 (defline-parser "(.*)" default)
115 (defun strength (before during after)
116 (list before (span :style-font-weight "bold" during) after))
118 (defun emphasis (before during after)
119 (list before (span :style-font-style "italic" during) after))
121 (defun md-link (before text md-link after)
122 (list before (a :href md-link (parse-texts text)) after))
124 (definline-parser "(.*)\\[([^\\]]*)\\]\\(([^)]*)\\)(.*)" md-link)
125 (definline-parser "(.*)\\*\\*(.*)\\*\\*(.*)" strength)
126 (definline-parser "(.*)\\*(.*)\\*(.*)" emphasis)