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

io.kaitai.struct.translators.CppTranslator.scala Maven / Gradle / Ivy

package io.kaitai.struct.translators

import java.nio.charset.Charset

import io.kaitai.struct.{ImportList, Utils}
import io.kaitai.struct.exprlang.Ast
import io.kaitai.struct.exprlang.Ast.expr
import io.kaitai.struct.datatype.DataType
import io.kaitai.struct.datatype.DataType._
import io.kaitai.struct.format.Identifier
import io.kaitai.struct.languages.CppCompiler

class CppTranslator(provider: TypeProvider, importListSrc: ImportList) extends BaseTranslator(provider) {
  val CHARSET_UTF8 = Charset.forName("UTF-8")

  /**
    * Handles string literal for C++ by wrapping a C `const char*`-style string
    * into a std::string constructor. Note that normally std::string
    * constructor treats given string in C manner, i.e. as zero-terminated
    * (and it is indeed would be generated by compiler as zero-terminated const
    * in .rodata segment). However, this is bad for string literals that contain
    * zero inside them: they would be cut abruptly at that zero. So, for string
    * literals that contain zero inside them, we use another constructor, which
    * allows explicit byte size argument.
    *
    * @param s string to present as C++ string literal
    * @return string as C++ string literal
    */
  override def doStringLiteral(s: String): String = {
    val lenSuffix = if (s.contains("\0")) {
      ", " + s.getBytes(CHARSET_UTF8).length
    } else {
      ""
    }
    s"std::string(${super.doStringLiteral(s)}$lenSuffix)"
  }

  /**
    * http://en.cppreference.com/w/cpp/language/escape
    */
  override val asciiCharQuoteMap: Map[Char, String] = Map(
    '\t' -> "\\t",
    '\n' -> "\\n",
    '\r' -> "\\r",
    '"' -> "\\\"",
    '\\' -> "\\\\",

    '\7' -> "\\a",
    '\f' -> "\\f",
    '\13' -> "\\v",
    '\b' -> "\\b"
  )

  override def doArrayLiteral(t: DataType, values: Seq[expr]): String =
    throw new RuntimeException("C++ literal arrays are not implemented yet")

  override def doByteArrayLiteral(arr: Seq[Byte]): String =
    "std::string(\"" + Utils.hexEscapeByteArray(arr) + "\", " + arr.length + ")"

  override def numericBinOp(left: Ast.expr, op: Ast.operator, right: Ast.expr) = {
    (detectType(left), detectType(right), op) match {
      case (_: IntType, _: IntType, Ast.operator.Mod) =>
        s"${CppCompiler.kstreamName}::mod(${translate(left)}, ${translate(right)})"
      case _ =>
        super.numericBinOp(left, op, right)
    }
  }

  override def anyField(value: expr, attrName: String): String =
    s"${translate(value)}->${doName(attrName)}"

  override def doName(s: String) = s match {
    case Identifier.ITERATOR => "_"
    case Identifier.ITERATOR2 => "_buf"
    case Identifier.INDEX => "i"
    case _ => s"$s()"
  }

  override def doEnumByLabel(enumType: List[String], label: String): String =
    (enumType.last + "_" + label).toUpperCase
  override def doEnumById(enumType: List[String], id: String): String =
    s"static_cast<${CppCompiler.types2class(enumType)}>($id)"

  override def doStrCompareOp(left: Ast.expr, op: Ast.cmpop, right: Ast.expr) = {
    if (op == Ast.cmpop.Eq) {
      s"${translate(left)} == (${translate(right)})"
    } else if (op == Ast.cmpop.NotEq) {
      s"${translate(left)} != ${translate(right)}"
    } else {
      s"(${translate(left)}.compare(${translate(right)}) ${cmpOp(op)} 0)"
    }
  }

  override def doSubscript(container: expr, idx: expr): String =
    s"${translate(container)}->at(${translate(idx)})"
  override def doIfExp(condition: expr, ifTrue: expr, ifFalse: expr): String =
    s"((${translate(condition)}) ? (${translate(ifTrue)}) : (${translate(ifFalse)}))"
  override def doCast(value: Ast.expr, typeName: String): String =
    s"static_cast<${CppCompiler.types2class(List(typeName))}*>(${translate(value)})"

  // Predefined methods of various types
  override def strToInt(s: expr, base: expr): String = {
    val baseStr = translate(base)
    s"std::stoi(${translate(s)}" + (baseStr match {
      case "10" => ""
      case _ => s", 0, $baseStr"
    }) + ")"
  }
  override def enumToInt(v: expr, et: EnumType): String =
    translate(v)
  override def boolToInt(v: expr): String =
    s"((${translate(v)}) ? 1 : 0)"
  override def floatToInt(v: expr): String =
    s"static_cast(${translate(v)})"
  override def intToStr(i: expr, base: expr): String = {
    val baseStr = translate(base)
    baseStr match {
      case "10" =>
        // FIXME: proper way for C++11, but not available in earlier versions
        //s"std::to_string(${translate(i)})"
        s"${CppCompiler.kstreamName}::to_string(${translate(i)})"
      case _ => throw new UnsupportedOperationException(baseStr)
    }
  }
  override def bytesToStr(bytesExpr: String, encoding: Ast.expr): String =
    s"${CppCompiler.kstreamName}::bytes_to_str($bytesExpr, ${translate(encoding)})"
  override def strLength(s: expr): String =
    s"${translate(s)}.length()"
  override def strReverse(s: expr): String =
    s"${CppCompiler.kstreamName}::reverse(${translate(s)})"
  override def strSubstring(s: expr, from: expr, to: expr): String =
    s"${translate(s)}.substr(${translate(from)}, (${translate(to)}) - (${translate(from)}))"

  override def arrayFirst(a: expr): String =
    s"${translate(a)}->front()"
  override def arrayLast(a: expr): String =
    s"${translate(a)}->back()"
  override def arraySize(a: expr): String =
    s"${translate(a)}->size()"
  override def arrayMin(a: expr): String = {
    importListSrc.add("algorithm")
    val v = translate(a)
    s"*std::min_element($v->begin(), $v->end())"
  }
  override def arrayMax(a: expr): String = {
    importListSrc.add("algorithm")
    val v = translate(a)
    s"*std::max_element($v->begin(), $v->end())"
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy