All Downloads are FREE. Search and download functionalities are using the official Maven repository.

parsley.combinator.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 Parsley Contributors 
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
package parsley

import scala.annotation.tailrec
import scala.collection.{Factory, mutable}

import parsley.Parsley.{atomic, empty, fresh, many, notFollowedBy, pure, secretSome, select, some, unit}
import parsley.state.{RefMaker, StateCombinators, forP}
import parsley.syntax.zipped.Zipped2

import parsley.internal.deepembedding.frontend

/** 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 range 65
  * @groupname range Range Combinators
  * @groupdesc range
  *     These combinators allow for the parsing of a specific parser either a specific number of times, or between a certain
  *     amount of times.
  *
  * @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.
  *
  * @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 is still used in internal testing, but is a trap for new users
    // it will not be exposed in the API again.
    private [parsley] def atomicChoice[A](ps: Parsley[A]*): Parsley[A] = ps.reduceRightOption((p, q) => atomic(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 p0 the first parser to be sequenced
      * @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](p0: Parsley[A], ps: Parsley[A]*): Parsley[List[A]] = sequence[A, List[A]](List, p0, ps: _*)
    private [parsley] def sequence[A, C](factory: Factory[A, C], p0: Parsley[A], ps: Parsley[A]*): Parsley[C] = {
        @tailrec def go(acc: Parsley[mutable.Builder[A, C]], ps: List[Parsley[A]]): Parsley[C] = ps match {
            case Nil => acc.map(_.result())
            case p::ps => go((acc, p).zipped(_ += _), ps)
        }
        go(p0.map(factory.newBuilder += _), ps.toList)
    }

    /** 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(str:  _*)(char).span
      * }
      * }}}
      *
      * @param f the function used to generate parsers for each values
      * @param x0 the first value to turn into a parser and sequence.
      * @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](x0: A, xs: A*)(f: A => Parsley[B]): Parsley[List[B]] = traverseGen(x0, xs: _*)(f, List)
    private [parsley] def traverseGen[A, B, C](x0: A, xs: A*)(f: A => Parsley[B], factory: Factory[B, C]): Parsley[C] = {
        sequence(factory, f(x0), xs.map(f): _*)
    }

    /** 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(_))  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.as(x)  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 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]] = manyN(n, p, List)
    private [parsley] def manyN[A, C](n: Int, p: Parsley[A], factory: Factory[A, C]): Parsley[C] = {
        require(n >= 0, "cannot pass negative integer to `manyN`")
        fresh(factory.newBuilder).persist { acc =>
            forP[Int](pure(0), pure(_ < n), pure(_ + 1)) {
                (acc, p).zipped(_ += _).impure // we don't want this optimised out, it's a mutable operation in a resultless context
            } ~> secretSome(acc, p)
        }
    }

    /** This combinator repeatedly parses a given parser '''zero''' or more times, returning how many times it succeeded.
      *
      * 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 number of times `p` succeeded is returned as the result.
      *
      * @example {{{
      * scala> import parsley.character.string
      * scala> import parsley.combinator.countMany
      * scala> val p = countMany(string("ab"))
      * scala> p.parse("")
      * val res0 = Success(0)
      * scala> p.parse("ab")
      * val res1 = Success(1)
      * scala> p.parse("abababab")
      * val res2 = Success(4)
      * scala> p.parse("aba")
      * val res3 = Failure(..)
      * }}}
      *
      * @param p the parser to execute multiple times.
      * @return the number of times `p` successfully parses
      * @group iter
      * @since 4.5.0
      */
    def countMany(p: Parsley[_]): Parsley[Int] = p.foldLeft(0)((n, _) => n + 1)

    /** This combinator repeatedly parses a given parser '''one''' or more times, returning how many times it succeeded.
      *
      * 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. The number of times `p` succeeded is returned as the result.
      *
      * @example {{{
      * scala> import parsley.character.string
      * scala> import parsley.combinator.countSome
      * scala> val p = countSome(string("ab"))
      * scala> p.parse("")
      * val res0 = Failure(..)
      * scala> p.parse("ab")
      * val res1 = Success(1)
      * scala> p.parse("abababab")
      * val res2 = Success(4)
      * scala> p.parse("aba")
      * val res3 = Failure(..)
      * }}}
      *
      * @param p the parser to execute multiple times.
      * @return the number of times `p` successfully parses
      * @group iter
      * @since 4.5.0
      */
    def countSome(p: Parsley[_]): Parsley[Int] = p.foldLeft1(0)((n, _) => n + 1)

    /** 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)  Nil
    private [parsley] def sepBy[A, C](p: Parsley[A], sep: =>Parsley[_], factory: Factory[A, C]): Parsley[C] = {
        sepBy1(p, sep, factory) <|> fresh(factory.newBuilder.result())
    }

    /** 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)
    private [parsley] def sepBy1[A, C](p: Parsley[A], sep: =>Parsley[_], factory: Factory[A, C]): Parsley[C] = {
        secretSome(p, sep *> p, factory)
    }

    /** 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]] = sepEndBy(p, sep, List)
    private [parsley] def sepEndBy[A, C](p: Parsley[A], sep: =>Parsley[_], factory: Factory[A, C]): Parsley[C] = {
        sepEndBy1(p, sep, factory) <|> fresh(factory.newBuilder.result())
    }

    /** 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]] = sepEndBy1(p, sep, List)
    private [parsley] def sepEndBy1[A, C](p: Parsley[A], sep: =>Parsley[_], factory: Factory[A, C]): Parsley[C] = {
        new Parsley(new frontend.SepEndBy1(p.internal, sep.internal, factory))
    }

    /** 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]] = endBy(p, sep, List)
    private [parsley] def endBy[A, C](p: Parsley[A], sep: =>Parsley[_], factory: Factory[A, C]): Parsley[C] = {
        many(p <* sep, factory)
    }

    /** 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)
    private [parsley] def endBy1[A, C](p: Parsley[A], sep: =>Parsley[_], factory: Factory[A, C]): Parsley[C] = {
        some(p <* sep, factory)
    }

    /** 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.manyTill
      * scala> val comment = string("//") *> manyTill(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
      * @since 4.5.0
      */
    def manyTill[A](p: Parsley[A], end: Parsley[_]): Parsley[List[A]] = manyTill(p, end, List)
    private [parsley] def manyTill[A, C](p: Parsley[A], end: Parsley[_], factory: Factory[A, C]): Parsley[C] = {
        new Parsley(new frontend.ManyUntil((end.as(ManyUntil.Stop) <|> p: Parsley[Any]).internal, factory))
    }

    // TODO: find a way to make this redundant
    private [parsley] def skipManyUntil(p: Parsley[_], end: Parsley[_]): Parsley[Unit] = {
        new Parsley(new frontend.SkipManyUntil((end.as(ManyUntil.Stop) <|> p.void: Parsley[Any]).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.someUntil
      * 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
      * @since 4.5.0
      */
    def someTill[A](p: Parsley[A], end: Parsley[_]): Parsley[List[A]] = {
        // similar to some, I think a someTill(init, p, end) could generalise both manyTill and someTill
        notFollowedBy(end) *> (p <::> manyTill(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 {{{
      * ifS(pure(true), p, _) == p
      * ifS(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.5.0
      */
    def ifS[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 {{{
      * whenS(pure(true), p) == p
      * whenS(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` after `condP`.
      * @group cond
      */
    def whenS(condP: Parsley[Boolean])(thenP: =>Parsley[Unit]): Parsley[Unit] = ifS(condP, thenP, unit)

    /** This combinator verfies that the given parser returns `true`, or else fails.
      *
      * First, parse `p`; if it succeeds then, so long at returns `true`, this `guard(p)` succeeds. Otherwise,
      * if `p` either fails, or returns `false`, `guard(p)` will fail.
      *
      * @example {{{
      * guard(pure(true)) == unit
      * guard(pure(false)) == empty
      * when(p.map(!_), empty) == guardS(p)
      * }}}
      *
      * @param p the parser that yields the condition value.
      * @group cond
      */
    def guardS(p: Parsley[Boolean]): Parsley[Unit] = ifS(p, unit, empty)

    /** 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) *> whenS(_cond, whileS(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 whileS(p: Parsley[Boolean]): Parsley[Unit] = {
        lazy val whileP: Parsley[Unit] = whenS(p)(whileP)
        whileP
    }

    /** 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 range
      * @since 4.0.0
      */
    def exactly[A](n: Int, p: Parsley[A]): Parsley[List[A]] = exactly(n, p, List)
    private [parsley] def exactly[A, C](n: Int, p: Parsley[A], factory: Factory[A, C]): Parsley[C] = {
        require(n > 0, "n must be greater than 0 for exactly")
        traverseGen(0, (1 until n): _*)(_ => p, factory)
    }

    /** This combinator parses between `min` and `max` occurrences of `p`, returning these `n` results in a list.
      *
      * Parses `p` repeatedly a minimum of `min` times and up to `max` times both inclusive. If `p` fails before
      * `min` is reached, then this combinator fails. It is not required for `p` to fail after the `max`^th^ parse. The results produced by
      * `p`, `x,,min,,` through `x,,max,,`, are returned as `List(x,,min,,, .., x,,max,,)`.
      *
      * @example {{{
      * scala> import parsley.character.item
      * scala> import parsley.combinator.range
      * scala> val p = range(min=3, max=5)(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', 'd'))
      * scala> p.parse("abcde")
      * val res2 = Success(List('a', 'b', 'c', 'd', 'e'))
      * scala> p.parse("abcdef")
      * val res2 = Success(List('a', 'b', 'c', 'd', 'e'))
      * }}}
      *
      * @param min the minimum number of times to repeat `p`, inclusive.
      * @param max the maximum number of times to repeat `p`, inclusive.
      * @param p the parser to repeat.
      * @return the results of the successful parses of `p`.
      * @group range
      * @since 4.4.0
      */
    def range[A](min: Int, max: Int)(p: Parsley[A]): Parsley[List[A]] = fresh(mutable.ListBuffer.empty[A]).persist { xs =>
        count(min, max)((xs, p).zipped(_ += _).impure) ~>
        xs.map(_.toList)
    }

    /** This combinator parses between `min` and `max` occurrences of `p`, returning the number of successes.
      *
      * Parses `p` repeatedly a minimum of `min` times and up to `max` times both inclusive. If `p` fails before
      * `min` is reached, then this combinator fails. It is not required for `p` to fail after the `max`^th^ parse.
      * The results are discarded and the number of successful parses of `p`, `n`, is returned instead, such that
      * `min <= n <= max`.
      *
      * @example {{{
      * scala> import parsley.character.item
      * scala> import parsley.combinator.count
      * scala> val p = count(min=3, max=5)(item)
      * scala> p.parse("ab")
      * val res0 = Failure(..)
      * scala> p.parse("abc")
      * val res1 = Success(3)
      * scala> p.parse("abcd")
      * val res2 = Success(4)
      * scala> p.parse("abcde")
      * val res2 = Success(5)
      * scala> p.parse("abcdef")
      * val res2 = Success(5)
      * }}}
      *
      * @param min the minimum number of times to repeat `p`, inclusive.
      * @param max the maximum number of times to repeat `p`, inclusive.
      * @param p the parser to repeat.
      * @return the number of times `p` parsed successfully.
      * @group range
      * @since 4.4.0
      */
    def count(min: Int, max: Int)(p: Parsley[_]): Parsley[Int] = min.makeRef { i =>
        exactly(min, p) ~>
        Parsley.many(guardS(i.gets(_ < max)) ~> p ~> i.update(_ + 1)) ~>
        i.get
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy