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

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

package io.kaitai.struct.languages.components

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

/**
  * An interface for switching operations.
  */
trait SwitchOps {
  def switchStart(id: Identifier, on: Ast.expr): Unit
  def switchCaseFirstStart(condition: Ast.expr): Unit = switchCaseStart(condition)
  def switchCaseStart(condition: Ast.expr): Unit
  def switchCaseEnd(): Unit
  def switchElseStart(): Unit
  def switchElseEnd(): Unit = switchCaseEnd()
  def switchEnd(): Unit

  /**
    * Controls parsing of typeless (BytesType) alternative in switch case. If true,
    * then target language does not support storing both bytes array and true object
    * in the same variable, so we'll use workaround: bytes array will be read as
    * _raw_ variable (which would be used anyway for all other cases as well). If
    * false (which is default), we'll store *both* true objects and bytes array in
    * the same variable.
    */
  def switchBytesOnlyAsRaw = false

  /**
    * 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
    */
  def switchCases[T](
    id: Identifier,
    on: Ast.expr,
    cases: Map[Ast.expr, T],
    normalCaseProc: (T) => Unit,
    elseCaseProc: (T) => Unit
  ): Unit = {
    switchCasesRender(id, on, cases, normalCaseProc, elseCaseProc)
  }

  protected def switchCasesRender[T](
    id: Identifier,
    on: Ast.expr,
    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) {
      switchStart(id, on)

      // 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) {
              switchCaseFirstStart(condition)
              first = false
            } else {
              switchCaseStart(condition)
            }
            normalCaseProc(result)
            switchCaseEnd()
        }
      }

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

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy