Skip to content

Commit

Permalink
Added some small transparency/opaque annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mie6 committed Apr 24, 2024
1 parent d6ad26a commit d765fcc
Show file tree
Hide file tree
Showing 29 changed files with 172 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ private [parsley] final class Named[A](_p: LazyParsley[A], val name: String) ext
XAssert.assert(!p.isInstanceOf[Named[_]], "Named parsers should not be nested within each other directly.")
def make(p: StrictParsley[A]): StrictParsley[A] = p
override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visitUnknown(this, context)
override private [parsley] def debugName = name
override private [parsley] var debugName = name
}

private [parsley] object Named {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private [parsley] final class TaggedWith[A](strat: DebugStrategy)(val origin: La
override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visitUnknown(this, context)
// $COVERAGE-ON$

override private [parsley] def debugName = userAssignedName.getOrElse(origin.debugName)
override private [parsley] var debugName = userAssignedName.getOrElse(origin.debugName)
}

private [parsley] object TaggedWith {
Expand Down Expand Up @@ -137,7 +137,7 @@ private [parsley] object TaggedWith {

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[B] = visitor.visitGeneric(this, context)

override private[parsley] def debugName = self.debugName
override private [parsley] var debugName = self.debugName
}
}
}
Expand All @@ -151,7 +151,7 @@ private [parsley] object TaggedWith {

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[C] = visitor.visitGeneric(this, context)

override private [parsley] def debugName = self.debugName
override private [parsley] var debugName = self.debugName
}
}
}
Expand All @@ -167,7 +167,7 @@ private [parsley] object TaggedWith {

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[D] = visitor.visitGeneric(this, context)

override private[parsley] def debugName = self.debugName
override private [parsley] var debugName = self.debugName
}
}
}
Expand All @@ -190,7 +190,7 @@ private [parsley] object TaggedWith {
}

override def visit[A](self: <|>[A], context: ParserTracker)(p: LazyParsley[A], q: LazyParsley[A]): DL[A] = handle2Ary(self, context)(p, q) { (p, q) =>
new <|>(p.get, q.get)
new <|>(p.get, q.get, self.debugName)
}

override def visit[A](self: ChainPre[A], context: ParserTracker)(p: LazyParsley[A], op: =>LazyParsley[A => A]): DL[A] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ private class DummyParser extends LazyParsley[Any] {
override protected def findLetsAux[M[_, +_] : ContOps, R](seen: Set[LazyParsley[_]])(implicit state: LetFinderState): M[R, Unit] = Utils.crash()
override protected def preprocess[M[_, +_] : ContOps, R, A_ >: Any](implicit lets: LetMap): M[R, StrictParsley[A_]] = Utils.crash()
override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[Any] = visitor.visitUnknown(this, context)
override private [parsley] def debugName = "dummyParser"
override private [parsley] var debugName = "dummyParser"
}

private class <**> extends LazyParsley[Any] {
override protected def findLetsAux[M[_, +_] : ContOps, R](seen: Set[LazyParsley[_]])(implicit state: LetFinderState): M[R, Unit] = Utils.crash()
override protected def preprocess[M[_, +_] : ContOps, R, A_ >: Any](implicit lets: LetMap): M[R, StrictParsley[A_]] = Utils.crash()
override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[Any] = visitor.visitUnknown(this, context)
override private [parsley] def debugName = "<**>"
override private [parsley] var debugName = "<**>"
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,64 +56,65 @@ class TotalAttachmentSpec extends ParsleyTest {
private final class AttachmentInspector extends GenericLazyParsleyIVisitor[Boolean, ConstUnit] {
def failure(msg: String = "Parent parser was not debugged."): Nothing = fail(msg)

override def visitSingleton[A](self: singletons.Singleton[A], context: Boolean): ConstUnit[A] =
if (context) CUnit else failure()
override def visitSingleton[A](self: singletons.Singleton[A], parentIsTag: Boolean): ConstUnit[A] = {
if (parentIsTag == self.isOpaque) CUnit else failure()
}

override def visitUnary[A, B](self: Unary[A, B], context: Boolean)(p: LazyParsley[A]): ConstUnit[B] =
if (context) {
visitUnknown(p, context = false): @unused
override def visitUnary[A, B](self: Unary[A, B], parentIsTag: Boolean)(p: LazyParsley[A]): ConstUnit[B] =
if (parentIsTag == self.isOpaque) {
visitUnknown(p, parentIsTag = false): @unused
CUnit
} else failure()

override def visitBinary[A, B, C](self: Binary[A, B, C], context: Boolean)(l: LazyParsley[A], r: => LazyParsley[B]): ConstUnit[C] =
if (context) {
visitUnknown(l, context = false): @unused
visitUnknown(r, context = false): @unused
override def visitBinary[A, B, C](self: Binary[A, B, C], parentIsTag: Boolean)(l: LazyParsley[A], r: => LazyParsley[B]): ConstUnit[C] =
if (parentIsTag == self.isOpaque) {
visitUnknown(l, parentIsTag = false): @unused
visitUnknown(r, parentIsTag = false): @unused
CUnit
} else failure()

override def visitTernary[A, B, C, D](self: Ternary[A, B, C, D], context: Boolean)(f: LazyParsley[A],
s: => LazyParsley[B],
t: => LazyParsley[C]): ConstUnit[D] =
if (context) {
visitUnknown(f, context = false): @unused
visitUnknown(s, context = false): @unused
visitUnknown(t, context = false): @unused
override def visitTernary[A, B, C, D](self: Ternary[A, B, C, D], parentIsTag: Boolean)(f: LazyParsley[A],
s: => LazyParsley[B],
t: => LazyParsley[C]): ConstUnit[D] =
if (parentIsTag == self.isOpaque) {
visitUnknown(f, parentIsTag = false): @unused
visitUnknown(s, parentIsTag = false): @unused
visitUnknown(t, parentIsTag = false): @unused
CUnit
} else failure()

override def visit[A](self: <|>[A], context: Boolean)(p: LazyParsley[A], q: LazyParsley[A]): ConstUnit[A] =
if (context) {
visitUnknown(p, context = false): @unused
visitUnknown(q, context = false): @unused
override def visit[A](self: <|>[A], parentIsTag: Boolean)(p: LazyParsley[A], q: LazyParsley[A]): ConstUnit[A] =
if (parentIsTag == self.isOpaque) {
visitUnknown(p, parentIsTag = false): @unused
visitUnknown(q, parentIsTag = false): @unused
CUnit
} else failure()

override def visit[A](self: ChainPre[A], context: Boolean)(p: LazyParsley[A], op: => LazyParsley[A => A]): ConstUnit[A] =
if (context) {
visitUnknown(p, context = false): @unused
visitUnknown(op, context = false): @unused
override def visit[A](self: ChainPre[A], parentIsTag: Boolean)(p: LazyParsley[A], op: => LazyParsley[A => A]): ConstUnit[A] =
if (parentIsTag == self.isOpaque) {
visitUnknown(p, parentIsTag = false): @unused
visitUnknown(op, parentIsTag = false): @unused
CUnit
} else failure()

// Somehow IntelliJ Scala thinks this is tail-recursive... but ScalaC does not?
//noinspection NoTailRecursionAnnotation
override def visitUnknown[A](self: LazyParsley[A], context: Boolean): ConstUnit[A] =
override def visitUnknown[A](self: LazyParsley[A], parentIsTag: Boolean): ConstUnit[A] =
self match {
case d: TaggedWith[_] if !context => visitUnknown(d.subParser, context = true)
case _: TaggedWith[_] => failure("Not allowed to stack debuggers.") // Can't have a debugged on top of another!
case s: singletons.Singleton[_] => visitSingleton(s.asInstanceOf[singletons.Singleton[A]], context)
case g: GenericLazyParsley[_] => visitGeneric(g.asInstanceOf[GenericLazyParsley[A]], context)
case alt: <|>[_] => alt.visit(this, context)
case cpre: ChainPre[_] => cpre.visit(this, context)
case _ => if (context) CUnit else failure()
case d: TaggedWith[_] if !parentIsTag => visitUnknown(d.subParser, parentIsTag = true)
case _: TaggedWith[_] => failure("Not allowed to stack debuggers.") // Can't have a debugged on top of another!
case s: singletons.Singleton[_] => visitSingleton(s.asInstanceOf[singletons.Singleton[A]], parentIsTag)
case g: GenericLazyParsley[_] => visitGeneric(g.asInstanceOf[GenericLazyParsley[A]], parentIsTag)
case alt: <|>[_] => alt.visit(this, parentIsTag)
case cpre: ChainPre[_] => cpre.visit(this, parentIsTag)
case _ => if (parentIsTag) CUnit else failure()
}

}

behavior of "the debug node attachment visitor"

it should "attach debuggers to all nodes of a parser" in {
it should "attach debuggers to all opaque nodes of a parser and not otherwise" in {
val verifier = new AttachmentInspector
val maxDepth = 12
val width = 100
Expand All @@ -124,7 +125,7 @@ class TotalAttachmentSpec extends ParsleyTest {
for (_ <- 0 until width) {
val (_, dbg) = attachDebugger(parserGenerator.generate(0))
dbg.internal match {
case seq: *>[_] => verifier.visitUnknown(seq.right, context = false)
case seq: *>[_] => verifier.visitUnknown(seq.right, parentIsTag = false)
case _ => fail("Debugger not attached.")
}
}
Expand All @@ -139,7 +140,7 @@ class TotalAttachmentSpec extends ParsleyTest {

val (_, dbg) = attachDebugger(par)
dbg.internal match {
case seq: *>[_] => verifier.visitUnknown(seq.right, context = false)
case seq: *>[_] => verifier.visitUnknown(seq.right, parentIsTag = false)
case _ => fail("Debugger not attached.")
}
}
Expand Down
40 changes: 23 additions & 17 deletions parsley/shared/src/main/scala/parsley/Parsley.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import parsley.internal.diagnostics.UserException
import parsley.internal.deepembedding.{frontend, singletons}
import parsley.internal.machine.Context

import Parsley.{emptyErr, pure, some}
import Parsley.{emptyErr, transPure => pure, some}
import XCompat._ // substituteCo

/**
Expand Down Expand Up @@ -160,7 +160,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @return a new parser that behaves the same as this parser, but with the given function `f` applied to its result.
* @group map
*/
def map[B](f: A => B): Parsley[B] = pure(f) <*> this
def map[B](f: A => B): Parsley[B] = (pure(f) <*> this).unsafeOpaque("map")
/** This combinator, pronounced "as", replaces the result of this parser, ignoring the old result.
*
* Similar to `map`, except the old result of this parser is not required to
Expand Down Expand Up @@ -208,7 +208,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @return a new parser that behaves the same as this parser, but always returns `()` on success.
* @group map
*/
def void: Parsley[Unit] = this.as(())
def void: Parsley[Unit] = this.as(()).unsafeOpaque("void")

// BRANCHING COMBINATORS
/** This combinator, pronounced "or", $or
Expand All @@ -232,7 +232,8 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @return a parser which either parses this parser or parses `q`.
* @group alt
*/
def <|>[Aʹ >: A](q: Parsley[Aʹ]): Parsley[Aʹ] = new Parsley(new frontend.<|>(this.internal, q.internal))
def <|>[Aʹ >: A](q: Parsley[Aʹ]): Parsley[Aʹ] = this.alt(q, "<|>")
@inline private def alt[Aʹ >: A](q: Parsley[Aʹ], name: String): Parsley[Aʹ] = new Parsley(new frontend.<|>(this.internal, q.internal, name))
/** This combinator, pronounced "or", $or
*
* $attemptreason
Expand All @@ -256,7 +257,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @note just an alias for `<|>`.
* @group alt
*/
def |[Aʹ >: A](q: Parsley[Aʹ]): Parsley[Aʹ] = this <|> q
def |[Aʹ >: A](q: Parsley[Aʹ]): Parsley[Aʹ] = this.alt(q, "|")
/** This combinator $or
*
* $attemptreason
Expand All @@ -280,7 +281,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @note just an alias for `<|>`.
* @group alt
*/
def orElse[Aʹ >: A](q: Parsley[Aʹ]): Parsley[Aʹ] = this <|> q
def orElse[Aʹ >: A](q: Parsley[Aʹ]): Parsley[Aʹ] = this.alt(q, "orElse")
/** This combinator, pronounced "or constant", $orconst
*
* $attemptreason
Expand All @@ -301,7 +302,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @return a parser which either parses this parser or returns `x`.
* @group alt
*/
def </>[Aʹ >: A](x: Aʹ): Parsley[Aʹ] = this <|> pure(x)
def </>[Aʹ >: A](x: Aʹ): Parsley[Aʹ] = this.alt(pure(x), "</>")
/** This combinator, pronounced "sum", wraps this parser's result in `Left` if it succeeds, and parses `q` if it failed '''without''' consuming input,
* wrapping the result in `Right`.
*
Expand All @@ -327,7 +328,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @return a parser which either parses this parser or parses `q` projecting their results into an `Either[A, B]`.
* @group alt
*/
def <+>[B](q: Parsley[B]): Parsley[Either[A, B]] = this.map(Left(_)) <|> q.map(Right(_))
def <+>[B](q: Parsley[B]): Parsley[Either[A, B]] = this.map(Left(_)).unsafeTransparent().alt(q.map(Right(_)).unsafeTransparent(), "<+>")

// SEQUENCING COMBINATORS
/** This combinator, pronounced "ap", first parses this parser then parses `px`: if both succeed then the function
Expand Down Expand Up @@ -698,7 +699,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @return a parser which parses this parser many times and folds the results together with `f` and `k` left-associatively.
* @group fold
*/
def foldLeft[B](k: B)(f: (B, A) => B): Parsley[B] = expr.infix.secretLeft1(pure(k), this, pure(f))
def foldLeft[B](k: B)(f: (B, A) => B): Parsley[B] = expr.infix.secretLeft1(pure(k), this, pure(f), "foldLeft")
/** This combinator will parse this parser '''one''' or more times combining the results with the function `f` and base value `k` from the right.
*
* This parser will continue to be parsed until it fails having '''not consumed''' input.
Expand Down Expand Up @@ -739,7 +740,7 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
* @since 2.1.0
* @group fold
*/
def foldLeft1[B](k: B)(f: (B, A) => B): Parsley[B] = expr.infix.secretLeft1(this.map(f(k, _)), this, pure(f))
def foldLeft1[B](k: B)(f: (B, A) => B): Parsley[B] = expr.infix.secretLeft1(this.map(f(k, _)), this, pure(f), "foldLeft1")
/** This combinator will parse this parser '''one''' or more times combining the results right-associatively with the function `op`.
*
* This parser will continue to be parsed until it fails having '''not consumed''' input.
Expand Down Expand Up @@ -920,11 +921,15 @@ final class Parsley[+A] private [parsley] (private [parsley] val internal: front
def withFilter(pred: A => Boolean): Parsley[A] = this.filter(pred)
// $COVERAGE-ON$

// hidden methods
// hidden methods (TODO: move these?)
private [parsley] def unsafeTransparent(): Parsley[A] = {
internal.transparent()
this
}
private [parsley] def unsafeOpaque(name: String): Parsley[A] = {
internal.opaque(name)
this
}
}

/** This object contains the core "function-style" combinators: all parsers will likely require something from within!
Expand Down Expand Up @@ -1045,7 +1050,8 @@ private [parsley] abstract class ParsleyImpl {
* @return a parser which consumes no input and produces a value `x`.
* @group basic
*/
final def pure[A](x: A): Parsley[A] = new Parsley(new singletons.Pure(x))
final def pure[A](x: A): Parsley[A] = new Parsley(new singletons.Pure(x, "pure"))
@inline private [parsley] final def transPure[A](x: A) = new Parsley(new singletons.Pure(x, null))
/** This combinator produces a '''new''' value everytime it is parsed without having any other effect.
*
* When this combinator is ran, no input is required, nor consumed, and
Expand Down Expand Up @@ -1126,7 +1132,7 @@ private [parsley] abstract class ParsleyImpl {
* @return a parser that will parse `p` then possibly parse `q` to transform `p`'s result into a `B`.
* @group cond
*/
final def select[A, B](p: Parsley[Either[A, B]], q: =>Parsley[A => B]): Parsley[B] = branch(p, q, pure(identity[B](_)))
final def select[A, B](p: Parsley[Either[A, B]], q: =>Parsley[A => B]): Parsley[B] = branch(p, q, transPure(identity[B](_)))
/** This combinator parses its argument `p`, but rolls back any consumed input on failure.
*
* If the parser `p` succeeds, then `atomic(p)` has no effect. However, if `p` failed,
Expand Down Expand Up @@ -1238,7 +1244,7 @@ private [parsley] abstract class ParsleyImpl {
* @note defined as `pure(())` as a simple convenience.
* @group basic
*/
final val unit: Parsley[Unit] = pure(())
final val unit: Parsley[Unit] = new Parsley(new singletons.Pure((), "unit"))

/** This parser only succeeds at the end of the input.
*
Expand Down Expand Up @@ -1315,14 +1321,14 @@ private [parsley] abstract class ParsleyImpl {
* @group iter
*/
final def some[A](p: Parsley[A]): Parsley[List[A]] = p <::> many(p)
private [parsley] final def some[A, C](p: Parsley[A], factory: Factory[A, C]): Parsley[C] = secretSome(p, p, factory)
private [parsley] final def some[A, C](p: Parsley[A], factory: Factory[A, C]): Parsley[C] = secretSome(p, p, factory).unsafeOpaque("some")
// This could be generalised to be the new many, where many(p, factory) = secretSome(fresh(factory.newBuilder), p, factory)
private [parsley] final def secretSome[A, C](init: Parsley[A], p: Parsley[A], factory: Factory[A, C]): Parsley[C] = {
secretSome(init.map(factory.newBuilder += _), p)
}
private [parsley] final def secretSome[A, C](init: Parsley[mutable.Builder[A, C]], p: Parsley[A]): Parsley[C] = {
val pf = pure[(mutable.Builder[A, C], A) => mutable.Builder[A, C]](_ += _)
val pf = transPure[(mutable.Builder[A, C], A) => mutable.Builder[A, C]](_ += _)
// Can't use the regular foldLeft1 here, because we need a fresh Builder each time.
expr.infix.secretLeft1(init, p, pf).map(_.result())
expr.infix.secretLeft1(init, p, pf, null).map(_.result())
}
}
1 change: 1 addition & 0 deletions parsley/shared/src/main/scala/parsley/ap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package parsley

//TODO: opaque/transparent
/** This module contains `ap1` through `ap22`, which allow for the application of a parser returning a function of arity `N` to `N` parsers.
*
* The combinators contained in this module all sequence a number of parsers together, but are capable of combining the
Expand Down
Loading

0 comments on commit d765fcc

Please sign in to comment.