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

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

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

import parsley.Parsley, Parsley._
import parsley.XAnnotation.{implicitNotFound212, implicitNotFound213}
import parsley.lift.lift4
import parsley.syntax.zipped.Zipped2

/** This module contains specialist combinators for mixing unary and binary operators
  * on the same level. This is only sensible when mixing infix-left and postfix
  * or infix-right and prefix.
  *
  * @since 4.0.0
  * @group Chains
  * @note the behaviour of the enclosed combinators may be counter intuitive: they should
  *       not be used unless you know what you are doing.
  */
object mixed {
    /** This combinator handles right-associative parsing, and application of, '''zero''' or more binary operators ''and'' prefix unary operators between/before
      * '''one''' or more values.
      *
      * First parse `p`, then parse either a `uop` or an `op` followed by a `p`, repeatedly. The results of the `p`s, `x,,1,,` through `x,,n,,`, are combined
      * with the results of the `bop`s, `f,,1,,` through `f,,n-1,,`, and the `uop`s, `g,,1,1,,` through `g,,m,i,,`, with right-associative application:
      * `g,,1,1,,(g,,1,2,,(..g,,1,i,,(f,,1,,(x,,1,,, g,,2,1,,(..g,,2,j,,(f,,2,,(x,,2,,,..` . This
      * application is then returned as the result of the combinator. If `p`, `uop`, or `bop` fails having consumed input at any point, the
      * whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.mixed, parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class B(l: C, r: Expr) extends Expr
      * scala> case class U(c: Expr) extends Expr
      * scala> case class C(x: Char) extends Expr
      * scala> val p = mixed.right1(digit.map(C), char('-').as(U), char('+').as(B))
      * scala> p.parse("-1+--2+3")
      * val res0 = Success(U(B(C('1'), U(U(B(C('2'), C('3')))))))
      * }}}
      *
      * @tparam A the type of the values.
      * @tparam B the type returned by the operator, which must be a subtype of the result type `C`.
      * @tparam C the result type of the chain, which also fits into the recursive application site of the operators.
      * @param p the value to be parsed.
      * @param uop the operator before each binary application.
      * @param bop the operator between each value.
      * @param wrap a function that can convert the value type into the result type, ''this is provided automatically when `A <:< C`''.
      * @return a parser that parses alternating `p`, `uop`, and `bop`, ending in a `p` and applies their results right-associatively.
      * @note if you ''know'' this is what you are looking for, you have found it: otherwise, this is not what you are looking for.
      * @since 4.0.0
      */
    def right1[A, B, C >: B](p: Parsley[A], uop: Parsley[C => C], bop: =>Parsley[(A, C) => B])
            (implicit @implicitNotFound213("Please provide a wrapper function from ${A} to ${C}")
                      @implicitNotFound212("Please provide a wrapper function from A to C") wrap: A => C): Parsley[C] = {
        lazy val rest: Parsley[C] = (
                uop <*> rest
            <|> (p <**> ((bop, rest).zipped(flip(_, _) _) <|> pure(wrap)))
        )
        rest
    }

    /** This combinator handles left-associative parsing, and application of, '''zero''' or more binary operators ''and'' postfix unary operators between/before
      * '''one''' or more values.
      *
      * First parse `p`, then parse either a `uop` or an `op` followed by a `p`, repeatedly. The results of the `p`s, `x,,1,,` through `x,,n,,`, are combined
      * with the results of the `bop`s, `f,,1,,` through `f,,n-1,,`, and the `uop`s, `g,,1,1,,` through `g,,m,i,,`, with left-associative application:
      * `g,,m,i,,(g,,m,i-1,,(..g,,m,1,,(f,,n-1,,(..f,,1,,(g,,1,j,,(..g,,1,1,,(x,,1,,)..)), ..` . This
      * application is then returned as the result of the combinator. If `p`, `uop`, or `bop` fails having consumed input at any point, the
      * whole combinator fails.
      *
      * @example {{{
      * scala> import parsley.expr.mixed, parsley.character.{digit, char}
      * scala> sealed trait Expr
      * scala> case class B(l: Expr, r: C) extends Expr
      * scala> case class U(c: Expr) extends Expr
      * scala> case class C(x: Char) extends Expr
      * scala> val p = mixed.left1(digit.map(Constant), char('?').as(U), char('+').as(B))
      * scala> p.parse("1?+2+3??")
      * val res0 = Success(U(U(B(B(U(C('1')), C('2')), C('3')))))
      * }}}
      *
      * @tparam A the type of the values.
      * @tparam B the type returned by the operator, which must be a subtype of the result type `C`.
      * @tparam C the result type of the chain, which also fits into the recursive application site of the operators.
      * @param p the value to be parsed.
      * @param uop the operator before each binary application.
      * @param bop the operator between each value.
      * @param wrap a function that can convert the value type into the result type, ''this is provided automatically when `A <:< C`''.
      * @return a parser that parses alternating `p`, `uop`, and `bop`, ending in a `p` and applies their results left-associatively.
      * @note if you ''know'' this is what you are looking for, you have found it: otherwise, this is not what you are looking for.
      * @since 4.0.0
      */
    def left1[A, B, C >: B](p: Parsley[A], uop: =>Parsley[C => C], bop: =>Parsley[(C, A) => B])
            (implicit @implicitNotFound213("Please provide a wrapper function from ${A} to ${C}")
                      @implicitNotFound212("Please provide a wrapper function from A to C") wrap: A => C): Parsley[C] = {
        lazy val _uop = uop
        lazy val uops = _uop.foldLeft(identity[C] _)(_ compose _)
        lazy val rest: Parsley[C => C] = (
                lift4((b: (C, A) => B, y: A, u: C => C, r: C => C) => (x: C) => r(u(b(x, y))), bop, p, uops, rest)
            <|> pure(identity[C] _)
        )
        chain.postfix(p.map(wrap))(_uop) <**> rest
    }

    private def flip[A, B, C](f: (A, B) => C, y: B)(x: A) = f(x, y)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy