2.2 Parsing sequences

Using or/p, it is possible to choose between alternatives when parsing, but what if a particular grammar permits any number of elements in sequence? For that, you can use the many/p combinator. It accepts a parser and attempts to parse it over and over again until it fails. For example, here is a parser that parses any number of occurrences of the letter a:

> (parse-string (many/p (char/p #\a)) "")

(success '())

> (parse-string (many/p (char/p #\a)) "a")

(success '(#\a))

> (parse-string (many/p (char/p #\a)) "aaaa")

(success '(#\a #\a #\a #\a))

This allows creating grammars that parse arbitrary numbers of values. The many/p combinator accepts an optional keyword argument #:min to specify a minimum number of values to parse. This can be used to parse two integers separated by some amount of whitespace, for example.

> (define two-integers/p
    (do [x <- integer/p]
        (many/p space/p #:min 1)
        [y <- integer/p]
        (pure (list x y))))
> (parse-string two-integers/p "13     102")

(success '(13 102))

Perhaps even more frequently, though, you may want to parse some number of values separated by some delimiter. For example, perhaps you want to parse a whole list of integers separated by commas. That can be accomplished by passing a parser for the #:sep argument to many/p.

> (define many-integers/p
    (many/p integer/p #:sep (char/p #\,)))
> (parse-string many-integers/p "1,2,3,5,8,13")

(success '(1 2 3 5 8 13))

Often an unbounded number of values is undesirable: some limit is desired. The #:max argument to many/p allows specifying a max number of values to parse. For example, we may not wish to allow more than five comma-separated integers.

> (define at-most-five-integers/p
    (many/p integer/p #:sep (char/p #\,) #:max 5))
> (parse-string at-most-five-integers/p "1,2,3")

(success '(1 2 3))

> (parse-string at-most-five-integers/p "1,2,3,5,8,13")

(success '(1 2 3 5 8))