I tried asking about this yesterday, but it seems like I expressed my
problem poorly. Anyways, here's another shot. :)

I have a little parser library. With its metafunctions, one can create
rules that accept tokens and spit out a result or nil if the tokens it
receives are invalid.
For instance, literal creates a basic literal rule that matches only a
token coll that starts with a certain literal token.
conc takes many rule functions and creates a new rule function that
slurps up the results of its subrules in order; if any of its subrules
fail, it fails too.
alt takes many rules and creates a new rule that tries each of its
subrules; it returns the results of the first one that succeeds.

This pattern has been going great for what I need for the past month,
until I needed to create a parser for a language containing rules that
referred to each other. Specifically, a language where a "value" can
be 0, 1, or an "array"—and "arrays" can contain two values.

Whenever I've tried to do this, I've always encountered unbound
variable errors for the value variable. So therefore, I need a way to
defer evaluation of conc and alt's arguments so that their arguments
are not called until needed.

I want to make the code below possible with as little API change as
possible, and the only way I can think of is by changing conc and alt
to macros. I'm not good with macros, so I have little idea how to do
this as elegantly as possible. Here's the code below:

(defn conc [& subrules]
  (fn [tokens]
    (loop [subrule-queue (seq subrules), remaining-tokens (seq
tokens), products []]
      (if (nil? subrule-queue)
        [products remaining-tokens]
        (let [[subrule-products subrule-remainder :as subrule-result]
              ((first subrule-queue) remaining-tokens)]
          (when-not (nil? subrule-result)
            (recur (rest subrule-queue) subrule-remainder
                   (conj products subrule-products))))))))

(defn alt [& subrules]
  (fn [tokens]
    (some #(% tokens) subrules)))

(defn literal [literal-token]
  (fn [tokens]
    (let [first-token (first tokens), remainder (rest tokens)]
      (when (= first-token literal-token)
        [first-token remainder]))))

; Here's the language rules below, defined in terms of literal, conc,
and alt.

(def on (literal \1))
(def off (literal \0))
(def bit (alt on off))

; (bit (seq "starst")) -> (bit (seq "1, 0")) -> [\1 (\, \space \0)]

(def array-start (literal \[))
(def array-end (literal \]))
(def array-sep (literal \,))

; (array-start (seq "[1, 0]")) -> [\[ (\1 \, \0 \])]

(declare value)

(def array (conc array-start value array-sep value array-end))

(def value (alt array bit))

; (array (seq "0")) -> nil
; (bit (seq "0")) -> [\0 nil]
; (value (seq "0")) -> [\0 nil]

; (array (seq "[0,0]")) -> [[\[ \0 \, \0 \]] nil]
; (bit (seq "[0,0]")) -> nil
; (value (seq "[0,0]")) -> [[\[ \0 \, \0 \]] nil]

; (array (seq "[0,[1,0]]")) -> [[\[ \0 \, [\[ \q \, \0 \]] \]] nil]
; (bit (seq "[0,[1,0]]")) -> nil
; (value (seq "[0,[1,0]]")) -> [[\[ \0 \, [\[ \q \, \0 \]] \]] nil]


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to