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 suspension) (funcall (funcall parser line) prev)
37 (squash-prev (parse-lines (cdr lines) parsed-line))
40 ((regex-to-find (car suspension))
41 (function-to-call (cadr suspension))
44 (lambda (line) (cl-ppcre:scan regex-to-find line))
49 (funcall function-to-call (subseq (cdr lines) 0 (or pos (length lines)))))
50 (parse-lines (nthcdr (1+ pos) (cdr lines))))))
51 (t (cons prev (parse-lines (cdr lines) parsed-line))))))))
53 (defun parse-texts (line)
55 ((textp line) (parse-inline (cadr line)))
56 ((listp line) (mapcar #'parse-texts line))
59 (defun parse-inline (str)
61 ((parser (getf (cadr (find-if #1'(funcall (getf $1 :checker) str) (reverse *inline-parsers*) :key #'cadr)) :parser)))
62 (if parser (funcall parser str) str)))
64 (defmacro defline-parser (regex handler)
66 ((regex (format nil "^~A$" regex)))
68 (when (not (utils:strassoc ,regex *line-parsers*)) (push (list ,regex nil) *line-parsers*))
69 (setf (utils:strassoc ,regex *line-parsers*)
71 :checker (lambda (str) (cl-ppcre:scan ,regex str))
77 (coerce (second (multiple-value-list (cl-ppcre:scan-to-strings ,regex str))) 'list)))))))))
79 (defmacro definline-parser (regex handler)
81 ((regex (format nil "^~A$" regex)))
83 (when (not (utils:strassoc ,regex *inline-parsers*)) (push (list ,regex nil) *inline-parsers*))
84 (setf (utils:strassoc ,regex *inline-parsers*)
86 :checker (lambda (str) (cl-ppcre:scan ,regex str))
88 (apply (function ,handler)
91 (coerce (second (multiple-value-list (cl-ppcre:scan-to-strings ,regex str))) 'list)))))))))
93 ; each parser function needs to return a function that takes the previous line and returns
97 ; whether the previous line should be consumed (optional)
98 ; an optional list with two items, which suspends parsing until regex is found
99 ; - the regex to end the suspension
100 ; - the function to call with the list of lines
104 (if (textp prev) (values (h2 prev) t) (hr))))
108 (if (textp prev) (values (h1 prev) t) (hr))))
110 (defun inline-h1 (text) (lambda (prev) (h1 text)))
111 (defun inline-h2 (text) (lambda (prev) (h2 text)))
112 (defun inline-h3 (text) (lambda (prev) (h3 text)))
113 (defun inline-h4 (text) (lambda (prev) (h4 text)))
114 (defun inline-h5 (text) (lambda (prev) (h5 text)))
116 (defun list-item (text)
120 (if (and prev (listp prev) (eql 'ul (car prev)))
121 (values (append prev (list li)) t)
127 ((textp prev) (values (p prev) t)))))
129 (defun default (text)
130 (lambda (prev) (declare (ignore prev)) text))
132 (defun codefence (codetype)
134 (declare (ignore prev))
143 (format nil "~{~A~%~}" lines))))))))
145 (defline-parser "-+" prev-h2)
146 (defline-parser "=+" prev-h1)
148 ; These need to be in reverse order so they match correctly
149 (defline-parser "##### *(.*)" inline-h5)
150 (defline-parser "#### *(.*)" inline-h4)
151 (defline-parser "### *(.*)" inline-h3)
152 (defline-parser "## *(.*)" inline-h2)
153 (defline-parser "# *(.*)" inline-h1)
154 (defline-parser "```(.*)" codefence)
155 (defline-parser " *\\* *(.*)" list-item)
156 (defline-parser " *" emptiness)
157 (defline-parser "(.*)" default)
159 (defun strength (before during after)
160 (list before (span :style-font-weight "bold" during) after))
162 (defun emphasis (before during after)
163 (list before (span :style-font-style "italic" during) after))
165 (defun md-link (before text md-link after)
166 (list before (a :href md-link (parse-texts text)) after))
168 (definline-parser "(.*)\\[([^\\]]*)\\]\\(([^)]*)\\)(.*)" md-link)
169 (definline-parser "(.*)\\*\\*(.*)\\*\\*(.*)" strength)
170 (definline-parser "(.*)\\*(.*)\\*(.*)" emphasis)