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

io.kaitai.struct.languages.components.SwitchIfOps.scala Maven / Gradle / Ivy

package io.kaitai.struct.languages.components

import io.kaitai.struct.ClassTypeProvider
import io.kaitai.struct.datatype.DataType
import io.kaitai.struct.datatype.DataType.SwitchType
import io.kaitai.struct.exprlang.Ast
import io.kaitai.struct.format.Identifier
import io.kaitai.struct.translators.BaseTranslator

/**
  * Trait to be used when language needs two implementation of switching:
  * a "true" one utilizing switch-like statement and an "if emulation"
  * which utilizes series of if-then-else statements to emulate a switch.
  *
  * "True" switches are typically more efficient, but are limited to a
  * subset of types. Examples of consumers of this pattern are C++, C#, Java.
  */
trait SwitchIfOps extends SwitchOps {
  val translator: BaseTranslator
  def typeProvider: ClassTypeProvider

  /**
    * Determines if this particular implementation of switches would be ok with true
    * built-in `switch` mechanism, or it will require `if`-based emulation.
    *
    * @param onType type we'll be switching over
    * @return true if `if`-based emulation is required
    */
  def switchRequiresIfs(onType: DataType): Boolean

  def switchIfStart(id: Identifier, on: Ast.expr, onType: DataType): Unit
  def switchIfCaseFirstStart(condition: Ast.expr): Unit = switchIfCaseStart(condition)
  def switchIfCaseStart(condition: Ast.expr): Unit
  def switchIfCaseEnd(): Unit
  def switchIfElseStart(): Unit
  def switchIfElseEnd(): Unit = switchIfCaseEnd()
  def switchIfEnd(): Unit

  /**
    * Generate switch cases by calling case procedures. Suitable for a wide variety of
    * target languages that something remotely resembling C-like `switch`-`case` statement.
    * Thanks to customizable argument type for case procedures, can be used for switch type
    * handling and a variety of other cases (i.e. switching between customizable endianness,
    * etc).
    * @param id attribute identifier
    * @param on on expression to decide upon
    * @param cases cases map: keys should be expressions, values are arbitrary typed objects
    *              that will be passed to case procedures
    * @param normalCaseProc procedure that would handle "normal" (i.e. non-else case)
    * @param elseCaseProc procedure that would handle "else" case
    * @tparam T type of object to pass to procedures
    */
  override def switchCases[T](
    id: Identifier,
    on: Ast.expr,
    cases: Map[Ast.expr, T],
    normalCaseProc: (T) => Unit,
    elseCaseProc: (T) => Unit
  ): Unit = {
    val onType = translator.detectType(on)
    typeProvider._currentSwitchType = Some(onType)
    val switchIfs = switchRequiresIfs(onType)

    if (switchIfs) {
      switchCasesUsingIf(id, on, onType, cases, normalCaseProc, elseCaseProc)
    } else {
      switchCasesRender(id, on, cases, normalCaseProc, elseCaseProc)
    }
  }

  protected def switchCasesUsingIf[T](
    id: Identifier,
    on: Ast.expr,
    onType: DataType,
    cases: Map[Ast.expr, T],
    normalCaseProc: (T) => Unit,
    elseCaseProc: (T) => Unit
  ): Unit = {
    val someNormalCases = cases.filter { case (caseExpr, _) =>
      caseExpr != SwitchType.ELSE_CONST
    }.size > 0

    if (someNormalCases) {
      switchIfStart(id, on, onType)

      // Pass 1: only normal case clauses
      var first = true

      cases.foreach { case (condition, result) =>
        condition match {
          case SwitchType.ELSE_CONST =>
            // skip for now
          case _ =>
            if (first) {
              switchIfCaseFirstStart(condition)
              first = false
            } else {
              switchIfCaseStart(condition)
            }
            normalCaseProc(result)
            switchIfCaseEnd()
        }
      }

      // Pass 2: else clause, if it is there
      cases.get(SwitchType.ELSE_CONST).foreach { (result) =>
        switchIfElseStart()
        elseCaseProc(result)
        switchIfElseEnd()
      }

      switchIfEnd()
    } else {
      cases.get(SwitchType.ELSE_CONST).foreach { (result) =>
        elseCaseProc(result)
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy