Skip to main content
deleted 2 characters in body
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80

I'm going to admit right off the bat here that I've never written a a parser using combinator functions before. I have quite a bit of experience with Clojure though, so I'll be primarily focusing on idiomatic Clojure, and proper functional practice.

I'm going to admit right off the bat here that I've never written a a parser using combinator functions before. I have quite a bit of experience with Clojure though, so I'll be primarily focusing on idiomatic Clojure, and proper functional practice.

I'm going to admit right off the bat here that I've never written a parser using combinator functions before. I have quite a bit of experience with Clojure though, so I'll be primarily focusing on idiomatic Clojure, and proper functional practice.

added 293 characters in body
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80

I came back to this and realized it can be reduced further! You can get rid of the call to map and make the #(% stream) the predicate of some. This makes it trivial:

(defn alt
  "Returns first successful parse"
  [& ps]
  (fn [stream]
    (some #(% stream) ps)))

I came back to this and realized it can be reduced further! You can get rid of the call to map and make the #(% stream) the predicate of some. This makes it trivial:

(defn alt
  "Returns first successful parse"
  [& ps]
  (fn [stream]
    (some #(% stream) ps)))
added 935 characters in body
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80

Regarding your recent edit clarifying what seqn is doing, these are the two options I'd choose between:

(defn sequence-parser1 [& parsers]
  (fn [stream]
    (reduce (fn [[res acc-stream] p]
              (let [[r stream'] (p acc-stream)]
                [(conj res r) stream']))
            [[] stream]
            parsers)))

(defn sequence-parser2 [& parsers]
  (fn [stream]
    (loop [[p & rest-parsers] parsers
           acc-stream stream
           res []]
      (if p
        (let [[r stream'] (p acc-stream)]
          (recur rest-parsers stream' (conj res r)))

        [res acc-stream]))))

Both are basically the same code; just a reduction over parsers. I tend to lean towards using loop when I have multiple accumulators though, as I find constant pairing/deconstruction in the reducing function to be messy.

If you didn't need to accumulate a stream while iterating the parsers, this could be done very succinctly using map, but alas, that wouldn't work here unfortunately.


Regarding your recent edit clarifying what seqn is doing, these are the two options I'd choose between:

(defn sequence-parser1 [& parsers]
  (fn [stream]
    (reduce (fn [[res acc-stream] p]
              (let [[r stream'] (p acc-stream)]
                [(conj res r) stream']))
            [[] stream]
            parsers)))

(defn sequence-parser2 [& parsers]
  (fn [stream]
    (loop [[p & rest-parsers] parsers
           acc-stream stream
           res []]
      (if p
        (let [[r stream'] (p acc-stream)]
          (recur rest-parsers stream' (conj res r)))

        [res acc-stream]))))

Both are basically the same code; just a reduction over parsers. I tend to lean towards using loop when I have multiple accumulators though, as I find constant pairing/deconstruction in the reducing function to be messy.

If you didn't need to accumulate a stream while iterating the parsers, this could be done very succinctly using map, but alas, that wouldn't work here unfortunately.

deleted 43 characters in body
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80
Loading
added 15 characters in body
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80
Loading
added 9 characters in body; deleted 1 character in body
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80
Loading
Massive edits because I wrote most of this while on the bus.
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80
Loading
Source Link
Carcigenicate
  • 16.3k
  • 3
  • 35
  • 80
Loading