Skip to content

Clarify when defparser is needed? #11

sjl opened this Issue Sep 13, 2012 · 3 comments

2 participants

sjl commented Sep 13, 2012

So I'm a bit confused as to when exactly I need to use defparser instead of a plain defn.

All the standard parsers I usually build up from like choice and char are plain old functions that return parsers. In fact, defparser isn't actually used in the core parsatron code at all.

For example, this works:

(def number (many1 (digit)))
(run number "100")

This also works, of course:

(defn number []
    (many1 (digit)))

(run (number) "100")

If I look at the bencode example and change defparser to defn it still works for every one except ben-value. If I change ben-value to use defn it blows the stack. Why is that? As far as I can tell:

(defn ben-value []
  (choice (ben-integer)

Should be a function that returns a parser (made with choice). Why does this explode? There's probably something subtle going on here that I'm not quite grasping.

youngnh commented Sep 14, 2012

The biggest reason for the existence of defparser in my mind was so users could omit a single unnecessary >> by wrapping the body of your parser in it for you. The reason that defn still works in a lot of situations is that the function is the fundamental abstraction that the rest of the library is built on. There have been other facilities mixed into defparser though, and when a parser created with defn doesn't work like a similar one created with defparser, then you're running into defn's lack of those facilities.

For example, you are encouraged to write:

(defparser foo
  (char \f)
  (char \o)
  (char \o))

because using defn above will not behave like most would expect it to.

When the stack-saving trampoline was put in, defparser took care to properly trampoline recursive calls (in the bencode example, ben-list and ben-dictionary are defined in terms of ben-value, so every call to ben-value results in another frame staying on the stack).

In the future, I'd like parsers to be mutually recursive without having to make forward declarations. That's counter to how Clojure works, so that won't be possible using the language's built-in facilities. If/when something like that is implemented, defn will probably still function to create simple parsers, but they won't be able to reference others that haven't already been declared or defined while defparser forms will.

sjl commented Sep 20, 2012

So basically the gist is to always use defparser because it'll ensure that things are wrapped correctly?

youngnh commented Sep 21, 2012

I'd say I've intended that defparser is to defn as (def (fn ... is to defn in plain Clojure.
Its a bit of an implementation detail that parsers are Clojure fns that take 5 arguments. If you know that detail, you can probably use defn to create parsers and if you take care to define your function in the right way, that parser will probably even behave exactly like other parsers created with defparser.

@youngnh youngnh closed this May 30, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.