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

dotty.tools.dotc.util.NameTransformer.scala Maven / Gradle / Ivy

The newest version!
package dotty.tools
package dotc
package util

import core.Names._
import collection.mutable
import scala.internal.Chars

import scala.annotation.internal.sharable

/** Provides functions to encode and decode Scala symbolic names.
 */
object NameTransformer {

  private val nops = 128

  @sharable private val op2code = new Array[String](nops)
  @sharable private val str2op = new mutable.HashMap[String, Char]

  private def enterOp(op: Char, code: String) = {
    op2code(op) = code
    str2op(code) = op
  }

  /* Note: decoding assumes opcodes are only ever lowercase. */
  enterOp('~', "$tilde")
  enterOp('=', "$eq")
  enterOp('<', "$less")
  enterOp('>', "$greater")
  enterOp('!', "$bang")
  enterOp('#', "$hash")
  enterOp('%', "$percent")
  enterOp('^', "$up")
  enterOp('&', "$amp")
  enterOp('|', "$bar")
  enterOp('*', "$times")
  enterOp('/', "$div")
  enterOp('+', "$plus")
  enterOp('-', "$minus")
  enterOp(':', "$colon")
  enterOp('\\', "$bslash")
  enterOp('?', "$qmark")
  enterOp('@', "$at")

  /** Expand characters that are illegal as JVM method names by `$u`, followed
   *  by the character's unicode expansion.
   */
  def avoidIllegalChars(name: SimpleName): SimpleName = {
    var i = name.length - 1
    while (i >= 0 && Chars.isValidJVMMethodChar(name(i))) i -= 1
    if (i >= 0)
      termName(
        name.toString.flatMap(ch =>
          if (Chars.isValidJVMMethodChar(ch)) ch.toString else "$u%04X".format(ch.toInt)))
    else name
  }

  /** Decode expanded characters starting with `$u`, followed by the character's unicode expansion. */
  def decodeIllegalChars(name: String): String = {
    if (name.contains("$u")) {
      val sb = new mutable.StringBuilder()
      var i = 0
      while (i < name.length) {
        if (i < name.length - 5 && name(i) == '$' && name(i + 1) == 'u') {
          val numbers = name.substring(i + 2, i + 6)
          try sb.append(Integer.valueOf(name.substring(i + 2, i + 6), 16).toChar)
          catch {
            case _: java.lang.NumberFormatException =>
              sb.append("$u").append(numbers)
          }
          i += 6
        } else {
          sb.append(name(i))
          i += 1
        }
      }
      sb.result()
    }
    else name
  }

  /** Replace operator symbols by corresponding expansion strings.
   *
   *  @param name the string to encode
   *  @return     the string with all recognized opchars replaced with their encoding
   *
   *  Operator symbols are only recognized if they make up the whole name, or
   *  if they make up the last part of the name which follows a `_`.
   */
  def encode(name: SimpleName): SimpleName = {
    def loop(len: Int, ops: List[String]): SimpleName = {
      def convert =
        if (ops.isEmpty) name
        else {
          val buf = new java.lang.StringBuilder
          buf.append(chrs, name.start, len)
          for (op <- ops) buf.append(op)
          termName(buf.toString)
        }
      if (len == 0 || name(len - 1) == '_') convert
      else {
        val ch = name(len - 1)
        if (ch <= nops && op2code(ch) != null)
          loop(len - 1, op2code(ch) :: ops)
        else if (Chars.isSpecial(ch))
          loop(len - 1, ch.toString :: ops)
        else name
      }
    }
    loop(name.length, Nil)
  }

  /** Replace operator expansions by the operators themselves.
   *  Operator expansions are only recognized if they make up the whole name, or
   *  if they make up the last part of the name which follows a `_`.
   */
  def decode(name: SimpleName): SimpleName = {
    def loop(len: Int, ops: List[Char]): SimpleName = {
      def convert =
        if (ops.isEmpty) name
        else {
          val buf = new java.lang.StringBuilder
          buf.append(chrs, name.start, len)
          for (op <- ops) buf.append(op)
          termName(buf.toString)
        }
      if (len == 0 || name(len - 1) == '_') convert
      else if (Chars.isSpecial(name(len - 1))) loop(len - 1, name(len - 1) :: ops)
      else {
        val idx = name.lastIndexOf('$', len - 1)
        if (idx >= 0 && idx + 2 < len)
          str2op.get(name.sliceToString(idx, len)) match {
            case Some(ch) => loop(idx, ch :: ops)
            case None => name
          }
        else name
      }
    }
    loop(name.length, Nil)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy