1
0

diplomacy: detect and report cycles in the diplomatic graph

This commit is contained in:
Wesley W. Terpstra 2017-09-27 11:46:06 -07:00
parent 5af08966d8
commit e35d3df6ea

View File

@ -199,6 +199,11 @@ trait OutwardNode[DO, UO, BO <: Data] extends BaseNode with OutwardNodeHandle[DO
protected[diplomacy] val oParams: Seq[DO] protected[diplomacy] val oParams: Seq[DO]
} }
abstract class CycleException(kind: String, loop: Seq[String]) extends Exception(s"Diplomatic ${kind} cycle detected involving ${loop}")
case class StarCycleException(loop: Seq[String] = Nil) extends CycleException("star", loop)
case class DownwardCycleException(loop: Seq[String] = Nil) extends CycleException("downward", loop)
case class UpwardCycleException(loop: Seq[String] = Nil) extends CycleException("upward", loop)
case class Edges[EI, EO](in: EI, out: EO) case class Edges[EI, EO](in: EI, out: EO)
sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
inner: InwardNodeImp [DI, UI, EI, BI], inner: InwardNodeImp [DI, UI, EI, BI],
@ -212,7 +217,10 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO]
protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI]
private var starCycleGuard = false
protected[diplomacy] lazy val (oPortMapping, iPortMapping, oStar, iStar) = { protected[diplomacy] lazy val (oPortMapping, iPortMapping, oStar, iStar) = {
try {
if (starCycleGuard) throw StarCycleException()
val oStars = oBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size val oStars = oBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size
val iStars = iBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size val iStars = iBindings.filter { case (_,_,b,_,_) => b == BIND_STAR }.size
val oKnown = oBindings.map { case (_, n, b, _, _) => b match { val oKnown = oBindings.map { case (_, n, b, _, _) => b match {
@ -237,6 +245,9 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
require(numPO.contains(oTotal), s"${name} has ${oTotal} outputs, expected ${numPO}${lazyModule.line}") require(numPO.contains(oTotal), s"${name} has ${oTotal} outputs, expected ${numPO}${lazyModule.line}")
require(numPI.contains(iTotal), s"${name} has ${iTotal} inputs, expected ${numPI}${lazyModule.line}") require(numPI.contains(iTotal), s"${name} has ${iTotal} inputs, expected ${numPI}${lazyModule.line}")
(oSum.init zip oSum.tail, iSum.init zip iSum.tail, oStar, iStar) (oSum.init zip oSum.tail, iSum.init zip iSum.tail, oStar, iStar)
} catch {
case c: StarCycleException => throw c.copy(loop = s"${name}${lazyModule.line}" +: c.loop)
}
} }
lazy val oPorts = oBindings.flatMap { case (i, n, _, p, s) => lazy val oPorts = oBindings.flatMap { case (i, n, _, p, s) =>
@ -248,15 +259,30 @@ sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
(start until end) map { j => (j, n, p, s) } (start until end) map { j => (j, n, p, s) }
} }
private var oParamsCycleGuard = false
protected[diplomacy] lazy val oParams: Seq[DO] = { protected[diplomacy] lazy val oParams: Seq[DO] = {
try {
if (oParamsCycleGuard) throw DownwardCycleException()
oParamsCycleGuard = true
val o = mapParamsD(oPorts.size, iPorts.map { case (i, n, _, _) => n.oParams(i) }) val o = mapParamsD(oPorts.size, iPorts.map { case (i, n, _, _) => n.oParams(i) })
require (o.size == oPorts.size, s"Bug in diplomacy; ${name} has ${o.size} != ${oPorts.size} down/up outer parameters${lazyModule.line}") require (o.size == oPorts.size, s"Bug in diplomacy; ${name} has ${o.size} != ${oPorts.size} down/up outer parameters${lazyModule.line}")
o.map(outer.mixO(_, this)) o.map(outer.mixO(_, this))
} catch {
case c: DownwardCycleException => throw c.copy(loop = s"${name}${lazyModule.line}" +: c.loop)
} }
}
private var iParamsCycleGuard = false
protected[diplomacy] lazy val iParams: Seq[UI] = { protected[diplomacy] lazy val iParams: Seq[UI] = {
try {
if (iParamsCycleGuard) throw UpwardCycleException()
iParamsCycleGuard = true
val i = mapParamsU(iPorts.size, oPorts.map { case (o, n, _, _) => n.iParams(o) }) val i = mapParamsU(iPorts.size, oPorts.map { case (o, n, _, _) => n.iParams(o) })
require (i.size == iPorts.size, s"Bug in diplomacy; ${name} has ${i.size} != ${iPorts.size} up/down inner parameters${lazyModule.line}") require (i.size == iPorts.size, s"Bug in diplomacy; ${name} has ${i.size} != ${iPorts.size} up/down inner parameters${lazyModule.line}")
i.map(inner.mixI(_, this)) i.map(inner.mixI(_, this))
} catch {
case c: UpwardCycleException => throw c.copy(loop = s"${name}${lazyModule.line}" +: c.loop)
}
} }
protected[diplomacy] def gco = if (iParams.size != 1) None else inner.getO(iParams(0)) protected[diplomacy] def gco = if (iParams.size != 1) None else inner.getO(iParams(0))