1 Quick start
1.1 Syntax supported
1.2 Use at the command line, to generate HTML
1.3 Use as a library, to generate xexprs and HTML
1.4 Use as a library, to generate "pre-Scribble"
2 Notes
2.1 Links
3.1 Parsing Markdown
3.2 Building a Table of Contents
3.3 Displaying Parsed Markdown
3.4 Generating Pre-Scribble


1 Quick start

1.1 Syntax supported

1.2 Use at the command line, to generate HTML

You can run this at the command-line: Pipe in markdown and it pipes out HTML.

$ raco markdown
I am _emph_ and I am **strong**.
<!DOCTYPE html>
  <meta charset="utf-8" /></head>
  <p>I am <em>emph</em> and I am <strong>strong</strong>.</p></body></html>

1.3 Use as a library, to generate xexprs and HTML

Use parse-markdown to convert a string? or path? to a (listof xexpr?).

You can modify the (listof xexpr?), splice it into the body element of an (html ...) wrapper, and convert to HTML text.

(require markdown)


; 1. Parse string to a list of xexprs


(define xs (parse-markdown "I am _emph_ and I am **strong**."))



'((p () "I am " (em () "emph") " and I am " (strong () "strong") "."))


; 2. Optionally, process the xexprs somehow:
; ... nom nom nom ...
; 3. Splice them into an HTML `xexpr` and...
; 4. Convert to HTML text:


(display-xexpr `(html ()
      (head ())
      (body () ,@xs)))




  <p>I am <em>emph</em> and I am <strong>strong</strong>.</p></body></html>

display-xexpr is provided as a "warm bowl of porridge" — in between xexpr->string (which does no formatting and isn’t very diff-friendly) and display-xml (which does too much and can for example break <pre> formatting).

1.4 Use as a library, to generate "pre-Scribble"

The xexprs returned by read-markdown can also be fed to the function xexprs->scribble-pres, which returns a Scribble representation — a list of pre-part?, pre-flow? or pre-content? items — acceptable to Scribble’s decode, which returns a Scribble part. The part can in turn be fed to any of the Scribble renderers: HTML, LaTeX, plain text, or even Markdown (if you’d like to go around in circles).

#lang racket/base
(require markdown
(define work-dir (find-system-path 'temp-dir))
(define (build-html-doc docs dest-file)
  (let* ([renderer (new (render-mixin render%) [dest-dir work-dir])]
         [fns      (list (build-path work-dir dest-file))]
         [fp       (send renderer traverse docs fns)]
         [info     (send renderer collect  docs fns fp)]
         [r-info   (send renderer resolve  docs fns info)])
    (send renderer render docs fns r-info)
    (send renderer get-undefined r-info)))
(define-runtime-path "test/")
(define part (decode
               (with-input-from-file read-markdown))))
(build-html-doc (list part) "test.html")
(send-url (~a "file://" work-dir "test.html"))

2 Notes

Originally this was implemented using a pile of regular expressions, somewhat like how works — but complicated by creating xexprs not an HTML string. On the bright side, I wrote nearly a hundred unit tests.

In October 2013 I redesigned it to be a "real" parser with a grammar, using Stephen Chang’s parsack, a monadic parser combinator library. The grammar was developed by looking first at peg-markdown and later at pandoc. For a few edge cases I found Babelmark and Babelmark2 helpful to compare result with many other implementations. The many unit tests from the old version also helped.

2.1 Links


 (require markdown) package: markdown

The markdown module provides all bindings from the markdown/display-xexpr, markdown/parse, and markdown/toc modules.

3.1 Parsing Markdown

 (require markdown/parse) package: markdown


(parse-markdown input    
  [footnote-prefix-symbol?])  (listof xexpr?)
  input : (or/c path? string?)
  footnote-prefix-symbol? : symbol? = (gensym)
Parses an entire markdown file.

When given a string?, parses the string as a markdown file. When given a path?, calls file->string and parses the result.


(current-strict-markdown?)  boolean?

(current-strict-markdown? strict?)  void?
  strict? : boolean?
 = #t
Parameter to limit the parser to strict markdown (no customizations).


(read-markdown [footnote-prefix-symbol?])  (listof xexpr?)

  footnote-prefix-symbol? : symbol? = (gensym)
Parses markdown input from current-input-port.

NOTE: This function is deprecated; use parse-markdown, instead.

3.2 Building a Table of Contents

 (require markdown/toc) package: markdown


(toc xexprs)  xexpr?

  xexprs : (listof xexpr?)
Builds a table of contents from the given markdown expression.

> (toc (parse-markdown (string-join '("# 1" "## 1.1" "# 2" "## 2.1") "\n\n")))


  ((class "toc"))


   (li (a ((href "#1")) "1") (ul (li (a ((href "#11")) "1.1"))))

   (li (a ((href "#2")) "2") (ul (li (a ((href "#21")) "2.1"))))))

3.3 Displaying Parsed Markdown

 (require markdown/display-xexpr) package: markdown


(display-xexpr xexpr indent)  any/c

  xexpr : xexpr?
  indent : zero?
Prints an HTML representation of xexpr to current-output-port.


(xexpr->string xexpr)  string?

  xexpr : xexpr?
Unlike Racket’s xexpr->string, this does not over-aggressively encode chars like & in attribute values.

3.4 Generating Pre-Scribble

 (require markdown/scrib) package: markdown

The bindings documented in this section are not provided by the markdown module.


(xexprs->scribble-pres xexprs)

  (listof (or/c pre-part? pre-flow? pre-content?))
  xexprs : (listof xexpr?)
Given a list of xexprs representing valid HTML, return a Scribble representation: A list of pre-part?, pre-flow?, or pre-content? acceptable to Scribble’s decode.

Although this could be generalized, currently it’s only intended to handle the subset of HTML that read-markdown returns.