summaryrefslogtreecommitdiff
path: root/src/clarktown/core.clj
blob: 436b7829e73879256bcb8628cfe70ebb5d27a531 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
(ns clarktown.core
  (:require
    [clojure.string :as string]
    [clarktown.parsers.empty-block :as empty-block]
    [clarktown.parsers.heading-block :as heading-block]))


(def default-parsers
  [{:matcher empty-block/is?
    :renderers [empty-block/render]}
   {:matcher heading-block/is?
    :renderers [heading-block/render]}])


(defn find-parser-by-block
  "Find a parser from `parsers` that matches the given `block`."
  [parsers block]
  (->> parsers
       (filter
         (fn [{:keys [matcher]}]
           (matcher block)))
       first))


(defn parse-block-with-known-parser
  "Parses a given `block` with a known `parser`."
  [parser block]
  (loop [block block
         renderers (:renderers parser)]
    (if (empty? renderers)
      block
      (let [renderer (first renderers)]
        (recur (renderer block)
               (drop 1 renderers))))))


(defn- parse-block-with-unknown-parsers
  "Parses the given `block` with all the parsers that do not have
  a matcher function, useful for any fallback parsing one might want
  to do."
  [parsers block]
  (loop [block block
         parsers (filter #(= nil (:matcher %)) parsers)]
    (if (empty? parsers)
      block
      (loop [block block
             renderers (:renderers (first parsers))]
        (if (empty? renderers)
          block
          (let [renderer (first renderers)]
            (recur (renderer block)
                   (drop 1 renderers))))))))


(defn- parse-blocks
  "Parses each individual Markdown block, given as `blocks`, with
  the list of `parsers`."
  [blocks parsers]
  (for [block blocks]
    (if-let [parser (find-parser-by-block parsers block)]
      (parse-block-with-known-parser parser block)
      (parse-block-with-unknown-parsers parsers block))))


(defn- parse
  "Parses given `markdown` with `parsers`."
  [markdown parsers]
  (let [blocks (string/split markdown #"\n\n")
        parsed-blocks (parse-blocks blocks parsers)]
    (string/join "" parsed-blocks)))


(defn render
  "Renders the given `markdown` into a consumable HTML form. Optionally,
  a second argument can be passed that is made out of a vector of parsers.

  A parser is a map that consists of two things;
  - A matcher (optional) , which returns a truthy or falsey value
  - Renderers, which will be run on the a block when matcher returns true.
    There can be any number of renderers.

  Each matcher, and each renderer have to be a function that take a single
  argument, which is a given Markdown block.

  An example parser:
  ```
  {:matcher (fn [block] ...)
   :renderers [(fn [block] ...) (fn [block] ...)]}
  ```"
  ([markdown]
   (render markdown default-parsers))
  ([markdown parsers]
   (parse markdown parsers)))


(render (slurp "/Users/asko/Documents/work/clarktown/test.md"))