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

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

package io.kaitai.struct.translators

import io.kaitai.struct.Utils
import io.kaitai.struct.datatype.DataType.EnumType
import io.kaitai.struct.exprlang.Ast
import io.kaitai.struct.format.Identifier
import io.kaitai.struct.languages.RubyCompiler

class RubyTranslator(provider: TypeProvider) extends BaseTranslator(provider)
  with ByteArraysAsTrueArrays[String] {
  override def doByteArrayLiteral(arr: Seq[Byte]): String =
    s"${super.doByteArrayLiteral(arr)}.pack('C*')"
  override def doByteArrayNonLiteral(elts: Seq[Ast.expr]): String =
    s"[${elts.map(translate).mkString(", ")}].pack('C*')"

  // https://github.com/ruby/ruby/blob/trunk/doc/syntax/literals.rdoc#strings
  // https://github.com/ruby/ruby/blob/trunk/string.c - see "rb_str_inspect"
  override val asciiCharQuoteMap: Map[Char, String] = Map(
    '\t' -> "\\t",
    '\n' -> "\\n",
    '\r' -> "\\r",
    '"' -> "\\\"",
    '\\' -> "\\\\",

    '#' -> "\\#",
    '\u0007' -> "\\a",
    '\f' -> "\\f",
    '\u000b' -> "\\v",
    '\u001b' -> "\\e",
    '\b' -> "\\b"
  )

  override def doName(s: String) = {
    s match {
      case Identifier.INDEX => "i" // FIXME: probably would clash with attribute named "i"
      case _ => s
    }
  }

  override def doInternalName(id: Identifier): String =
    RubyCompiler.publicMemberName(id)

  override def doEnumByLabel(enumTypeAbs: List[String], label: String): String =
    s":${enumTypeAbs.last}_$label"
  override def doEnumById(enumType: List[String], id: String): String =
    s"${RubyCompiler.kstreamName}::resolve_enum(${enumDirectMap(enumType)}, $id)"

  def enumDirectMap(enumTypeAndName: List[String]): String = {
    val enumTypeAbs = enumTypeAndName.dropRight(1)
    val enumTypeName = Utils.upperUnderscoreCase(enumTypeAndName.last)

    val enumTypeRel = Utils.relClass(enumTypeAbs, provider.nowClass.name)

    if (enumTypeRel.nonEmpty) {
      (enumTypeRel.map((x) => Utils.upperCamelCase(x)) ++ List(enumTypeName)).mkString("::")
    } else {
      enumTypeName
    }
  }

  def enumInverseMap(et: EnumType): String = {
    val enumTypeAndName = et.enumSpec.get.name
    val enumDirectMap = this.enumDirectMap(enumTypeAndName)
    val enumNameDirect = Utils.upperUnderscoreCase(enumTypeAndName.last)
    val enumNameInverse = RubyCompiler.inverseEnumName(enumNameDirect)

    enumDirectMap.replace(enumNameDirect, enumNameInverse)
  }

  override def arraySubscript(container: Ast.expr, idx: Ast.expr): String =
    s"${translate(container)}[${translate(idx)}]"
  override def doIfExp(condition: Ast.expr, ifTrue: Ast.expr, ifFalse: Ast.expr): String =
    s"(${translate(condition)} ? ${translate(ifTrue)} : ${translate(ifFalse)})"

  // Predefined methods of various types
  override def strToInt(s: Ast.expr, base: Ast.expr): String = {
    val baseStr = translate(base)
    translate(s) + ".to_i" + (baseStr match {
      case "10" => ""
      case _ => s"($baseStr)"
    })
  }
  override def enumToInt(v: Ast.expr, et: EnumType): String =
    s"${enumInverseMap(et)}[${translate(v)}]"
  override def floatToInt(v: Ast.expr): String =
    s"(${translate(v)}).to_i"
  override def intToStr(i: Ast.expr, base: Ast.expr): String =
    translate(i) + s".to_s(${translate(base)})"

  override def bytesToStr(bytesExpr: String, encoding: Ast.expr): String =
    s"($bytesExpr).force_encoding(${translate(encoding)})"
  override def bytesLength(b: Ast.expr): String =
    s"${translate(b)}.size"
  /**
    * Alternatives considered:
    *
    * * value[0].ord => 6341 => winner by performance
    * * value.bytes[0] => 8303
    */
  override def bytesSubscript(container: Ast.expr, idx: Ast.expr): String =
    s"${translate(container)}[${translate(idx)}].ord"
  override def bytesFirst(b: Ast.expr): String =
    s"${translate(b)}[0].ord"
  override def bytesLast(b: Ast.expr): String =
    s"${translate(b)}[-1].ord"
  override def bytesMin(b: Ast.expr): String =
    s"${translate(b)}.bytes.min"
  override def bytesMax(b: Ast.expr): String =
    s"${translate(b)}.bytes.max"

  override def strLength(s: Ast.expr): String =
    s"${translate(s)}.size"
  override def strReverse(s: Ast.expr): String =
    s"${translate(s)}.reverse"
  override def strSubstring(s: Ast.expr, from: Ast.expr, to: Ast.expr): String =
    s"${translate(s)}[${translate(from)}..(${translate(to)} - 1)]"

  override def arrayFirst(a: Ast.expr): String =
    s"${translate(a)}.first"
  override def arrayLast(a: Ast.expr): String =
    s"${translate(a)}.last"
  override def arraySize(a: Ast.expr): String =
    s"${translate(a)}.length"
  override def arrayMin(a: Ast.expr): String =
    s"${translate(a)}.min"
  override def arrayMax(a: Ast.expr): String =
    s"${translate(a)}.max"

  override def kaitaiStreamEof(value: Ast.expr): String =
    s"${translate(value)}.eof?"
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy