Skip to content

Commit 342f582

Browse files
committed
curried chains, closing #172
1 parent 3ba4964 commit 342f582

File tree

11 files changed

+91
-91
lines changed

11 files changed

+91
-91
lines changed

docs/api-guide/expr/chain.md

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ operators. As an example:
2424

2525
> <h3>`p op p op p op p op p`</h3>
2626
27-
The above can be parsed using `chain.left1(p, op)` to have the
27+
The above can be parsed using `chain.left1(p)(op)` to have the
2828
effect of parsing like:
2929

3030
> <h3>`(((p op p) op p) op p) op p`</h3>
3131
32-
It can also be parded using `chain.right1(p, op)` to have the
32+
It can also be parded using `chain.right1(p)(op)` to have the
3333
effect of parsing like:
3434

3535
> <h3>`p op (p op (p op (p op p)))`</h3>
@@ -56,7 +56,7 @@ Given input of shape:
5656

5757
> <h3>`p op op op op`</h3>
5858
59-
The combinator `postfix p op` will parse the input and apply
59+
The combinator `postfix(p)(op)` will parse the input and apply
6060
results such that it would look like:
6161

6262
> <h3>`(((p op) op) op) op`</h3>
@@ -65,7 +65,7 @@ Similarly, given input of shape:
6565

6666
> <h3>`op op op op p`</h3>
6767
68-
The combinator `prefix op p` will parse the input and apply
68+
The combinator `prefix(p)(op)` will parse the input and apply
6969
results such that it would look like:
7070

7171
> <h3>`op (op (op (op p)))`</h3>
@@ -84,7 +84,7 @@ or not. This is enforced by the type signature of the operations
8484
themselves:
8585

8686
```scala
87-
def prefix1[A, B <: A](op: Parsley[A => B], p: Parsley[A]): Parsley[B]
87+
def prefix1[A, B <: A](p: Parsley[A])(op: Parsley[A => B]): Parsley[B]
8888
```
8989

9090
Given that `B` is a subtype of `A`, it is not possible for the

docs/api-guide/expr/infix.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ sealed trait Expr
5252
case class Add(x: Expr, y: Num) extends Expr
5353
case class Num(n: Int) extends Expr
5454

55-
lazy val expr = infix.left1(num, "+".as(Add(_, _)))
55+
lazy val expr = infix.left1(num)("+".as(Add(_, _)))
5656
lazy val num = digit.foldLeft1(0)((n, d) => n * 10 + d.asDigit).map(Num(_))
5757
expr.parse("56+43+123")
5858
```
@@ -62,7 +62,7 @@ left, but `Num` must appear on the right. As `Num` and `Add`
6262
share a common supertype `Expr`, this is what the chains will return. To illustrate what happens if `right1` was used instead:
6363

6464
```scala mdoc:fail
65-
lazy val badExpr = infix.right1(num, "+".as(Add(_, _)))
65+
lazy val badExpr = infix.right1(num)("+".as(Add(_, _)))
6666
```
6767

6868
Notice that the error message here refers to the type `(A, C) => B`. In practice, to help reduce type ascriptions, the explicit `C >: B` is used as well -- in the working example, `C` is `Expr`, `A` is `Num`, and `B` is `Add`. The wrap is actually `A => C`, which in this case is provided by `Num <:< Expr`.

docs/api-guide/generic.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ like in [chain](expr/chain.md) or [precedence](expr/precedence.md) combinators:
131131
import parsley.expr.chain
132132
import parsley.syntax.character.stringLift
133133

134-
val term = chain.left1(px, Add.from("+")) // or `Add <# "+"`
134+
val term = chain.left1(px)(Add.from("+")) // or `Add <# "+"`
135135
```
136136

137137
They are analogous to the `as` and `#>` combinators respectively.

docs/tutorial/building-expression-parsers.md

+7-9
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,14 @@ val number = digit.foldLeft1[Int](0)((n, d) => n * 10 + d.asDigit)
125125
val add = (x: Int, y: Int) => x + y
126126
val sub = (x: Int, y: Int) => x - y
127127

