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

parsley.expr.chain.scala Maven / Gradle / Ivy

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

import parsley.Parsley

import parsley.internal.deepembedding.frontend

/** This module contains the very useful chaining family of combinators,
  * which are mostly used to parse operators and expressions of varying fixities.
  * It is a more low-level API compared with [[precedence]].
  * @since 2.2.0
  * @group Chains
  *
  * @groupprio binary 0
  * @groupname binary Binary Operator Chains
  * @groupdesc binary
  *     These chains allow for the chaining together of values and binary operators in either left- or right-associative application.
  *
  * @groupprio unary 0
  * @groupname unary Unary Operator Chains
  * @groupdesc unary
  *     These chains allow for the chaining together and application of multiple prefix or postfix unary operators to a single value.
  */
object chain {
    /** This combinator handles right-associative parsing, and application of, '''zero''' or more binary operators between '''one''' or more values.
      *
      * First parse `p`, then parse `op` followed by a `p` repeatedly. The results of the `p`s, `x,,1,,` through `x,,n,,`, are combined with the results
      * of the `op`s, `f,,1,,` through `f,,n-1,,`, with right-associative application: `f,,1,,(x,,1,,, f,,2,,(x,,2,,, ..f,,n-1,,(x,,n-1,,, x,,n,,)..))`. This
      * application is then returned as the result of the combinator. If `p` or `op` fails having consumed input at any point, the whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class Add(x: Expr, y: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.right1(digit.map(d => Num(d.asDigit)))(char('+').as(Add))
      * scala> expr.parse("1+2+3+4")
      * val res0 = Success(Add(Num(1), Add(Num(2), Add(Num(3), Num(4)))))
      * scala> expr.parse("")
      * val res1 = Failure(..)
      * }}}
      *
      * @param p the value to be parsed.
      * @param op the operator between each value.
      * @return a parser that parses alternating `p` and `op`, ending in a `p` and applies their results right-associatively.
      * @see [[infix.right1 `infix.right1`]] for a version where the types can vary, ensuring that the associativity is enforced in a type-safe way.
      * @since 4.0.0
      * @group binary
      */
    def right1[A](p: Parsley[A])(op: =>Parsley[(A, A) => A]): Parsley[A] = infix.right1(p)(op)

    /** This combinator handles left-associative parsing, and application of, '''zero''' or more binary operators between '''one''' or more values.
      *
      * First parse `p`, then parse `op` followed by a `p` repeatedly. The results of the `p`s, `x,,1,,` through `x,,n,,`, are combined with the results
      * of the `op`s, `f,,1,,` through `f,,n-1,,`, with left-associative application: `f,,n-1,,(f,,n-2,,(..f,,1,,(x,,1,,, x,,2,,).., x,,n-1,,), x,,n,,)`. This
      * application is then returned as the result of the combinator. If `p` or `op` fails having consumed input at any point, the whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class Add(x: Expr, y: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.left1(digit.map(d => Num(d.asDigit)))(char('+').as(Add))
      * scala> expr.parse("1+2+3+4")
      * val res0 = Success(Add(Add(Add(Num(1), Num(2)), Num(3)), Num(4)))
      * scala> expr.parse("")
      * val res1 = Failure(..)
      * }}}
      *
      * @param p the value to be parsed.
      * @param op the operator between each value.
      * @return a parser that parses alternating `p` and `op`, ending in a `p` and applies their results left-associatively.
      * @see [[infix.left1 `infix.left1`]] for a version where the types can vary, ensuring that the associativity is enforced in a type-safe way.
      * @since 4.0.0
      * @group binary
      */
    def left1[A](p: Parsley[A])(op: =>Parsley[(A, A) => A]): Parsley[A] = infix.left1(p)(op)

    /** This combinator handles right-associative parsing, and application of, '''zero''' or more binary operators between '''zero''' or more values.
      *
      * First parse `p`, then parse `op` followed by a `p` repeatedly. The results of the `p`s, `x,,1,,` through `x,,n,,`, are combined with the results
      * of the `op`s, `f,,1,,` through `f,,n-1,,`, with right-associative application: `f,,1,,(x,,1,,, f,,2,,(x,,2,,, ..f,,n-1,,(x,,n-1,,, x,,n,,)..))`. This
      * application is then returned as the result of the combinator. If `p` or `op` fails having consumed input at any point, the whole combinator fails.
      * If no `p` could be parsed, this combinator will return a default result `x`.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class Add(x: Expr, y: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.right(digit.map(d => Num(d.asDigit)))(char('+').as(Add), Num(0))
      * scala> expr.parse("1+2+3+4")
      * val res0 = Success(Add(Num(1), Add(Num(2), Add(Num(3), Num(4)))))
      * scala> expr.parse("")
      * val res1 = Success(Num(0))
      * }}}
      *
      * @param p the value to be parsed.
      * @param op the operator between each value.
      * @param x the default value to return if no `p`s can be parsed.
      * @return a parser that parses alternating `p` and `op`, ending in a `p` and applies their results right-associatively or
      *         returns `x` if no `p` was parsed.
      * @see [[infix.right `infix.right`]] for a version where the types can vary, ensuring that the associativity is enforced in a type-safe way.
      * @since 4.0.0
      * @group binary
      */
    def right[A](p: Parsley[A])(op: =>Parsley[(A, A) => A], x: A): Parsley[A] = infix.right(p)(op, x)

    /** This combinator handles left-associative parsing, and application of, '''zero''' or more binary operators between '''zero''' or more values.
      *
      * First parse `p`, then parse `op` followed by a `p` repeatedly. The results of the `p`s, `x,,1,,` through `x,,n,,`, are combined with the results
      * of the `op`s, `f,,1,,` through `f,,n-1,,`, with left-associative application: `f,,n-1,,(f,,n-2,,(..f,,1,,(x,,1,,, x,,2,,).., x,,n-1,,), x,,n,,)`. This
      * application is then returned as the result of the combinator. If `p` or `op` fails having consumed input at any point, the whole combinator fails.
      * If no `p` could be parsed, this combinator will return a default result `x`.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class Add(x: Expr, y: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.left(digit.map(d => Num(d.asDigit)))(char('+').as(Add), Num(0))
      * scala> expr.parse("1+2+3+4")
      * val res0 = Success(Add(Add(Add(Num(1), Num(2)), Num(3)), Num(4)))
      * scala> expr.parse("")
      * val res1 = Success(Num(0))
      * }}}
      *
      * @param p the value to be parsed.
      * @param op the operator between each value.
      * @param x the default value to return if no `p`s can be parsed.
      * @return a parser that parses alternating `p` and `op`, ending in a `p` and applies their results left-associatively or
      *         returns `x` if no `p` was parsed.
      * @see [[infix.left `infix.left`]] for a version where the types can vary, ensuring that the associativity is enforced in a type-safe way.
      * @since 4.0.0
      * @group binary
      */
    def left[A](p: Parsley[A])(op: =>Parsley[(A, A) => A], x: A): Parsley[A] = infix.left(p)(op, x)

    /** This combinator handles right-assocative parsing, and application of, '''zero''' or more prefix unary operators to a single value.
      *
      * First parse many repeated `op`s. When there are no more `op`s left to parse, parse a single `p`. The result of `p`, `x`, is
      * applied to each of the results of the `op`s, `f,,1,,` through `f,,n,,`, such that `f,,n,,` is applied first and `f,,1,,` last:
      * `f,,1,,(f,,2,,(..f,,n,,(x)..))`. This application is then returned as the result of the combinator. If `p` or `op` fails having
      * consumed input at any point, the whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class Negate(x: Expr) extends Expr
      * scala> case class Id(x: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.prefix(digit.map(d => Num(d.asDigit))(char('-').as(Negate) <|> char('+').as(Id))
      * scala> expr.parse("--+1")
      * val res0 = Success(Negate(Negate(Id(Num(1)))))
      * scala> expr.parse("1")
      * val res1 = Success(Num(1))
      * scala> expr.parse("")
      * val res2 = Failure(..)
      * }}}
      *
      * @param op the prefix operator to repeatedly parse before `p`.
      * @param p the single value to be parsed.
      * @return a parser that parses many `op`s, and a final `p`, and applies all of the results right-associatively.
      * @since 2.2.0
      * @group unary
      */
    def prefix[A](p: Parsley[A])(op: Parsley[A => A]): Parsley[A] = new Parsley(new frontend.ChainPre(p.internal, op.internal))

