Skip to content

Commit

Permalink
Add count
Browse files Browse the repository at this point in the history
This required implementing my own Traverse instance. See [this issue](higherkindness#72).
  • Loading branch information
ceedubs committed Sep 9, 2018
1 parent 97f8b88 commit ec96323
Showing 1 changed file with 49 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package examples

import qq.droste.data._
import qq.droste.data.prelude._
import qq.droste.macros.deriveTraverse
import cats.implicits._

import cats.Order
import cats.{Applicative, Eval, Order, Traverse}
import cats.implicits._

@deriveTraverse
sealed abstract class KleeneF[+A] extends Product with Serializable

object KleeneF {
Expand All @@ -18,6 +16,33 @@ object KleeneF {
final case class Star[+A](value: A) extends KleeneF[A]
case object Zero extends KleeneF[Nothing]
case object One extends KleeneF[Nothing]

implicit val traverseKleeneF: Traverse[KleeneF] = new Traverse[KleeneF] {
def traverse[G[_], A, B](fa: KleeneF[A])(f: A => G[B])(implicit G: Applicative[G]): G[KleeneF[B]] =
fa match {
case Plus(l, r) => G.map2(f(l), f(r))((lb, rb) => Plus(lb, rb))
case Times(l, r) => G.map2(f(l), f(r))((lb, rb) => Times(lb, rb))
case Star(x) => G.map(f(x))(Star(_))
case Zero => G.pure(Zero)
case One => G.pure(One)
}

def foldLeft[A, B](fa: KleeneF[A], b: B)(f: (B, A) => B): B = fa match {
case Plus(l, r) => f(f(b, l), r)
case Times(l, r) => f(f(b, l), r)
case Star(x) => f(b, x)
case Zero => b
case One => b
}

def foldRight[A, B](fa: KleeneF[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = fa match {
case Plus(l, r) => f(l, f(r, lb))
case Times(l, r) => f(l, f(r, lb))
case Star(x) => f(x, lb)
case Zero => lb
case One => lb
}
}
}

sealed abstract class Match[+A] extends Product with Serializable
Expand Down Expand Up @@ -81,6 +106,13 @@ object regex {

def wildcard[A]: Regex[A] = toMu(Fix(CoattrF.pure(Match.Wildcard)))

/**
* A match on the empty string (this should always succeed and consume no input).
*/
def empty[A]: Regex[A] = Mu(CoattrF.roll(KleeneF.One))

def count[A](n: Int, r: Regex[A]): Regex[A] = (1 to n).foldLeft(empty[A])((acc, _) => andThen(acc, r))

def consume[A:Order]: Algebra[CoattrF[KleeneF, Match[A], ?], Consume[A]] =
Algebra[CoattrF[KleeneF, Match[A], ?], Consume[A]]{
CoattrF.un(_) match {
Expand Down Expand Up @@ -192,6 +224,20 @@ final class RegexTests extends Properties("Regex"){
property("oneOrMore two") = stringMatcher(oneOrMore(literal('b')))("bb")

property("oneOrMore three") = stringMatcher(oneOrMore(literal('b')))("bbb")

property("count zero empty") = stringMatcher(count(0, literal('b')))("")

property("count zero non-empty") = !stringMatcher(count(0, literal('b')))("b")

property("count 1 empty") = !stringMatcher(count(1, literal('b')))("")

property("count 1 match") = stringMatcher(count(1, literal('b')))("b")

property("count 1 non-match") = !stringMatcher(count(1, literal('b')))("c")

property("count 2 match") = stringMatcher(count(2, literal('b')))("bb")

property("count 2 non-match") = !stringMatcher(count(2, literal('b')))("bc")
}

final class ScalacheckRegexTests extends Properties("Regex"){
Expand Down

0 comments on commit ec96323

Please sign in to comment.