128-
// chain.left1[A](p: Parsley[A], op: Parsley[(A, A) => A]): Parsley[A]
129-
lazy val expr: Parsley[Int] = chain.left1(term, '+'.as(add) | '-'.as(sub))
130-
lazy val term = chain.left1[Int](atom, '*' as (_ * _))
128+
// chain.left1[A](p: Parsley[A])(op: Parsley[(A, A) => A]): Parsley[A]
129+
lazy val expr: Parsley[Int] = chain.left1(term)('+'.as(add) | '-'.as(sub))
130+
lazy val term = chain.left1(atom)('*' as (_ * _))
131131
lazy val atom = '(' ~> expr <~ ')' | number
132132
```
133133

134134
The structure of the parser is roughly the same, however now you'll notice that `expr` and `term`
135135
are no longer self-recursive, and neither `term` nor `atom` need to be lazy (or have explicit types).
136-
Just to illustrate, if we provide the type argument to `chain.left1` we can continue to use
137-
`_ * _`, but without it, we need explicit type signatures: see `add` and `sub`.
138136

139137
@:todo(The first type parameter represents the type of the `atom`s, and the second is the type of the `term`
140138
itself: unlike mainstream parser combinator libraries in Haskell, Parsley allows these types to vary \(see
@@ -144,10 +142,10 @@ To make the relationship very clear between what we had before and what we have
144142
the transformation from recursive to `chains` follows these shape:
145143

146144
```scala
147-
self <**> (op <*> next) | next == chain.left1(next, op) // flipped op
148-
self <**> op <*> next | next == chain.left1(next, op) // normal op
149-
next <**> (op <*> self </> identity) == chain.right1(next, op) // no backtracking, flipped
150-
atomic(next <**> op <*> self) | next == chain.right1(next, op) // backtracking, normal op
145+
self <**> (op <*> next) | next == chain.left1(next)(op) // flipped op
146+
self <**> op <*> next | next == chain.left1(next)(op) // normal op
147+
next <**> (op <*> self </> identity) == chain.right1(next)(op) // no backtracking, flipped
148+
atomic(next <**> op <*> self) | next == chain.right1(next)(op) // backtracking, normal op
151149
```
152150

153151
In this parser, the nesting of the chains dictates the precedence order (again, terms are found _inside_

docs/tutorial/interlude-1-haskell.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ lazy val `<pat-naked>`: Parsley[PatNaked] =
475475
| PatTuple("(" ~> sepBy1(`<pat>`, ",") <~ ")")
476476
| PatList("[" ~> sepBy(`<pat>`, ",") <~ "]")
477477
)
478-
lazy val `<pat>` = infix.right1(`<pat-paren>`, PatCons from ":")
478+
lazy val `<pat>` = infix.right1(`<pat-paren>`)(PatCons from ":")
479479
lazy val `<pat-paren>` = atomic(`<pat-app>`) | `<pat-naked>`
480480
lazy val `<pat-app>` = PatApp(`<pat-con>`, some(`<pat-naked>`))
481481
lazy val `<pat-con>` = ( atomic("(" ~> (ConsCon from ":") <~ ")")
@@ -503,7 +503,7 @@ cases: there isn't much more to say until we try and deal with much more complex
503503
features.
504504

505505
```scala mdoc
506-
lazy val `<type>`: Parsley[Type] = infix.right1(`<type-app>`, FunTy from "->")
506+
lazy val `<type>`: Parsley[Type] = infix.right1(`<type-app>`)(FunTy from "->")
507507
lazy val `<type-app>` = `<type-atom>`.reduceLeft(TyApp)
508508
lazy val `<type-atom>` = ( `<type-con>` | `<var-id>` | (UnitTy from "()")
509509
| ListTy("[" ~> `<type>` <~ "]")

parsley/shared/src/main/scala/parsley/Parsley.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
668668
* @return a parser which parses this parser many times and folds the results together with `f` and `k` right-associatively.
669669
* @group fold
670670
*/
671-
def foldRight[B](k: B)(f: (A, B) => B): Parsley[B] = chain.prefix(this.map(f.curried), pure(k))
671+
def foldRight[B](k: B)(f: (A, B) => B): Parsley[B] = chain.prefix(pure(k))(this.map(f.curried))
672672
/** This combinator will parse this parser '''zero''' or more times combining the results with the function `f` and base value `k` from the left.
673673
*
674674
* This parser will continue to be parsed until it fails having '''not consumed''' input.
@@ -781,7 +781,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
781781
* @since 2.3.0
782782
* @group fold
783783
*/
784-
def reduceLeft[B >: A](op: (B, A) => B): Parsley[B] = infix.left1(this, pure(op))
784+
def reduceLeft[B >: A](op: (B, A) => B): Parsley[B] = infix.left1(this)(pure(op))
785785
/** This combinator will parse this parser '''zero''' or more times combining the results left-associatively with the function `op`.
786786
*
787787
* This parser will continue to be parsed until it fails having '''not consumed''' input.

parsley/shared/src/main/scala/parsley/expr/Fixity.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ sealed trait Fixity {
2525
*/
2626
case object InfixL extends Fixity {
2727
override type Op[-A, B] = (B, A) => B
28-
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.left1(p, op)
28+
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.left1(p)(op)
2929
}
3030

3131
/**
@@ -35,7 +35,7 @@ case object InfixL extends Fixity {
3535
*/
3636
case object InfixR extends Fixity {
3737
override type Op[-A, B] = (A, B) => B
38-
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.right1(p, op)
38+
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.right1(p)(op)
3939
}
4040

4141
/**
@@ -45,7 +45,7 @@ case object InfixR extends Fixity {
4545
*/
4646
case object Prefix extends Fixity {
4747
override type Op[A, B] = B => B
48-
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.prefix(op, p)
48+
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.prefix(p)(op)
4949
}
5050

5151
/**
@@ -55,7 +55,7 @@ case object Prefix extends Fixity {
5555
*/
5656
case object Postfix extends Fixity {
5757
override type Op[A, B] = B => B
58-
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.postfix(p, op)
58+
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.postfix(p)(op)
5959
}
6060

6161
/**
@@ -65,5 +65,5 @@ case object Postfix extends Fixity {
6565
*/
6666
case object InfixN extends Fixity {
6767
override type Op[-A, +B] = (A, A) => B
68-
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.nonassoc(p, op)
68+
private [expr] def chain[A, B](p: Parsley[A], op: Parsley[Op[A, B]])(implicit wrap: A => B): Parsley[B] = infix.nonassoc(p)(op)
6969
}

parsley/shared/src/main/scala/parsley/expr/chain.scala

+16-16
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ object chain {
3838
* scala> sealed trait Expr
3939
* scala> case class Add(x: Expr, y: Expr) extends Expr
4040
* scala> case class Num(x: Int) extends Expr
41-
* scala> val expr = chain.right1(digit.map(d => Num(d.asDigit)), char('+').as(Add))
41+
* scala> val expr = chain.right1(digit.map(d => Num(d.asDigit)))(char('+').as(Add))
4242
* scala> expr.parse("1+2+3+4")
4343
* val res0 = Success(Add(Num(1), Add(Num(2), Add(Num(3), Num(4)))))
4444
* scala> expr.parse("")
@@ -52,7 +52,7 @@ object chain {
5252
* @since 4.0.0
5353
* @group binary
5454
*/
55-
def right1[A](p: Parsley[A], op: =>Parsley[(A, A) => A]): Parsley[A] = infix.right1(p, op)
55+
def right1[A](p: Parsley[A])(op: =>Parsley[(A, A) => A]): Parsley[A] = infix.right1(p)(op)
5656

5757
/** This combinator handles left-associative parsing, and application of, '''zero''' or more binary operators between '''one''' or more values.
5858
*
@@ -66,7 +66,7 @@ object chain {
6666
* scala> sealed trait Expr
6767
* scala> case class Add(x: Expr, y: Expr) extends Expr
6868
* scala> case class Num(x: Int) extends Expr
69-
* scala> val expr = chain.left1(digit.map(d => Num(d.asDigit)), char('+').as(Add))
69+
* scala> val expr = chain.left1(digit.map(d => Num(d.asDigit)))(char('+').as(Add))
7070
* scala> expr.parse("1+2+3+4")
7171
* val res0 = Success(Add(Add(Add(Num(1), Num(2)), Num(3)), Num(4)))
7272
* scala> expr.parse("")
@@ -80,7 +80,7 @@ object chain {
8080
* @since 4.0.0
8181
* @group binary
8282
*/
83-
def left1[A](p: Parsley[A], op: =>Parsley[(A, A) => A]): Parsley[A] = infix.left1(p, op)
83+
def left1[A](p: Parsley[A])(op: =>Parsley[(A, A) => A]): Parsley[A] = infix.left1(p)(op)
8484

8585
/** This combinator handles right-associative parsing, and application of, '''zero''' or more binary operators between '''zero''' or more values.
8686
*
@@ -95,7 +95,7 @@ object chain {
9595
* scala> sealed trait Expr
9696
* scala> case class Add(x: Expr, y: Expr) extends Expr
9797
* scala> case class Num(x: Int) extends Expr
98-
* scala> val expr = chain.right(digit.map(d => Num(d.asDigit)), char('+').as(Add), Num(0))
98+
* scala> val expr = chain.right(digit.map(d => Num(d.asDigit)))(char('+').as(Add), Num(0))
9999
* scala> expr.parse("1+2+3+4")
100100
* val res0 = Success(Add(Num(1), Add(Num(2), Add(Num(3), Num(4)))))
101101
* scala> expr.parse("")
@@ -111,7 +111,7 @@ object chain {
111111
* @since 4.0.0
112112
* @group binary
113113
*/
114-
def right[A](p: Parsley[A], op: =>Parsley[(A, A) => A], x: A): Parsley[A] = infix.right(p, op, x) // TODO: right(x)(p, op)?
114+
def right[A](p: Parsley[A])(op: =>Parsley[(A, A) => A], x: A): Parsley[A] = infix.right(p)(op, x)
115115

116116
/** This combinator handles left-associative parsing, and application of, '''zero''' or more binary operators between '''zero''' or more values.
117117
*
@@ -126,7 +126,7 @@ object chain {
126126
* scala> sealed trait Expr
127127
* scala> case class Add(x: Expr, y: Expr) extends Expr
128128
* scala> case class Num(x: Int) extends Expr
129-
* scala> val expr = chain.left(digit.map(d => Num(d.asDigit)), char('+').as(Add), Num(0))
129+
* scala> val expr = chain.left(digit.map(d => Num(d.asDigit)))(char('+').as(Add), Num(0))
130130
* scala> expr.parse("1+2+3+4")
131131
* val res0 = Success(Add(Add(Add(Num(1), Num(2)), Num(3)), Num(4)))
132132
* scala> expr.parse("")
@@ -142,7 +142,7 @@ object chain {
142142
* @since 4.0.0
143143
* @group binary
144144
*/
145-
def left[A](p: Parsley[A], op: =>Parsley[(A, A) => A], x: A): Parsley[A] = infix.left(p, op, x) // TODO: left(x)(p, op)?
145+
def left[A](p: Parsley[A])(op: =>Parsley[(A, A) => A], x: A): Parsley[A] = infix.left(p)(op, x)
146146

147147
/** This combinator handles right-assocative parsing, and application of, '''zero''' or more prefix unary operators to a single value.
148148
*
@@ -158,7 +158,7 @@ object chain {
158158
* scala> case class Negate(x: Expr) extends Expr
159159
* scala> case class Id(x: Expr) extends Expr
160160
* scala> case class Num(x: Int) extends Expr
161-
* scala> val expr = chain.prefix(char('-').as(Negate) <|> char('+').as(Id), digit.map(d => Num(d.asDigit)))
161+
* scala> val expr = chain.prefix(digit.map(d => Num(d.asDigit))(char('-').as(Negate) <|> char('+').as(Id))
162162
* scala> expr.parse("--+1")
163163
* val res0 = Success(Negate(Negate(Id(Num(1)))))
164164
* scala> expr.parse("1")
@@ -173,7 +173,7 @@ object chain {
173173
* @since 2.2.0
174174
* @group unary
175175
*/
176-
def prefix[A](op: Parsley[A => A], p: Parsley[A]): Parsley[A] = new Parsley(new frontend.ChainPre(p.internal, op.internal))
176+
def prefix[A](p: Parsley[A])(op: Parsley[A => A]): Parsley[A] = new Parsley(new frontend.ChainPre(p.internal, op.internal))
177177

178178
/** This combinator handles left-assocative parsing, and application of, '''zero''' or more postfix unary operators to a single value.
179179
*
@@ -189,7 +189,7 @@ object chain {
189189
* scala> case class Inc(x: Expr) extends Expr
190190
* scala> case class Dec(x: Expr) extends Expr
191191
* scala> case class Num(x: Int) extends Expr
192-
* scala> val expr = chain.postfix(digit.map(d => Num(d.asDigit)), string("++").as(Inc) <|> string("--").as(Dec))
192+
* scala> val expr = chain.postfix(digit.map(d => Num(d.asDigit)))(string("++").as(Inc) <|> string("--").as(Dec))
193193
* scala> expr.parse("1++----")
194194
* val res0 = Success(Dec(Dec(Inc(Num(1)))))
195195
* scala> expr.parse("1")
@@ -204,7 +204,7 @@ object chain {
204204
* @since 2.2.0
205205
* @group unary
206206
*/
207-
def postfix[A](p: Parsley[A], op: =>Parsley[A => A]): Parsley[A] = new Parsley(new frontend.ChainPost(p.internal, op.internal))
207+
def postfix[A](p: Parsley[A])(op: =>Parsley[A => A]): Parsley[A] = new Parsley(new frontend.ChainPost(p.internal, op.internal))
208208

209209
/** This combinator handles right-assocative parsing, and application of, '''one''' or more prefix unary operators to a single value.
210210
*
@@ -220,7 +220,7 @@ object chain {
220220
* scala> case class Negate(x: Expr) extends Expr
221221
* scala> case class Id(x: Expr) extends Expr
222222
* scala> case class Num(x: Int) extends Expr
223-
* scala> val expr = chain.prefix1(char('-').as(Negate) <|> char('+').as(Id), digit.map(d => Num(d.asDigit)))
223+
* scala> val expr = chain.prefix1(digit.map(d => Num(d.asDigit)))(char('-').as(Negate) <|> char('+').as(Id))
224224
* scala> expr.parse("--+1")
225225
* val res0 = Success(Negate(Negate(Id(Num(1)))))
226226
* scala> expr.parse("1")
@@ -235,7 +235,7 @@ object chain {
235235
* @since 3.0.0
236236
* @group unary
237237
*/
238-
def prefix1[A, B <: A](op: Parsley[A => B], p: =>Parsley[A]): Parsley[B] = op <*> prefix(op, p)
238+
def prefix1[A, B <: A](p: =>Parsley[A])(op: Parsley[A => B]): Parsley[B] = op <*> prefix(p)(op)
239239

240240
/** This combinator handles left-assocative parsing, and application of, '''one''' or more postfix unary operators to a single value.
241241
*
@@ -266,8 +266,8 @@ object chain {
266266
* @since 3.0.0
267267
* @group unary
268268
*/
269-
def postfix1[A, B <: A](p: Parsley[A], op: =>Parsley[A => B]): Parsley[B] = {
269+
def postfix1[A, B <: A](p: Parsley[A])(op: =>Parsley[A => B]): Parsley[B] = {
270270
lazy val op_ = op
271-
postfix(p <**> op_, op_)
271+
postfix(p <**> op_)(op_)
272272
}
273273
}

0 commit comments

Comments
 (0)