Created: 2017-04-10 Mon 23:35
We would like to parse the character a
.
The parser of a
is a function that, given an input string
s
:
(defn parse-a [s] (if (= \a (first s)) {:success ["a" (subs s 1)] } {:error s }))
(defn- as-str "Ensures wathever is returned as string" [whatever] (cond (char? whatever) (str whatever) (empty? whatever) "" (string? whatever) whatever))
(defn- success "Creates a success" [parsed nonparsed] {:success [(as-str parsed) (as-str nonparsed)]})
(defn- fail "Creates a failure" [s] {:failure (as-str s)})
(defn- advance [s] (subs s 1))
(defn parse-a [s] (if (= \a (first s)) (success \a (advance s)) (fail s)))
Now that we know how to parse the a
character, we can generalize a bit.
Given a character c
, return a parser that parses c
.
(defn p-char "Returns a char parser" [c] (fn [s] (if (= c (first s)) (success c (advance s)) (fail s))))
(def parse-a (parse-char \a))
From:
(defn parse-a [s] (if (= \a (first s)) {:success ["a" (subs s 1)] } {:error s }))
to
(def parse-a (parse-char \a))
Now parsers (a special kind of functions) are
the objects we deal with (we use def
, not defn
).
We have to climb another level.
a
or b
.(defn parse-a-or-b [a b] (fn [s] (let [f (first s)] (if (or (= a f) (= b f)) (success f (advance s)) (fail s)))))
Yes
Are you listening to me?
We should deal with parsers, not charaters.
(def parse-a (p-char \a))
(def parse-b (p-char \b))
(def parse-a-or-b (p-or parse-a parse-b))
I have not told you about this p-or
yet :$
But we're on the next level, right?
Move on.
We want a parser that given two parsers p1
and p2
returns a parser that parses what p1
parses or what p2
parses.
(defn p-or [p1 p2] (fn [s] (let [r1 (p1 s)] (if (failure? r1) (p2 s) (best-match r1 (p2 s))))))
(defn failure? [result] (contains? result :failure))
(defn- best-match [r1 r2] (let [len1 (count (get-parsed r1)) len2 (count (get-parsed r2))] (if (> len1 len2) r1 r2)))
p-or
is a function that given 2 parsers returns another parser.
It is a parser combinator.
(defn p-apply "Returns a parser that parses as p and applies f to the parsed result if p succeeds. Fails otherwise, of course." [p f] (fn [s] (let [r (p s)] (if (success? r) (success (f (get-parsed r)) (get-nonparsed r)) r))))
(defn- as-str "Ensures wathever is returned as string" [whatever] (cond (char? whatever) (str whatever) (empty? whatever) "" (string? whatever) whatever)) (defn- success "Creates a success" [parsed nonparsed] {:success [(as-str parsed) (as-str nonparsed)]}) (defn- fail "Creates a failure" [s] {:failure (as-str s)}) (defn- advance [s] (subs s 1))
(defn- get-parsed "Extracts parsed part from success" [suc] (let [[parsed nonparse] (:success suc)] parsed))
(defn- get-nonparsed "Extracts next string to be parsed from a success" [suc] (let [[parsed nonparsed] (:success suc)] nonparsed))