All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dfhdl.core.DFIf.scala Maven / Gradle / Ivy

package dfhdl.core
import dfhdl.internals.*
import dfhdl.compiler.ir
import dfhdl.compiler.printing.*

import ir.DFConditional
import ir.DFConditional.{DFIfHeader, DFIfElseBlock}

protected[core] def analyzeControlRet(ret: Any)(using DFC): DFTypeAny = Exact.strip(ret) match
  case v: DFValAny =>
    // adding ident placement as the last member in the control block
    DFVal.Alias.AsIs.ident(v)(using dfc.anonymize)
    v.dfType
  case _ =>
    DFUnit

object DFIf:
  def singleBranch[R](
      condOption: Option[DFValOf[DFBool]],
      prevBlockOrHeader: DFOwnerAny | DFValAny,
      run: () => R
  )(using
      DFC
  ): (DFTypeAny, DFOwnerAny) =
    // first we create the header without a known type.
    val block = DFIf.Block(condOption, prevBlockOrHeader)
    dfc.enterOwner(block)
    // now all members of the branch will be constructed
    val ret: R = run()
    val dfType = analyzeControlRet(ret)
    dfc.exitOwner()
    (dfType, block)
  end singleBranch
  def fromBranches[R](
      branches: List[(DFValOf[DFBool], () => R)],
      elseOption: Option[() => R]
  )(using DFC): R = try
    val header = Header(DFUnit)
    val dfcAnon = summon[DFC].anonymize
    var branchTypes = List.empty[ir.DFType]
    // creating a hook to save the return value for the first branch run
    var firstIfRet: Option[R] = None
    val firstIfRun: () => R = () =>
      firstIfRet = Some(branches.head._2())
      firstIfRet.get
    val firstIf = singleBranch(Some(branches.head._1), header, firstIfRun)
    branchTypes = firstIf._1.asIR :: branchTypes
    val midIfsBlock =
      branches.drop(1).foldLeft(firstIf._2) { case (prevBlock, branch) =>
        val (dfType, block) =
          singleBranch(Some(branch._1), prevBlock, branch._2)(using dfcAnon)
        branchTypes = dfType.asIR :: branchTypes
        block
      }
    elseOption.foreach { e =>
      val (dfType, _) = singleBranch(None, midIfsBlock, e)(using dfcAnon)
      branchTypes = dfType.asIR :: branchTypes
    }
    val hasNoType = branchTypes.contains(ir.DFUnit)
    // if one branch has DFUnit, the return type is DFUnit.
    // otherwise, all types must be the same.
    if (hasNoType || branchTypes.allElementsAreEqual)
      val retDFType = if (hasNoType) ir.DFUnit else branchTypes.head
      val DFVal(headerIR: DFIfHeader) = header: @unchecked
      val headerUpdate = headerIR.copy(dfType = retDFType)
      // updating the type of the if header
      headerIR.replaceMemberWith(headerUpdate).asValAny.asInstanceOf[R]
    else // violation
      given printer: Printer = DefaultPrinter(using dfc.getSet)
      val err = DFError.Basic(
        "if",
        new IllegalArgumentException(
          s"""|This DFHDL `if` expression has different return types for branches.
              |These are its branch types in order:
              |${branchTypes.view.reverse.map(t => printer.csDFType(t)).mkString("\n")}
              |""".stripMargin
        )
      )
      dfc.logError(err)
      err.asVal[DFTypeAny, ModifierAny].asInstanceOf[R]
    end if
  catch case e: DFError => DFVal(DFError.Derived(e)).asInstanceOf[R]
  end fromBranches

  object Header:
    def apply(dfType: DFTypeAny)(using DFC): DFValAny =
      DFIfHeader(
        dfType.asIR,
        dfc.owner.ref,
        dfc.getMeta,
        ir.DFTags.empty
      ).addMember.asValAny
  end Header

  object Block:
    def apply(
        guardOption: Option[DFValOf[DFBool]],
        prevBlockOrHeader: DFOwnerAny | DFValAny
    )(using
        DFC
    ): DFOwnerAny =
      val guardRef: DFConditional.Block.GuardRef = guardOption match
        case Some(cond) => cond.asIR.refTW[DFIfElseBlock]
        case None       => ir.DFRef.TwoWay.Empty
      val prevBlockOrHeaderRef: DFIfElseBlock.Ref = prevBlockOrHeader match
        case prevBlock: DFOwnerAny =>
          prevBlock.asIR.asInstanceOf[DFIfElseBlock].refTW[DFIfElseBlock]
        case header: DFValAny =>
          header.asIR.asInstanceOf[DFIfHeader].refTW[DFIfElseBlock]
      val block: DFIfElseBlock =
        DFIfElseBlock(
          guardRef,
          prevBlockOrHeaderRef,
          dfc.owner.ref,
          dfc.getMeta,
          ir.DFTags.empty
        ).addMember
      block.asFE
    end apply
  end Block
end DFIf




© 2015 - 2024 Weber Informatics LLC | Privacy Policy