136 lines
4.6 KiB
Scala
136 lines
4.6 KiB
Scala
package tf.bug.fancadetagless
|
|
|
|
import cats._
|
|
import cats.implicits._
|
|
import higherkindness.droste.data._
|
|
import polymorphic._
|
|
import tf.bug.fancadegraph.Level
|
|
import tf.bug.fancadegraph.PinDefinition
|
|
import tf.bug.fancadegraph.Template
|
|
import tf.bug.fancadegraph.Block
|
|
import tf.bug.fancadescodec.Position
|
|
import scalax.collection.Graph
|
|
import scalax.collection.GraphEdge.DiHyperEdge
|
|
import tf.bug.fancadegraph.Pin
|
|
|
|
sealed trait FancadeF[A]
|
|
|
|
object FancadeF {
|
|
|
|
case class Create[A](from: Vector[A], template: Exists[Template], imports: Vector[PinDefinition], exports: Vector[PinDefinition]) extends FancadeF[A]
|
|
case class Select[A](from: A, indicies: Vector[Int]) extends FancadeF[A]
|
|
case class Combine[A](from: Vector[A]) extends FancadeF[A]
|
|
case class Sequence[A](first: A, afterPin: PinDefinition, next: A) extends FancadeF[A]
|
|
case class Noop[A]() extends FancadeF[A]
|
|
|
|
implicit val fancadeTraverse: Traverse[FancadeF] = new Traverse[FancadeF] {
|
|
override def traverse[G[_]: Applicative, A, B](fa: FancadeF[A])(f: A => G[B]): G[FancadeF[B]] =
|
|
fa match {
|
|
case Create(v, t, i, e) => v.traverse(f).map(w => Create(w, t, i, e))
|
|
case Select(a, i) => f(a).map(b => Select(b, i))
|
|
case Combine(v) => v.traverse(f).map(w => Combine(w))
|
|
case Sequence(i, p, n) => (f(i), f(n)).mapN((j, o) => Sequence(j, p, o))
|
|
case Noop() => Noop[B]().pure[G].widen
|
|
}
|
|
|
|
override def foldLeft[A, B](fa: FancadeF[A], b: B)(f: (B, A) => B): B =
|
|
fa match {
|
|
case Create(v, _, _, _) => v.foldLeft(b)(f)
|
|
case Select(a, _) => f(b, a)
|
|
case Combine(v) => v.foldLeft(b)(f)
|
|
case Sequence(i, _, n) => f(f(b, i), n)
|
|
case Noop() => b
|
|
}
|
|
|
|
override def foldRight[A, B](fa: FancadeF[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
|
|
fa match {
|
|
case Create(v, _, _, _) => v.foldr(lb)(f)
|
|
case Select(a, _) => f(a, lb)
|
|
case Combine(v) => v.foldr(lb)(f)
|
|
case Sequence(i, _, n) => f(i, f(n, lb))
|
|
case Noop() => lb
|
|
}
|
|
}
|
|
|
|
def origins(root: Int, stack: Vector[FancadeF[Int]]): Vector[(Int, PinDefinition)] = {
|
|
stack(root) match {
|
|
case Create(_, _, _, pins) =>
|
|
pins.map((root, _))
|
|
case Select(f, i) =>
|
|
val original = origins(f, stack)
|
|
i.map(original(_))
|
|
case Combine(f) =>
|
|
f.flatMap(origins(_, stack))
|
|
case Sequence(f, _, _) =>
|
|
origins(f, stack)
|
|
case Noop() =>
|
|
Vector.empty
|
|
}
|
|
}
|
|
|
|
def filteredIndex[A](vec: Vector[A], f: A => Boolean): Vector[(A, Int)] = {
|
|
vec.zipWithIndex.filter { case (e, _) => f(e) }
|
|
}
|
|
|
|
def render(f: Fix[FancadeF]): Level = {
|
|
val pc: PointerChain[Id, FancadeF] = PointerChain.deduplicate[Id, FancadeF](f)
|
|
val pcv: Vector[FancadeF[Int]] = pc.vertices
|
|
val bdVector: Vector[Exists[Template]] = pcv.collect {
|
|
case Create(_, template, _, _) => template
|
|
}
|
|
val blocks: Set[Block] = bdVector.zipWithIndex.map {
|
|
case (template, idx) =>
|
|
Block(
|
|
Position(0, idx, 0),
|
|
template
|
|
)
|
|
}.toSet
|
|
val pcf: Vector[(FancadeF[Int], Int)] = filteredIndex(pcv, (_: FancadeF[Int]) match {
|
|
case Create(_, _, _, _) => true
|
|
case _ => false
|
|
})
|
|
val pcfr: Int => Int =
|
|
idx => pcf.indexWhere { case (_, i) => i == idx }
|
|
val wires: Graph[Pin, DiHyperEdge] =
|
|
pcv.zipWithIndex.foldLeft(Graph.empty[Pin, DiHyperEdge]) {
|
|
case (graph, (entry, idx)) =>
|
|
entry match {
|
|
case Create(from, _, imports, _) =>
|
|
val thisH = pcfr(idx)
|
|
val dependencies: Vector[(Int, PinDefinition)] = from.flatMap(origins(_, pcv))
|
|
val newWires = imports.zip(dependencies).map {
|
|
case (thisPinD, (thatI, thatPinD)) =>
|
|
val thisPin = Pin(
|
|
Position(0, thisH, 0),
|
|
thisPinD
|
|
)
|
|
val thatPin = Pin(
|
|
Position(0, pcfr(thatI), 0),
|
|
thatPinD
|
|
)
|
|
DiHyperEdge(thatPin, thisPin)
|
|
}
|
|
graph ++ newWires
|
|
case Sequence(f, p, n) =>
|
|
if(pcv(n) == Noop()) graph
|
|
else {
|
|
val po = origins(f, pcv)
|
|
val to = Pin(
|
|
Position(0, pcfr(po.head._1), 0),
|
|
p
|
|
)
|
|
val fo = origins(n, pcv)
|
|
val from = Pin(
|
|
Position(0, pcfr(fo.head._1), 0),
|
|
fo.head._2
|
|
)
|
|
graph ++ Set(DiHyperEdge(from, to))
|
|
}
|
|
case _ => graph
|
|
}
|
|
}
|
|
Level(blocks, wires)
|
|
}
|
|
|
|
}
|