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 -~----------~----~----~----~------~----~------~--~---