--- /dev/null
+(in-package #:honey)
+
+(defparameter *line-parsers* nil)
+(defparameter *inline-parsers* nil)
+
+(defun parse (str)
+ "PARSE STR => HTML
+
+ARGUMENTS AND VALUES:
+
+ STR: a string, representing the markdown
+ HTML: a weave style html form
+
+DESCRIPTION:
+
+ Parses the actual markdown, returing the html. This is the main
+ function for the honey package."
+ (let
+ ((lines (cl-ppcre:split "\\n" str)))
+ (mapcar
+ #'parse-texts
+ (parse-lines (append lines (list ""))))))
+
+(defun textp (x) (and (listp x) (eql 'text (car x))))
+(defun as-text (x) (list 'text x))
+
+(defun parse-lines (lines &optional prev)
+ (if
+ (not lines)
+ (list prev)
+ (let*
+ ((line (car lines))
+ (parser (getf (cadr (find-if #1'(funcall (getf $1 :checker) line) (reverse *line-parsers*) :key #'cadr)) :parser)))
+ (when (not parser) (error "Weird! Couldn't find a match for ~A" line))
+ (multiple-value-bind (parsed-line squash-prev) (funcall (funcall parser line) prev)
+ (if squash-prev
+ (parse-lines (cdr lines) parsed-line)
+ (cons prev (parse-lines (cdr lines) parsed-line)))))))
+
+(defun parse-texts (line)
+ (cond
+ ((textp line) (parse-inline (cadr line)))
+ ((listp line) (mapcar #'parse-texts line))
+ (t line)))
+
+(defun parse-inline (str)
+ (let
+ ((parser (getf (cadr (find-if #1'(funcall (getf $1 :checker) str) (reverse *inline-parsers*) :key #'cadr)) :parser)))
+ (if parser (funcall parser str) str)))
+
+(defmacro defline-parser (regex handler)
+ (let
+ ((regex (format nil "^~A$" regex)))
+ `(progn
+ (when (not (utils:strassoc ,regex *line-parsers*)) (push (list ,regex nil) *line-parsers*))
+ (setf (utils:strassoc ,regex *line-parsers*)
+ (list
+ :checker (lambda (str) (cl-ppcre:scan ,regex str))
+ :parser (lambda (str)
+ (apply
+ (function ,handler)
+ (mapcar
+ #'as-text
+ (coerce (second (multiple-value-list (cl-ppcre:scan-to-strings ,regex str))) 'list)))))))))
+
+(defmacro definline-parser (regex handler)
+ (let
+ ((regex (format nil "^~A$" regex)))
+ `(progn
+ (when (not (utils:strassoc ,regex *inline-parsers*)) (push (list ,regex nil) *inline-parsers*))
+ (setf (utils:strassoc ,regex *inline-parsers*)
+ (list
+ :checker (lambda (str) (cl-ppcre:scan ,regex str))
+ :parser (lambda (str)
+ (apply (function ,handler)
+ (mapcar
+ #'parse-inline
+ (coerce (second (multiple-value-list (cl-ppcre:scan-to-strings ,regex str))) 'list)))))))))
+
+; each parser function needs to return a function that takes the previous line and returns either
+;
+; a single element being the new element, and optionally a second values option about whether
+; the previous line should be consumed
+
+(defun prev-h2 ()
+ (lambda (prev)
+ (if (textp prev) (values (h2 prev) t) (hr))))
+
+(defun prev-h1 ()
+ (lambda (prev)
+ (if (textp prev) (values (h1 prev) t) (hr))))
+
+(defun list-item (text)
+ (lambda (prev)
+ (let
+ ((li (li text)))
+ (if (and prev (listp prev) (eql 'ul (car prev)))
+ (values (append prev (list li)) t)
+ (ul li)))))
+
+(defun emptiness ()
+ (lambda (prev)
+ (cond
+ ((textp prev) (values (p prev) t)))))
+
+(defun default (text)
+ (lambda (prev) (declare (ignore prev)) text))
+
+(defline-parser "-+" prev-h2)
+(defline-parser "=+" prev-h1)
+(defline-parser " *\\* *(.*)" list-item)
+(defline-parser " *" emptiness)
+(defline-parser "(.*)" default)
+
+(defun strength (before during after)
+ (list before (span :style-font-weight "bold" during) after))
+
+(defun emphasis (before during after)
+ (list before (span :style-font-style "italic" during) after))
+
+(defun md-link (before text md-link after)
+ (list before (a :href md-link (parse-texts text)) after))
+
+(definline-parser "(.*)\\[([^\\]]*)\\]\\(([^)]*)\\)(.*)" md-link)
+(definline-parser "(.*)\\*\\*(.*)\\*\\*(.*)" strength)
+(definline-parser "(.*)\\*(.*)\\*(.*)" emphasis)