parsley.combinator.scala Maven / Gradle / Ivy
/* SPDX-FileCopyrightText: © 2021 Parsley Contributors
* SPDX-License-Identifier: BSD-3-Clause
*/
package parsley
import scala.annotation.tailrec
import parsley.Parsley.{attempt, empty, notFollowedBy, pure, select, unit}
import parsley.implicits.zipped.{Zipped2, Zipped3}
import parsley.internal.deepembedding.{frontend, singletons}
/** This module contains a huge number of pre-made combinators that are very useful for a variety of purposes.
*
* In particular, it contains combinators for: performing a parser iteratively, collecting all the results;
* querying whether or not any input is left; optionally performing parsers; parsing delimited constructions;
* handling multiple possible alternatives or parsers to sequence; handling more complex conditional execution; and more.
* @since 2.2.0
*
* @groupprio iter 0
* @groupname iter Iterative Combinators
* @groupdesc iter
* These combinators all execute a given parser an unbounded number of times, until either it fails, or another
* parser succeeds, depending on the combinator. Depending on the combinator, all of the results produced by the
* repeated execution of the parser may be returned in a `List`. These are almost essential for any practical parsing
* task.
*
* @groupprio item 10
* @groupname item Input Query Combinators
* @groupdesc item
* These combinators do not consume input, but they allow for querying of the input stream - specifically checking
* whether or not there is more input that can be consumed or not. In particular, most parsers should be making
* use of `eof` to ensure that the parser consumes all the input available at the end of the parse.
*
* @groupprio opt 20
* @groupname opt Optional Parsing Combinators
* @groupdesc opt
* These combinators allow for the ''possible'' parsing of some parser. If the parser succeeds, that is ok
* so long as it '''did not consume input'''. Be aware that the result of the success may be replaced with
* these combinators, with the exception of [[option `option`]], which still preserves the result.
*
* @groupprio sep 25
* @groupname sep Separated Values Combinators
* @groupdesc sep
* These combinators are concerned with delimited parsing, where one parser is repeated but delimited by another one.
* In each of these cases `p` is the parser of interest and `sep` is the delimeter. These combinators mainly differ
* in either the number of `p`s they require, or exactly where the delimeters are allowed (only between, always
* trailing, or either). In all cases, they return the list of results generated by the repeated parses of `p`.
*
* @groupprio multi 50
* @groupname multi Multiple Branching/Sequencing Combinators
* @groupdesc multi
* These combinators allow for testing or sequencing a large number of parsers in one go. Be careful, however, these are
* variadic combinators and are necessarily (for compatibility with Scala 2) '''not lazy'''.
*
* In such a case where laziness is desired without resorting to the other lazier combinators, there
* is a neat trick: unroll the first iteration of the combinator, and use the corresponding regular combinator
* to do that (i.e. `<::>` or `*>`): since these will have a lazy
* right-hand side, the remaining variadic arguments will be kept lazily suspended until later. Alternatively,
* it is possible to use the [[parsley.Parsley.LazyParsley.unary_~ prefix `~`]] combinator to make any individual
* arguments lazy as required, for example `skip(p, ~q, r)`.
*
* @groupprio cond 75
* @groupname cond Conditional Combinators
* @groupdesc cond
* These combinators allow for the conditional extraction of a result, or the execution of a parser
* based on another. They are morally related to [[Parsley.branch `branch`]] and [[Parsley.select `select`]] but are
* less fundamental.
*
* @groupprio misc 100
* @groupname misc Miscellaneous
*
* @define strict be aware that all of the arguments to this combinator are in '''strict''' positions.
*/
object combinator {
/** This combinator tries to parse each of the parsers `ps` in order, until one of them succeeds.
*
* Finds the first parser in `ps` which succeeds, returning its result. If none of the parsers
* succeed, then this combinator fails. If a parser fails having consumed input, this combinator
* fails '''immediately'''.
*
* @example {{{
* scala> import parsley.combinator.choice
* scala> import parsley.character.string
* scala> val p = choice(string("abc"), string("ab"), string("bc"), string("d"))
* scala> p.parse("abc")
* val res0 = Success("abc")
* scala> p.parse("ab")
* val res1 = Failure(..)
* scala> p.parse("bc")
* val res2 = Success("bc")
* scala> p.parse("x")
* val res3 = Failure(..)
* }}}
*
* @param ps the parsers to try, in order.
* @return a parser that tries to parse one of `ps`.
* @group multi
* @see [[parsley.Parsley.<|> `<|>`]]
*/
def choice[A](ps: Parsley[A]*): Parsley[A] = ps.reduceRightOption(_ <|> _).getOrElse(empty)
/** This combinator tries to parse each of the parsers `ps` in order, until one of them succeeds.
*
* Finds the first parser in `ps` which succeeds, returning its result. If none of the parsers
* succeed, then this combinator fails. This combinator will always try and parse each of the
* combinators until one succeeds, regardless of how they fail. The last argument will '''not'''
* be wrapped in `attempt`, as this is not necessary.
*
* @example {{{
* scala> import parsley.combinator.attemptChoice
* scala> import parsley.character.string
* scala> val p = attemptChoice(string("abc"), string("ab"), string("bc"), string("d"))
* scala> p.parse("abc")
* val res0 = Success("abc")
* scala> p.parse("ab")
* val res1 = Success("ab")
* scala> p.parse("bc")
* val res2 = Success("bc")
* scala> p.parse("x")
* val res3 = Failure(..)
* }}}
*
* @param ps the parsers to try, in order.
* @return a parser that tries to parse one of `ps`.
* @group multi
* @see [[parsley.Parsley.<|> `<|>`]]
* @see [[parsley.Parsley$.attempt `attempt`]]
* @note this combinator is not particularly efficient, because it may unnecessarily backtrack for each alternative.
*/
def attemptChoice[A](ps: Parsley[A]*): Parsley[A] = ps.reduceRightOption((p, q) => attempt(p) <|> q).getOrElse(empty)
/** This combinator will parse each of `ps` in order, collecting the results.
*
* Given the parsers `ps`, consisting of `p,,1,,` through `p,,n,,`, parses
* each in order. If they all succeed, producing the results `x,,1,,` through `x,,n,,`,
* then `List(x,,1,,, .., x,,n,,)` is returned. If any of the parsers fail, then
* the whole combinator fails.
*
* @example {{{
* scala> import parsley.combinator.sequence
* scala> import parsley.character.{char, item}
* scala> val p = sequence(char('a'), item, char('c'))
* scala> p.parse("abc")
* val res0 = Success(List('a', 'b', 'c'))
* scala> p.parse("ab")
* val res1 = Failure(..)
* }}}
*
* @param ps parsers to be sequenced.
* @return a parser that parses each of `ps`, returning the results in a list
* @group multi
* @since 4.0.0
* @see [[parsley.Parsley.<::> `<::>`]]
* @note $strict
*/
def sequence[A](ps: Parsley[A]*): Parsley[List[A]] = ps match {
// these cases are going to be slightly more efficient as lift3 and below have efficient implementations
case Seq(p) => p.map(List(_))
case Seq(p, q) => (p, q).zipped(List(_, _))
case Seq(p, q, r) => (p, q, r).zipped(List(_, _, _))
case _ => ps.foldRight(pure(List.empty[A]))(_ <::> _)
}
/** This combinator will parse each of the parsers generated by applying `f` to `xs`, in order, collecting the results.
*
* Given the values `xs`, consisting of `x,,1,,` through `x,,n,,`, first creates
* the parses `f(x,,1,,)` through `f(x,,n,,)` and then called `sequence` on them.
*
* @example {{{
* // this is an OK implementation for `string`, which is common in Haskell.
* def string(str: String) = {
* traverse(char, str: _*).map(_.mkString)
* }
* }}}
*
* @param f the function used to generate parsers for each values
* @param xs the values to turn into parsers and sequence.
* @return a parser that sequences the parsers generated from applying `f` to each of `xs`.
* @group multi
* @since 4.0.0
* @see [[sequence `sequence`]]
* @note $strict
*/
def traverse[A, B](f: A => Parsley[B], xs: A*): Parsley[List[B]] = sequence(xs.map(f): _*)
/** This combinator will parse each of `ps` in order, discarding the results.
*
* Given the parsers `ps`, consisting of `p,,1,,` through `p,,n,,`, parses
* each in order. If they all succeed, this combinator succeeds. If any of
* the parsers fail, then the whole combinator fails.
*
* @example {{{
* scala> import parsley.combinator.skip
* scala> import parsley.character.{char, item}
* scala> val p = skip(char('a'), item, char('c'))
* scala> p.parse("abc")
* val res0 = Success(())
* scala> p.parse("ab")
* val res1 = Failure(..)
* }}}
*
* @param p first parser to be sequenced
* @param ps parsers to be sequenced.
* @return a parser that parses each of `ps`, returning `()`.
* @group multi
* @see [[parsley.Parsley.*> `*>`]]
* @note $strict
*/
def skip(p: Parsley[_], ps: Parsley[_]*): Parsley[Unit] = ps.foldLeft(p.void)(_ <* _)
/** This combinator parses exactly `n` occurrences of `p`, returning these `n` results in a list.
*
* Parses `p` repeatedly up to `n` times. If `p` fails before `n` is reached, then this combinator
* fails. It is not required for `p` to fail after the `n`^th^ parse. The results produced by
* `p`, `x,,1,,` through `x,,n,,`, are returned as `List(x,,1,,, .., x,,n,,)`.
*
* @example {{{
* scala> import parsley.character.item
* scala> import parsley.combinator.exactly
* scala> val p = exactly(3, item)
* scala> p.parse("ab")
* val res0 = Failure(..)
* scala> p.parse("abc")
* val res1 = Success(List('a', 'b', 'c'))
* scala> p.parse("abcd")
* val res2 = Success(List('a', 'b', 'c'))
* }}}
*
* @param n the number of times to repeat `p`.
* @param p the parser to repeat.
* @return a parser that parses `p` exactly `n` times, returning a list of the results.
* @group misc
* @since 4.0.0
*/
def exactly[A](n: Int, p: Parsley[A]): Parsley[List[A]] = traverse[Int, A](_ => p, (1 to n): _*)
/** This combinator tries to parse `p`, wrapping its result in a `Some` if it succeeds, or returns `None` if it fails.
*
* Tries to parse `p`. If `p` succeeded, producing `x`, then `Some(x)` is returned. Otherwise, if `p` failed
* '''without consuming input''', then `None` is returned instead.
*
* @example {{{
* scala> import parsley.combinator.option
* scala> import parsley.character.string
* scala> val p = option(string("abc"))
* scala> p.parse("")
* val res0 = Success(None)
* scala> p.parse("abc")
* val res1 = Success(Some("abc"))
* scala> p.parse("ab")
* val res2 = Failure(..)
* }}}
*
* @param p the parser to try to parse.
* @return a parser that tries to parse `p`, but can still succeed with `None` if that was not possible.
* @group opt
*/
def option[A](p: Parsley[A]): Parsley[Option[A]] = p.map(Some(_)).getOrElse(None)
/** This combinator will parse `p` if possible, otherwise will do nothing.
*
* Tries to parse `p`. If `p` succeeds, or fails '''without consuming input''' then this combinator is successful. Otherwise, if `p` failed
* having consumed input, this combinator fails.
*
* @example {{{
* scala> import parsley.combinator.optional
* scala> import parsley.character.string
* scala> val p = optional(string("abc"))
* scala> p.parse("")
* val res0 = Success(())
* scala> p.parse("abc")
* val res1 = Success(())
* scala> p.parse("ab")
* val res2 = Failure(..)
* }}}
*
* @param p the parser to try to parse.
* @return a parser that tries to parse `p`.
* @note equivalent to `optionalAs(p, ())`.
* @group opt
*/
def optional(p: Parsley[_]): Parsley[Unit] = optionalAs(p, ())
/** This combinator will parse `p` if possible, otherwise will do nothing.
*
* Tries to parse `p`. If `p` succeeds, or fails '''without consuming input''' then this combinator is successful and returns `x`. Otherwise,
* if `p` failed having consumed input, this combinator fails.
*
* @example {{{
* scala> import parsley.combinator.optionalAs
* scala> import parsley.character.string
* scala> val p = optionalAs(string("abc"), 7)
* scala> p.parse("")
* val res0 = Success(7)
* scala> p.parse("abc")
* val res1 = Success(7)
* scala> p.parse("ab")
* val res2 = Failure(..)
* }}}
*
* @param p the parser to try to parse.
* @param x the value to return regardless of how `p` performs.
* @return a parser that tries to parse `p`, returning `x` regardless of success or failure.
* @group opt
*/
def optionalAs[A](p: Parsley[_], x: A): Parsley[A] = {
(p #> x).getOrElse(x)
}
/** This combinator can eliminate an `Option` from the result of the parser `p`.
*
* First parse `p`, if it succeeds returning `Some(x)`, then return `x`. However,
* if `p` fails, or returned `None`, then this combinator fails.
*
* @example {{{
* decide(option(p)) = p
* }}}
*
* @param p the parser to parse and extract the result from.
* @return a parser that tries to extract the result from `p`.
* @group cond
*/
def decide[A](p: Parsley[Option[A]]): Parsley[A] = p.collect {
case Some(x) => x
}
/** This combinator parses `q` depending only if `p` returns a `None`.
*
* First parses `p`. If `p` returned `Some(x)`, then `x` is returned.
* Otherwise, if `p` returned `None` then `q` is parsed, producing `y`,
* and `y` is returned. If `p` or `q` fails, the combinator fails.
*
* @example {{{
* decide(option(p), q) = p <|> q
* }}}
*
* @param p the first parser, which returns an `Option` to eliminate.
* @param q a parser to execute when `p` returns `None`, to provide a value of type `A`.
* @return a parser that either just parses `p` or both `p` and `q` in order to return an `A`.
* @group cond
*/
def decide[A](p: Parsley[Option[A]], q: =>Parsley[A]): Parsley[A] = select(p.map(_.toRight(())), q.map(x => (_: Unit) => x))
/** This combinator parses `open`, followed by `p`, and then `close`.
*
* First parse `open`, ignore its result, then parse, `p`, producing `x`. Finally, parse `close`, ignoring its result.
* If `open`, `p`, and `close` all succeeded, then return `x`. If any of them failed, this combinator fails.
*
* @example {{{
* def braces[A](p: Parsley[A]) = between(char('{'), char('}'), p)
* }}}
*
* @param open the first parser to parse.
* @param close the last parser to parse.
* @param p the parser to parse between the other two.
* @return a parser that reads `open`, then `p`, then `close` and returns the result of `p`.
* @group misc
*/
def between[A](open: Parsley[_], close: =>Parsley[_], p: =>Parsley[A]): Parsley[A] = open *> p <* close
/** This combinator repeatedly parses a given parser '''zero''' or more times, collecting the results into a list.
*
* Parses a given parser, `p`, repeatedly until it fails. If `p` failed having consumed input,
* this combinator fails. Otherwise when `p` fails '''without consuming input''', this combinator
* will return all of the results, `x,,1,,` through `x,,n,,` (with `n >= 0`), in a list: `List(x,,1,,, .., x,,n,,)`.
* If `p` was never successful, the empty list is returned.
*
* @example {{{
* scala> import parsley.character.string
* scala> import parsley.combinator.many
* scala> val p = many(string("ab"))
* scala> p.parse("")
* val res0 = Success(Nil)
* scala> p.parse("ab")
* val res1 = Success(List("ab"))
* scala> p.parse("abababab")
* val res2 = Success(List("ab", "ab", "ab", "ab"))
* scala> p.parse("aba")
* val res3 = Failure(..)
* }}}
*
* @param p the parser to execute multiple times.
* @return a parser that parses `p` until it fails, returning the list of all the successful results.
* @since 2.2.0
* @group iter
*/
def many[A](p: Parsley[A]): Parsley[List[A]] = new Parsley(new frontend.Many(p.internal))
/** This combinator repeatedly parses a given parser '''one''' or more times, collecting the results into a list.
*
* Parses a given parser, `p`, repeatedly until it fails. If `p` failed having consumed input,
* this combinator fails. Otherwise when `p` fails '''without consuming input''', this combinator
* will return all of the results, `x,,1,,` through `x,,n,,` (with `n >= 1`), in a list: `List(x,,1,,, .., x,,n,,)`.
* If `p` was not successful at least one time, this combinator fails.
*
* @example {{{
* scala> import parsley.character.string
* scala> import parsley.combinator.some
* scala> val p = some(string("ab"))
* scala> p.parse("")
* val res0 = Failure(..)
* scala> p.parse("ab")
* val res1 = Success(List("ab"))
* scala> p.parse("abababab")
* val res2 = Success(List("ab", "ab", "ab", "ab"))
* scala> p.parse("aba")
* val res3 = Failure(..)
* }}}
*
* @param p the parser to execute multiple times.
* @return a parser that parses `p` until it fails, returning the list of all the successful results.
* @group iter
*/
def some[A](p: Parsley[A]): Parsley[List[A]] = manyN(1, p)
/** This combinator repeatedly parses a given parser '''`n`''' or more times, collecting the results into a list.
*
* Parses a given parser, `p`, repeatedly until it fails. If `p` failed having consumed input,
* this combinator fails. Otherwise when `p` fails '''without consuming input''', this combinator
* will return all of the results, `x,,1,,` through `x,,m,,` (with `m >= n`), in a list: `List(x,,1,,, .., x,,m,,)`.
* If `p` was not successful at least `n` times, this combinator fails.
*
* @example {{{
* scala> import parsley.character.string
* scala> import parsley.combinator.manyN
* scala> val p = manyN(2, string("ab"))
* scala> p.parse("")
* val res0 = Failure(..)
* scala> p.parse("ab")
* val res1 = Failure(..)
* scala> p.parse("abababab")
* val res2 = Success(List("ab", "ab", "ab", "ab"))
* scala> p.parse("aba")
* val res3 = Failure(..)
* }}}
*
* @param n the minimum number of `p`s required.
* @param p the parser to execute multiple times.
* @return a parser that parses `p` until it fails, returning the list of all the successful results.
* @note `many(p) == many(0, p)` and `some(p) == many(1, p)`.
* @group iter
*/
def manyN[A](n: Int, p: Parsley[A]): Parsley[List[A]] = {
@tailrec def go(n: Int, acc: Parsley[List[A]] = many(p)): Parsley[List[A]] = {
if (n == 0) acc
else go(n-1, p <::> acc)
}
go(n)
}
/** This combinator repeatedly parses a given parser '''zero''' or more times, ignoring the results.
*
* Parses a given parser, `p`, repeatedly until it fails. If `p` failed having consumed input,
* this combinator fails. Otherwise when `p` fails '''without consuming input''', this combinator
* will succeed.
*
* @example {{{
* scala> import parsley.character.string
* scala> import parsley.combinator.skipMany
* scala> val p = skipMany(string("ab"))
* scala> p.parse("")
* val res0 = Success(())
* scala> p.parse("ab")
* val res1 = Success(())
* scala> p.parse("abababab")
* val res2 = Success(())
* scala> p.parse("aba")
* val res3 = Failure(..)
* }}}
*
* @param p the parser to execute multiple times.
* @return a parser that parses `p` until it fails, returning unit.
* @since 2.2.0
* @group iter
*/
def skipMany(p: Parsley[_]): Parsley[Unit] = new Parsley(new frontend.SkipMany(p.internal))
/** This combinator repeatedly parses a given parser '''one''' or more times, ignoring the results.
*
* Parses a given parser, `p`, repeatedly until it fails. If `p` failed having consumed input,
* this combinator fails. Otherwise when `p` fails '''without consuming input''', this combinator
* will succeed. The parser `p` must succeed at least once.
*
* @example {{{
* scala> import parsley.character.string
* scala> import parsley.combinator.skipSome
* scala> val p = skipSome(string("ab"))
* scala> p.parse("")
* val res0 = Failure(..)
* scala> p.parse("ab")
* val res1 = Success(())
* scala> p.parse("abababab")
* val res2 = Success(())
* scala> p.parse("aba")
* val res3 = Failure(..)
* }}}
*
* @param p the parser to execute multiple times.
* @return a parser that parses `p` until it fails, returning unit.
* @group iter
*/
def skipSome(p: Parsley[_]): Parsley[Unit] = skipManyN(1, p)
/** This combinator repeatedly parses a given parser '''`n`''' or more times, ignoring the results.
*
* Parses a given parser, `p`, repeatedly until it fails. If `p` failed having consumed input,
* this combinator fails. Otherwise when `p` fails '''without consuming input''', this combinator
* will succeed. The parser `p` must succeed at least `n` times.
*
* @example {{{
* scala> import parsley.character.string
* scala> import parsley.combinator.skipManyN
* scala> val p = skipManyN(2, string("ab"))
* scala> p.parse("")
* val res0 = Failure(..)
* scala> p.parse("ab")
* val res1 = Failure(..)
* scala> p.parse("abababab")
* val res2 = Success(())
* scala> p.parse("aba")
* val res3 = Failure(..)
* }}}
*
* @param p the parser to execute multiple times.
* @return a parser that parses `p` until it fails, returning unit.
* @group iter
*/
def skipManyN(n: Int, p: Parsley[_]): Parsley[Unit] = {
@tailrec def go(n: Int, acc: Parsley[Unit] = skipMany(p)): Parsley[Unit] = {
if (n == 0) acc
else go(n-1, p *> acc)
}
go(n)
}
/** This combinator parses '''zero''' or more occurrences of `p`, separated by `sep`.
*
* Behaves just like `sepBy1`, except does not require an initial `p`, returning the empty list instead.
*
* @example {{{
* scala> ...
* scala> val args = sepBy(int, string(", "))
* scala> args.parse("7, 3, 2")
* val res0 = Success(List(7, 3, 2))
* scala> args.parse("")
* val res1 = Success(Nil)
* scala> args.parse("1")
* val res2 = Success(List(1))
* scala> args.parse("1, 2, ")
* val res3 = Failure(..) // no trailing comma allowed
* }}}
*
* @param p the parser whose results are collected into a list.
* @param sep the delimiter that must be parsed between every `p`.
* @return a parser that parses `p` delimited by `sep`, returning the list of `p`'s results.
* @group sep
*/
def sepBy[A](p: Parsley[A], sep: =>Parsley[_]): Parsley[List[A]] = sepBy1(p, sep).getOrElse(Nil)
/** This combinator parses '''one''' or more occurrences of `p`, separated by `sep`.
*
* First parses a `p`. Then parses `sep` followed by `p` until there are no more `sep`s.
* The results of the `p`'s, `x,,1,,` through `x,,n,,`, are returned as `List(x,,1,,, .., x,,n,,)`.
* If `p` or `sep` fails having consumed input, the whole parser fails. Requires at least
* one `p` to have been parsed.
*
* @example {{{
* scala> ...
* scala> val args = sepBy1(int, string(", "))
* scala> args.parse("7, 3, 2")
* val res0 = Success(List(7, 3, 2))
* scala> args.parse("")
* val res1 = Failure(..)
* scala> args.parse("1")
* val res2 = Success(List(1))
* scala> args.parse("1, 2, ")
* val res3 = Failure(..) // no trailing comma allowed
* }}}
*
* @param p the parser whose results are collected into a list.
* @param sep the delimiter that must be parsed between every `p`.
* @return a parser that parses `p` delimited by `sep`, returning the list of `p`'s results.
* @group sep
*/
def sepBy1[A](p: Parsley[A], sep: =>Parsley[_]): Parsley[List[A]] = {
p <::> many(sep *> p)
}
/** This combinator parses '''zero''' or more occurrences of `p`, separated and optionally ended by `sep`.
*
* Behaves just like `sepEndBy1`, except does not require an initial `p`, returning the empty list instead.
*
* @example {{{
* scala> ...
* scala> val args = sepEndBy(int, string(";\n"))
* scala> args.parse("7;\n3;\n2")
* val res0 = Success(List(7, 3, 2))
* scala> args.parse("")
* val res1 = Success(Nil)
* scala> args.parse("1")
* val res2 = Success(List(1))
* scala> args.parse("1;\n2;\n")
* val res3 = Success(List(1, 2))
* }}}
*
* @param p the parser whose results are collected into a list.
* @param sep the delimiter that must be parsed between every `p`.
* @return a parser that parses `p` delimited by `sep`, returning the list of `p`'s results.
* @group sep
*/
def sepEndBy[A](p: Parsley[A], sep: =>Parsley[_]): Parsley[List[A]] = sepEndBy1(p, sep).getOrElse(Nil)
/** This combinator parses '''one''' or more occurrences of `p`, separated and optionally ended by `sep`.
*
* First parses a `p`. Then parses `sep` followed by `p` until there are no more: if a final `sep` exists, this is parsed.
* The results of the `p`'s, `x,,1,,` through `x,,n,,`, are returned as `List(x,,1,,, .., x,,n,,)`.
* If `p` or `sep` fails having consumed input, the whole parser fails. Requires at least
* one `p` to have been parsed.
*
* @example {{{
* scala> ...
* scala> val args = sepEndBy1(int, string(";\n"))
* scala> args.parse("7;\n3;\n2")
* val res0 = Success(List(7, 3, 2))
* scala> args.parse("")
* val res1 = Failure(..)
* scala> args.parse("1")
* val res2 = Success(List(1))
* scala> args.parse("1;\n2;\n")
* val res3 = Success(List(1, 2))
* }}}
*
* @param p the parser whose results are collected into a list.
* @param sep the delimiter that must be parsed between every `p`.
* @return a parser that parses `p` delimited by `sep`, returning the list of `p`'s results.
* @group sep
*/
def sepEndBy1[A](p: Parsley[A], sep: =>Parsley[_]): Parsley[List[A]] = new Parsley(new frontend.SepEndBy1(p.internal, sep.internal))
/** This combinator parses '''zero''' or more occurrences of `p`, separated and ended by `sep`.
*
* Behaves just like `endBy1`, except does not require an initial `p` and `sep`, returning the empty list instead.
*
* @example {{{
* scala> ...
* scala> val args = endBy(int, string(";\n"))
* scala> args.parse("7;\n3;\n2")
* val res0 = Failure(..)
* scala> args.parse("")
* val res1 = Success(Nil)
* scala> args.parse("1;\n")
* val res2 = Success(List(1))
* scala> args.parse("1;\n2;\n")
* val res3 = Success(List(1, 2))
* }}}
*
* @param p the parser whose results are collected into a list.
* @param sep the delimiter that must be parsed between every `p`.
* @return a parser that parses `p` delimited by `sep`, returning the list of `p`'s results.
* @group sep
*/
def endBy[A](p: Parsley[A], sep: =>Parsley[_]): Parsley[List[A]] = many(p <* sep)
/** This combinator parses '''one''' or more occurrences of `p`, separated and ended by `sep`.
*
* Parses `p` followed by `sep` one or more times.
* The results of the `p`'s, `x,,1,,` through `x,,n,,`, are returned as `List(x,,1,,, .., x,,n,,)`.
* If `p` or `sep` fails having consumed input, the whole parser fails.
*
* @example {{{
* scala> ...
* scala> val args = endBy1(int, string(";\n"))
* scala> args.parse("7;\n3;\n2")
* val res0 = Failure(..)
* scala> args.parse("")
* val res1 = Failure(..)
* scala> args.parse("1;\n")
* val res2 = Success(List(1))
* scala> args.parse("1;\n2;\n")
* val res3 = Success(List(1, 2))
* }}}
*
* @param p the parser whose results are collected into a list.
* @param sep the delimiter that must be parsed between every `p`.
* @return a parser that parses `p` delimited by `sep`, returning the list of `p`'s results.
* @group sep
*/
def endBy1[A](p: Parsley[A], sep: =>Parsley[_]): Parsley[List[A]] = some(p <* sep)
/** This parser only succeeds at the end of the input.
*
* Equivalent to `notFollowedBy(item)`.
*
* @example {{{
* scala> import parsley.combinator.eof
* scala> eof.parse("a")
* val res0 = Failure(..)
* scala> eof.parse("")
* val res1 = Success(())
* }}}
*
* @group item
*/
val eof: Parsley[Unit] = new Parsley(singletons.Eof)
/** This parser only succeeds if there is still more input.
*
* Equivalent to `lookAhead(item).void`.
*
* @example {{{
* scala> import parsley.combinator.more
* scala> more.parse("")
* val res0 = Failure(..)
* scala> more.parse("a")
* val res1 = Success(())
* }}}
*
* @group item
*/
val more: Parsley[Unit] = notFollowedBy(eof)
/** This combinator repeatedly parses a given parser '''zero''' or more times, until the `end` parser succeeds, collecting the results into a list.
*
* First tries to parse `end`, if it fails '''without consuming input''', then parses `p`, which must succeed. This repeats until `end` succeeds.
* When `end` does succeed, this combinator will return all of the results generated by `p`, `x,,1,,` through `x,,n,,` (with `n >= 0`), in a
* list: `List(x,,1,,, .., x,,n,,)`. If `end` could be parsed immediately, the empty list is returned.
*
* @example This can be useful for scanning comments: {{{
* scala> import parsley.character.{string, item, endOfLine}
* scala> import parsley.combinator.many
* scala> val comment = string("//") *> manyUntil(item, endOfLine)
* scala> p.parse("//hello world")
* val res0 = Failure(..)
* scala> p.parse("//hello world\n")
* val res1 = Success(List('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'))
* scala> p.parse("//\n")
* val res2 = Success(Nil)
* }}}
*
* @param p the parser to execute multiple times.
* @param end the parser that stops the parsing of `p`.
* @return a parser that parses `p` until `end` succeeds, returning the list of all the successful results.
* @group iter
*/
def manyUntil[A](p: Parsley[A], end: Parsley[_]): Parsley[List[A]] = {
new Parsley(new frontend.ManyUntil((end #> ManyUntil.Stop <|> p).internal))
}
// TODO: document and test before release
private [parsley] def skipManyUntil(p: Parsley[_], end: Parsley[_]): Parsley[Unit] = {
new Parsley(new frontend.SkipManyUntil((end #> ManyUntil.Stop <|> p).internal))
}
private [parsley] object ManyUntil {
object Stop
}
/** This combinator repeatedly parses a given parser '''one''' or more times, until the `end` parser succeeds, collecting the results into a list.
*
* First ensures that trying to parse `end` fails, then tries to parse `p`. If it succeed then it will repeatedly: try to parse `end`, if it fails
* '''without consuming input''', then parses `p`, which must succeed. When `end` does succeed, this combinator will return all of the results
* generated by `p`, `x,,1,,` through `x,,n,,` (with `n >= 1`), in a list: `List(x,,1,,, .., x,,n,,)`. The parser `p` must succeed at least once
* before `end` succeeds.
*
* @example This can be useful for scanning comments: {{{
* scala> import parsley.character.{string, item, endOfLine}
* scala> import parsley.combinator.many
* scala> val comment = string("//") *> someUntil(item, endOfLine)
* scala> p.parse("//hello world")
* val res0 = Failure(..)
* scala> p.parse("//hello world\n")
* val res1 = Success(List('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'))
* scala> p.parse("//\n")
* val res2 = Failure(..)
* scala> p.parse("//a\n")
* val res3 = Success(List('a'))
* }}}
*
* @param p the parser to execute multiple times.
* @param end the parser that stops the parsing of `p`.
* @return a parser that parses `p` until `end` succeeds, returning the list of all the successful results.
* @group iter
*/
def someUntil[A](p: Parsley[A], end: Parsley[_]): Parsley[List[A]] = {
notFollowedBy(end) *> (p <::> manyUntil(p, end))
}
// TODO: document and test before release
private [parsley] def skipSomeUntil(p: Parsley[_], end: Parsley[_]): Parsley[Unit] = {
notFollowedBy(end) *> (p *> skipManyUntil(p, end))
}
/** This combinator parses one of `thenP` or `elseP` depending on the result of parsing `condP`.
*
* This is a lifted `if`-statement. First, parse `condP`: if it is successful and returns
* `true`, then parse `thenP`; else, if it returned `false`, parse `elseP`; or, if `condP` failed
* then fail. If either of `thenP` or `elseP` fail, then this combinator also fails.
*
* Most useful in conjunction with ''Registers'', as this allows for decisions to be made
* based on state.
*
* @example {{{
* ifP(pure(true), p, _) == p
* ifP(pure(false), _, p) == p
* }}}
*
* @param condP the parser that yields the condition value.
* @param thenP the parser to execute if the condition is `true`.
* @param elseP the parser to execute if the condition is `false.
* @return a parser that conditionally parses `thenP` or `elseP` after `condP`.
* @group cond
* @since 4.0.0
*/
def ifP[A](condP: Parsley[Boolean], thenP: =>Parsley[A], elseP: =>Parsley[A]): Parsley[A] = {
new Parsley(new frontend.If(condP.internal, thenP.internal, elseP.internal))
}
/** This combinator conditionally parses `thenP` depending on the result of parsing `condP`.
*
* This is a lifted `if`-statement. First, parse `condP`: if it is successful and returns
* `true`, then parse `thenP`; else, if it returned `false` do nothing; or, if `condP` failed
* then fail. If `thenP` fails, then this combinator also fails.
*
* Most useful in conjunction with ''Registers'', as this allows for decisions to be made
* based on state.
*
* @example {{{
* when(pure(true), p) == p
* when(pure(false), _) == unit
* }}}
*
* @param condP the parser that yields the condition value.
* @param thenP the parser to execute if the condition is `true`.
* @return a parser that conditionally parses `thenP` or `elseP` after `condP`.
* @group cond
*/
def when(condP: Parsley[Boolean], thenP: =>Parsley[Unit]): Parsley[Unit] = ifP(condP, thenP, unit)
// TODO: document and test before release
private [parsley] def ensure[A](condP: Parsley[Boolean], beforeP: =>Parsley[A]): Parsley[A] =
//ifP(condP, beforeP, empty)
condP.filter(identity) *> beforeP
/** This combinator repeatedly parses `p` so long as it returns `true`.
*
* This is a lifted `while`-loop. First, parse `p`: if it is successful and
* returns `true`, then repeat; else if it returned `false` stop; or, if it
* failed then this combinator fails.
*
* Most useful in conjunction with ''Registers'', as this allows for decisions to be made
* based on state. In particular, this can be used to define the `forP` combinator.
*
* @example {{{
* def forP[A](init: Parsley[A], cond: =>Parsley[A => Boolean], step: =>Parsley[A => A])(body: =>Parsley[_]): Parsley[Unit] = {
* val reg = Reg.make[A]
* lazy val _cond = reg.gets(cond)
* lazy val _step = reg.modify(step)
* reg.put(init) *> when(_cond, whileP(body *> _step *> _cond))
* }
* }}}
*
* @param p the parser to repeatedly parse.
* @return a parser that continues to parse `p` until it returns `false`.
* @group cond
*/
def whileP(p: Parsley[Boolean]): Parsley[Unit] = {
lazy val whilePP: Parsley[Unit] = when(p, whilePP)
whilePP
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy