Well, I just find out there was an obvious bug in the code I posted
previously. Here's the correct expand-parser-body:

(defn- expand-parser-body [body s]
  (let [rec (fn [r p xs]
              `(let [~r (~p ~s) ~s (second (first ~r))]
                 (if (= 0 (count ~r)) ~r
                     ~(expand-parser-body xs s))))]
    (match body
           (_ '<- _) (parser-error)
           (p) (list p s)
           (v '<- p & xs) (rec v p xs)
           (p & xs) (rec (gensym 'r) p xs))))

So now we can do this:

user=> ((parser a <- (pchar \t) (result a)) "test")
([([\t "est"]) "est"])

And the 'string' combinator can be simply written like that:

(defn string [s]
  (if (= 0 (count s)) (result "")
      (let [[x xs] (split s)]
        (parser (pchar x)
                (string xs)
                (result (join x xs))))))

On Aug 29, 4:09 pm, budu <[EMAIL PROTECTED]> wrote:
> Thank you very much! Your trick worked and it even made me realize
> that the match-forms function is not even required. I've replaced it
> by a much more simple is-match? function and completely overhauled the
> match macro.
>
> Here's the new code with your corrections, some other improvements and
> an example of how I intend to use it:
>
> (defn result [v] #(list [v %]))
>
> (def zero (fn [s] ()))
>
> (def item
>   #(cond (= 0 (. % length)) ()
>          true (list [(. % (charAt 0))
>                      (. % (substring 1))])))
>
> (defn bind [p f]
>   (fn [s]
>      (let [r (p s)]
>        (if (= 0 (count r)) r
>            (mapcat #(let [[v s] %] ((f v) s)) r)))))
>
> (defn sat [p]
>   (bind item #(if (p %) (result %) zero)))
>
> (defn pchar [c] (sat #(= % c)))
>
> (defn is-match? [p t]
>   (if (and (seq? p) (= 'quote (first p)))
>     (= t (second p))
>     (symbol? p)))
>
> (defmacro match [value & clauses]
>   (when (and clauses (= 0 (rem (count clauses) 2)))
>     (let [[c1 c2 & cr] clauses
>           only-sym #(or (not (symbol? %)) (= '_ %))
>           syms (map #(if (only-sym %) (gensym) %) c1)]
>       `(if (and (every? identity (map is-match? '~c1 ~value))
>                 (or (some (fn [x#] (= '& x#)) '~c1)
>                     (= (count '~c1) (count ~value))))
>          (if (= 0 (count '~syms)) false
>              (let [~(vec syms) ~value] ~c2))
>          (match ~value [EMAIL PROTECTED])))))
>
> (defn- parser-error []
>   (new Exception "Parser must end with non-binding form."))
>
> (defn- expand-parser-body [body s]
>   (let [rec (fn [p xs]
>               `(let [r# (~p ~s) ~s (second (first r#))]
>                  (if (= 0 (count r#)) r#
>                      ~(expand-parser-body xs s))))]
>     (match body
>            (_ '<- _) (parser-error)
>            (p) (list p s)
>            (v '<- p & xs) (rec p xs)
>            (p & xs) (rec p xs))))
>
> (defmacro parser [& body]
>   (let [s (gensym 's)]
>     `(fn [~s] ~(expand-parser-body body s))))
>
> (defn split [s]
>   (if (= 0 (count s)) []
>       [(. s (charAt 0))
>        (. s (substring 1 (count s)))]))
>
> (defn join [c s]
>   (. String (format "%c%s" (to-array [c s]))))
>
> ;; string :: String -> Parser String
> (defn string [s]
>   (if (= 0 (count s)) (result "")
>       (let [[x xs] (split s)]
>         (parser _ <- (pchar x)
>                 _ <- (string xs)
>                 (result (join x xs))))))
>
> user=> ((string "hello") "hello world")
> (["hello" " world"])
>
> It's a straitforward implementation of the parser combinators
> described in the "Monadic Parser Combinators" papers by Hutton and
> Meijer.
>
> Thanks again!!!
>
> On Aug 26, 11:57 pm, Chouser <[EMAIL PROTECTED]> wrote:
>
> > On Sun, Aug 24, 2008 at 6:02 PM, budu <[EMAIL PROTECTED]> wrote:
>
> > > Well, for now only the value s used by the parser macro is really
> > > needed.
>
> > I think you misunderstood me, but I'm not too sure.  Anyway, here's an 
> > attempt:
>
> > (defn match-forms [p s]
> >   (if (= '_ p) []
> >     (loop [p p s s vars []]
> >       (cond
> >         (and (= 0 (count p)) (= 0 (count s))) vars
> >         (= 0 (count s)) nil
> >         :else (let [[fp & rp] p [fs & rs] s]
> >                 (cond
> >                   ;; wildcard pattern
> >                   (= '_ fp) (recur rp rs vars)
> >                   ;; rest
> >                   (and (symbol? fp) (= '& fp))
> >                   (conj vars (first rp) (conj rs fs))
> >                   ;; add variable to bindings
> >                   (symbol? fp) (recur rp rs (conj vars fp fs))
> >                   ;; match a symbol
> >                   (and (seq? fp) (= 'quote (first fp)))
> >                   (if (= fs (second fp)) (recur rp rs vars) nil)
> >                   ;; not matching
> >                   true nil))))))
>
> > (defmacro match [value & clauses]
> >   (when (and clauses (= 0 (rem (count clauses) 2)))
> >     (let [[c1 c2 & cr] clauses
> >           syms (take-nth 2 (match-forms c1 c1))]
> >       `(if-let m# (take-nth 2 (rest (match-forms '~c1 ~value)))
> >          (let [~(vec syms) (vec m#)]
> >            ~c2)
> >          (match ~value [EMAIL PROTECTED])))))
>
> > The trick here is I run match-forms once at compile time to get the
> > list of symbols that will need to be bound, even though I don't have
> > the real values yet.  I use that list to set up a "let" expression.
> > At runtime, I run match-forms again, but only take the values part and
> > drop that into the "let" that I set up at compile time.
>
> > That's probably not the best way to solve the problem, but it is *a*
> > way, and may help you find a more correct solution.  ...and since I
> > don't really understand what you're trying to do, I had only limited
> > tests that I could try, so I've probably introduced some new bugs.
> > But at the very least, your original examples now work:
>
> > user=> (match '(1 2 3) (a b c) (list c b a))
> > (3 2 1)
> > user=> (let [z 4] (match '(1 2 3) (a b c) (list z c b a)))
> > (4 3 2 1)
>
> > --Chouser
--~--~---------~--~----~------------~-------~--~----~
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 [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to