    /** This combinator handles left-assocative parsing, and application of, '''zero''' or more postfix unary operators to a single value.
      *
      * First parse a single `p`. Then, parse many repeated `op`s. The result of `p`, `x`, is
      * applied to each of the results of the `op`s, `f,,1,,` through `f,,n,,`, such that `f,,1,,` is applied first and `f,,n,,` last:
      * `f,,n,,(f,,n-1,,(..f,,1,,(x)..))`. This application is then returned as the result of the combinator. If `p` or `op` fails having
      * consumed input at any point, the whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, string}
      * scala> sealed trait Expr
      * scala> case class Inc(x: Expr) extends Expr
      * scala> case class Dec(x: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.postfix(digit.map(d => Num(d.asDigit)))(string("++").as(Inc) <|> string("--").as(Dec))
      * scala> expr.parse("1++----")
      * val res0 = Success(Dec(Dec(Inc(Num(1)))))
      * scala> expr.parse("1")
      * val res1 = Success(Num(1))
      * scala> expr.parse("")
      * val res2 = Failure(..)
      * }}}
      *
      * @param p the single value to be parsed.
      * @param op the postfix operator to repeatedly parser after `p`.
      * @return a parser that an initial `p`, then many `op`s, and applies all of the results left-associatively.
      * @since 2.2.0
      * @group unary
      */
    def postfix[A](p: Parsley[A])(op: =>Parsley[A => A]): Parsley[A] = new Parsley(new frontend.ChainPost(p.internal, op.internal))

    /** This combinator handles right-assocative parsing, and application of, '''one''' or more prefix unary operators to a single value.
      *
      * First parse at least one repeated `op`s. When there are no more `op`s left to parse, parse a single `p`. The result of `p`, `x`, is
      * applied to each of the results of the `op`s, `f,,1,,` through `f,,n,,`, such that `f,,n,,` is applied first and `f,,1,,` last:
      * `f,,1,,(f,,2,,(..f,,n,,(x)..))`. This application is then returned as the result of the combinator. If `p` or `op` fails having
      * consumed input at any point, the whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class Negate(x: Expr) extends Expr
      * scala> case class Id(x: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.prefix1(digit.map(d => Num(d.asDigit)))(char('-').as(Negate) <|> char('+').as(Id))
      * scala> expr.parse("--+1")
      * val res0 = Success(Negate(Negate(Id(Num(1)))))
      * scala> expr.parse("1")
      * val res1 = Failure(..)
      * scala> expr.parse("")
      * val res2 = Failure(..)
      * }}}
      *
      * @param op the prefix operator to repeatedly parse before `p`.
      * @param p the single value to be parsed.
      * @return a parser that parses some `op`s, and a final `p`, and applies all of the results right-associatively.
      * @since 3.0.0
      * @group unary
      */
    def prefix1[A, B <: A](p: =>Parsley[A])(op: Parsley[A => B]): Parsley[B] = op <*> prefix(p)(op)

    /** This combinator handles left-assocative parsing, and application of, '''one''' or more postfix unary operators to a single value.
      *
      * First parse a single `p`. Then, parse at least one repeated `op`s. The result of `p`, `x`, is
      * applied to each of the results of the `op`s, `f,,1,,` through `f,,n,,`, such that `f,,1,,` is applied first and `f,,n,,` last:
      * `f,,n,,(f,,n-1,,(..f,,1,,(x)..))`. This application is then returned as the result of the combinator. If `p` or `op` fails having
      * consumed input at any point, the whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.chain
      * scala> import parsley.character.{digit, string}
      * scala> sealed trait Expr
      * scala> case class Inc(x: Expr) extends Expr
      * scala> case class Dec(x: Expr) extends Expr
      * scala> case class Num(x: Int) extends Expr
      * scala> val expr = chain.postfix1(digit.map(d => Num(d.asDigit)), string("++").as(Inc) <|> string("--").as(Dec))
      * scala> expr.parse("1++----")
      * val res0 = Success(Dec(Dec(Inc(Num(1)))))
      * scala> expr.parse("1")
      * val res1 = Failure(..)
      * scala> expr.parse("")
      * val res2 = Failure(..)
      * }}}
      *
      * @param p the single value to be parsed.
      * @param op the postfix operator to repeatedly parser after `p`.
      * @return a parser that an initial `p`, then some `op`s, and applies all of the results left-associatively.
      * @since 3.0.0
      * @group unary
      */
    def postfix1[A, B <: A](p: Parsley[A])(op: =>Parsley[A => B]): Parsley[B] = {
        lazy val op_ = op
        postfix(p <**> op_)(op_)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy