Skip to content

Commit

Permalink
Add indexed Visitor classes for LazyParsley (#206)
Browse files Browse the repository at this point in the history
* feat: add visitor for LazyParsley

used for matching and processing while
carrying context without having to pattern
match or type match

must be extended every time a new internal
parser class is added

* refactor: marked visitors as visible only to parsley.internal

* test: add tests for laziness preservation
and for testing if the visitors are running
properly.

also fixed Lift3's missing laziness in the
visitor class.

* style: shortened lines for visit implementations
within LazyParsley classes

* fix: strengthened GenericLazyParsleyIVisitor's
private bound to frontend, also explicitly
declared Any type parameter for *> and <*'s
default implementations in that generic visitor

* fix: explicitly declared ManyUntil and SkipManyUntil's
default overrides' type arguments to
visitUnary due to Any inference

* fix: LazyParsleyIVisitor's private bound weakened
further to parsley to match LazyParsley's private
upper bound

* fix: subbed functions for partial case
analyses where appropriate

---------

Co-authored-by: Jamie Willis <[email protected]>
  • Loading branch information
MF42-DZH and j-mie6 authored Jun 29, 2023
1 parent 5c81ecb commit 9fe1b5b
Show file tree
Hide file tree
Showing 18 changed files with 620 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ private [parsley] final class <|>[A](p: LazyParsley[A], q: LazyParsley[A]) exten
p <- suspend(p.optimised[M, R, A])
q <- suspend(q.optimised[M, R, A])
} yield backend.<|>(p, q)

final override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this)(context, p, q)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,39 @@ import parsley.internal.deepembedding.backend, backend.StrictParsley

private [parsley] final class ErrorLabel[A](p: LazyParsley[A], private [ErrorLabel] val labels: Seq[String]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.ErrorLabel(p, labels)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, labels)
}
private [parsley] final class ErrorExplain[A](p: LazyParsley[A], reason: String) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.ErrorExplain(p, reason)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, reason)
}

private [parsley] final class ErrorAmend[A](p: LazyParsley[A], partial: Boolean) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.ErrorAmend(p, partial)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, partial)
}
private [parsley] final class ErrorEntrench[A](p: LazyParsley[A]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.ErrorEntrench(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p)
}
private [parsley] final class ErrorDislodge[A](n: Int, p: LazyParsley[A]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.ErrorDislodge(n, p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(n, p)
}

private [parsley] final class ErrorLexical[A](p: LazyParsley[A]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.ErrorLexical(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p)
}

private [parsley] final class VerifiedError[A](p: LazyParsley[A], msggen: Either[A => Seq[String], Option[A => String]]) extends Unary[A, Nothing](p) {
override def make(p: StrictParsley[A]): StrictParsley[Nothing] = new backend.VerifiedError(p, msggen)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[Nothing] = visitor.visit(this, context)(p, msggen)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ import parsley.internal.deepembedding.backend, backend.StrictParsley

private [parsley] final class Lift2[A, B, C](private [Lift2] val f: (A, B) => C, p: LazyParsley[A], q: =>LazyParsley[B]) extends Binary[A, B, C](p, q) {
override def make(p: StrictParsley[A], q: StrictParsley[B]): StrictParsley[C] = new backend.Lift2(f, p, q)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[C] = visitor.visit(this, context)(f, p, q)
}
private [parsley] final class Lift3[A, B, C, D](private [Lift3] val f: (A, B, C) => D, p: LazyParsley[A], q: =>LazyParsley[B], r: =>LazyParsley[C])
extends Ternary[A, B, C, D](p, q, r) {
override def make(p: StrictParsley[A], q: StrictParsley[B], r: StrictParsley[C]): StrictParsley[D] = new backend.Lift3(f, p, q, r)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[D] = visitor.visit(this, context)(f, p, q, r)
}
private [parsley] final class Local[S, A](val reg: Reg[S], p: LazyParsley[S], q: =>LazyParsley[A]) extends Binary[S, A, A](p, q) with UsesRegister {
override def make(p: StrictParsley[S], q: StrictParsley[A]): StrictParsley[A] = new backend.Local(reg, p, q)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(reg, p, q)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ import parsley.internal.deepembedding.backend, backend.StrictParsley

private [parsley] final class Many[A](p: LazyParsley[A]) extends Unary[A, List[A]](p) {
override def make(p: StrictParsley[A]): StrictParsley[List[A]] = new backend.Many(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[List[A]] = visitor.visit(this, context)(p)
}
private [parsley] final class SkipMany[A](p: LazyParsley[A]) extends Unary[A, Unit](p) {
override def make(p: StrictParsley[A]): StrictParsley[Unit] = new backend.SkipMany(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[Unit] = visitor.visit(this, context)(p)
}
private [parsley] final class ChainPost[A](p: LazyParsley[A], _op: =>LazyParsley[A => A]) extends Binary[A, A => A, A](p, _op) {
override def make(p: StrictParsley[A], op: StrictParsley[A => A]): StrictParsley[A] = new backend.ChainPost(p, op)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, _op)
}
private [parsley] final class ChainPre[A](p: LazyParsley[A], op: LazyParsley[A => A]) extends LazyParsley[A] {
final override def findLetsAux[M[_, _]: ContOps, R](seen: Set[LazyParsley[_]])(implicit state: LetFinderState): M[R, Unit] = {
Expand All @@ -24,21 +30,33 @@ private [parsley] final class ChainPre[A](p: LazyParsley[A], op: LazyParsley[A =
p <- suspend(p.optimised[M, R, A])
op <- suspend(op.optimised[M, R, A => A])
} yield new backend.ChainPre(p, op)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, op)
}
private [parsley] final class Chainl[A, B](init: LazyParsley[B], p: =>LazyParsley[A], op: =>LazyParsley[(B, A) => B])
extends Ternary[B, A, (B, A) => B, B](init, p, op) {
override def make(init: StrictParsley[B], p: StrictParsley[A], op: StrictParsley[(B, A) => B]): StrictParsley[B] = new backend.Chainl(init, p, op)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[B] = visitor.visit(this, context)(init, p, op)
}
private [parsley] final class Chainr[A, B](p: LazyParsley[A], op: =>LazyParsley[(A, B) => B], private [Chainr] val wrap: A => B)
extends Binary[A, (A, B) => B, B](p, op) {
override def make(p: StrictParsley[A], op: StrictParsley[(A, B) => B]): StrictParsley[B] = new backend.Chainr(p, op, wrap)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[B] = visitor.visit(this, context)(p, op, wrap)
}
private [parsley] final class SepEndBy1[A, B](p: LazyParsley[A], sep: =>LazyParsley[B]) extends Binary[A, B, List[A]](p, sep) {
override def make(p: StrictParsley[A], sep: StrictParsley[B]): StrictParsley[List[A]] = new backend.SepEndBy1(p, sep)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[List[A]] = visitor.visit(this, context)(p, sep)
}
private [parsley] final class ManyUntil[A](body: LazyParsley[Any]) extends Unary[Any, List[A]](body) {
override def make(p: StrictParsley[Any]): StrictParsley[List[A]] = new backend.ManyUntil(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[List[A]] = visitor.visit(this, context)(body)
}
private [parsley] final class SkipManyUntil(body: LazyParsley[Any]) extends Unary[Any, Unit](body) {
override def make(p: StrictParsley[Any]): StrictParsley[Unit] = new backend.SkipManyUntil(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[Unit] = visitor.visit(this, context)(body)
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ private [parsley] abstract class LazyParsley[+A] private [deepembedding] {
}
}

// Processing with visitors.
/** Use a visitor implementation to process this internal lazy parser. */
def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A]

// $COVERAGE-OFF$
/** Pretty-prints a combinator tree, for internal debugging purposes only. */
final private [internal] def prettyAST: String = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,39 @@ import parsley.internal.deepembedding.backend, backend.StrictParsley

private [parsley] final class Attempt[A](p: LazyParsley[A]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.Attempt(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p)
}
private [parsley] final class Look[A](p: LazyParsley[A]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.Look(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p)
}
private [parsley] final class NotFollowedBy[A](p: LazyParsley[A]) extends Unary[A, Unit](p) {
override def make(p: StrictParsley[A]): StrictParsley[Unit] = new backend.NotFollowedBy(p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[Unit] = visitor.visit(this, context)(p)
}
private [parsley] final class Put[S](val reg: Reg[S], _p: LazyParsley[S]) extends Unary[S, Unit](_p) with UsesRegister {
override def make(p: StrictParsley[S]): StrictParsley[Unit] = new backend.Put(reg, p)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[Unit] = visitor.visit(this, context)(reg, _p)
}
private [parsley] final class NewReg[S, A](val reg: Reg[S], init: LazyParsley[S], body: =>LazyParsley[A])
extends Binary[S, A, A](init, body) with UsesRegister {
override def make(init: StrictParsley[S], body: StrictParsley[A]): StrictParsley[A] = new backend.NewReg(reg, init, body)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(reg, init, body)
}
// $COVERAGE-OFF$
private [parsley] final class Debug[A](p: LazyParsley[A], name: String, ascii: Boolean, break: Breakpoint) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.Debug(p, name, ascii, break)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, name, ascii, break)
}
private [parsley] final class DebugError[A](p: LazyParsley[A], name: String, ascii: Boolean, errBuilder: ErrorBuilder[_]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.DebugError(p, name, ascii, errBuilder)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, name, ascii, errBuilder)
}
// $COVERAGE-ON$
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,38 @@ import parsley.internal.deepembedding.backend, backend.StrictParsley
private [parsley] final class Branch[A, B, C](b: LazyParsley[Either[A, B]], p: =>LazyParsley[A => C], q: =>LazyParsley[B => C])
extends Ternary[Either[A, B], A => C, B => C, C](b, p, q) {
override def make(b: StrictParsley[Either[A, B]], p: StrictParsley[A => C], q: StrictParsley[B => C]): StrictParsley[C] = new backend.Branch(b, p, q)

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

private [parsley] final class If[A](b: LazyParsley[Boolean], p: =>LazyParsley[A], q: =>LazyParsley[A]) extends Ternary[Boolean, A, A, A](b, p, q) {
override def make(b: StrictParsley[Boolean], p: StrictParsley[A], q: StrictParsley[A]): StrictParsley[A] = new backend.If(b, p, q)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(b, p, q)
}

private [parsley] final class Filter[A](p: LazyParsley[A], pred: A => Boolean) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.Filter(p, pred)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, pred)
}
private [parsley] final class MapFilter[A, B](p: LazyParsley[A], f: A => Option[B]) extends Unary[A, B](p) {
override def make(p: StrictParsley[A]): StrictParsley[B] = new backend.MapFilter(p, f)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[B] = visitor.visit(this, context)(p, f)
}
private [parsley] final class FilterOut[A](p: LazyParsley[A], pred: PartialFunction[A, String]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.FilterOut(p, pred)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, pred)
}
private [parsley] final class GuardAgainst[A](p: LazyParsley[A], pred: PartialFunction[A, Seq[String]]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.GuardAgainst(p, pred)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, pred)
}
private [parsley] final class UnexpectedWhen[A](p: LazyParsley[A], pred: PartialFunction[A, (String, Option[String])]) extends Unary[A, A](p) {
override def make(p: StrictParsley[A]): StrictParsley[A] = new backend.UnexpectedWhen(p, pred)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(p, pred)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ import parsley.internal.deepembedding.backend, backend.StrictParsley

private [parsley] final class <*>[A, B](pf: LazyParsley[A => B], px: =>LazyParsley[A]) extends Binary[A => B, A, B](pf, px) {
override def make(pf: StrictParsley[A => B], px: StrictParsley[A]): StrictParsley[B] = new backend.<*>(pf, px)

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

private [parsley] final class >>=[A, B](p: LazyParsley[A], private [>>=] val f: A => LazyParsley[B]) extends Unary[A, B](p) {
override def make(p: StrictParsley[A]): StrictParsley[B] = new backend.>>=(p, f)

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

private [parsley] final class *>[A](_p: LazyParsley[_], _q: =>LazyParsley[A]) extends Binary[Any, A, A](_p, _q) {
override def make(p: StrictParsley[Any], q: StrictParsley[A]): StrictParsley[A] = backend.*>(p, q)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(_p, _q)
}
private [parsley] final class <*[A](_p: LazyParsley[A], _q: =>LazyParsley[_]) extends Binary[A, Any, A](_p, _q) {
override def make(p: StrictParsley[A], q: StrictParsley[Any]): StrictParsley[A] = backend.<*(p, q)

override def visit[T, U[+_]](visitor: LazyParsleyIVisitor[T, U], context: T): U[A] = visitor.visit(this, context)(_p, _q)
}
Loading

0 comments on commit 9fe1b5b

Please sign in to comment.