Skip to content

Instantly share code, notes, and snippets.

@sogaiu
Last active April 11, 2025 09:17
Show Gist options
  • Save sogaiu/09975ae019fce55b2c971c466cc87f1f to your computer and use it in GitHub Desktop.
Save sogaiu/09975ae019fce55b2c971c466cc87f1f to your computer and use it in GitHub Desktop.
tweaked mendoza.janet from initial commit of mendoza
# parse docstring's flavor of markdown
# based on initial commit of mendoza by bakpakin
(defn- combine-strings
"Flatten consecutive strings in an indexed structure by concatenating
them. The resulting document graph should be much cleaner. Also, empty
strings will be removed entirely."
[els]
(def buf @"")
(def accum @[])
(defn bflush []
(unless (empty? buf)
(array/push accum (string buf))
(buffer/clear buf)))
(loop [x :in els]
(if (bytes? x)
(buffer/push-string buf x)
(do (bflush) (array/push accum x))))
(bflush)
(tuple/slice accum))
(defn- capture-tokens
"Wrapper to make combine-strings variadic and useful as a subtitution
function in the peg grammar."
[& args]
(combine-strings args))
(defn- line-el
"Create a peg pattern for parsing some markup
on a line. Captures the HTML text to output."
[delim tag]
(defn replace
[& args]
{:tag tag
:content (combine-strings args)})
~(* ,delim
(/ (any (if-not ,delim :token)) ,replace)
,delim))
(defn- capture-code
"Peg capture function for single line of code"
[code]
{:tag "code"
"class" "medoza-code"
:content [code]})
(defn- capture-li
"Capture a list inside the peg and create a Document Node."
[& contents]
{:tag "li"
:content (combine-strings contents)})
(defn- capture-ol
"Capture a list inside the peg and create a Document Node."
[& contents]
{:tag "ol"
:content (combine-strings contents)})
(defn- capture-ul
"Capture a list inside the peg and create a Document Node."
[& contents]
{:tag "ul"
:content (combine-strings contents)})
(defn- capture-paragraph
"Capture a paragraph node in the peg."
[& contents]
{:tag "p"
:content (combine-strings contents)})
(def- md-grammar
"Grammar for markdown -> document AST parser."
~{:next (any (+ (set "\t \n\r") -1))
:opt-ws (any (set "\t "))
:nl-char (+ (* (? "\r") "\n") -1)
:nl ':nl-char
:escape (* "\\" '1)
:strong ,(line-el "**" "strong")
:em ,(line-el "*" "em")
:code (* "`" (/ '(any (if-not "`" 1)) ,capture-code) "`")
:token (* ':opt-ws
(+ :strong :em :code :escape '(if-not :nl-char 1)))
:lines (some (* (some :token) :opt-ws :nl))
:li (* (/ (some :token) ,capture-li) :nl)
:ulli (* :opt-ws (set "-*") :li)
:olli (* :opt-ws (some (range "09")) "." :li)
:ul (* (/ (some :ulli) ,capture-ul) :nl)
:ol (* (/ (some :olli) ,capture-ol) :nl)
:paragraph (/ (* :lines :nl-char) ,capture-paragraph)
:main (any (* :next
(+ :ul :ol :paragraph -1 (error ""))))})
(def- md-peg
"A peg that converts markdown to html."
(peg/compile md-grammar))
(defn md-parse
"Parse docstring markdown and return a dom."
[source]
(def matches (peg/match md-peg source))
(unless matches (error "bad docstring markdown"))
#
{:tag "div"
:content matches})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment