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

scala.tools.nsc.interpreter.Naming.scala Maven / Gradle / Ivy

The newest version!
/*
 * Scala (https://www.scala-lang.org)
 *
 * Copyright EPFL and Lightbend, Inc.
 *
 * Licensed under Apache License 2.0
 * (http://www.apache.org/licenses/LICENSE-2.0).
 *
 * See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 */

package scala.tools.nsc.interpreter

import scala.util.matching.Regex

/** This is for name logic which is independent of the compiler (notice there's no Global.)
 *  That includes at least generating, metaquoting, mangling, and unmangling.
 */
object Naming {
  // The CSI pattern matches a subset of the following spec:
  // For CSI, or "Control Sequence Introducer" commands,
  // the ESC [ is followed by any number (including none) of "parameter bytes" in the range 0x30–0x3F (ASCII 0–9:;<=>?),
  // then by any number of "intermediate bytes" in the range 0x20–0x2F (ASCII space and !"#$%&'()*+,-./),
  // then finally by a single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~)
  private final val esc = "\u001b"    // "\N{escape}"
  private val csi = raw"$esc\[[0-9;]*([\x40-\x7E])"

  // Matches one of 3 alternatives:
  // group 1 is the CSI command letter, where 'm' is color rendition
  // group 2 is a sequence of chars to be rendered as `?`: anything non-printable and not some space char
  // additional groups are introduced by linePattern but not used
  private lazy val cleaner = raw"$csi|([\p{Cntrl}&&[^\p{Space}]]+)|$linePattern".r

  /** Final pass to clean up REPL output.
   *
   *  Substrings representing REPL artifacts are stripped.
   *
   *  Attempt to replace dangerous characters with '?', which might otherwise
   *  put the terminal in a bad state. Allow SGR (select graphic rendition, "m")
   *  control sequences, but restrict otherwise.
   */
  def unmangle(str: String): String = cleaner.replaceSomeIn(str, clean)

  private def clean(m: Regex.Match): Option[String] =
    if ("m" == m.group(1) ) None
    else if (m.group(1) != null || m.group(2) != null) Some("?" * (m.end - m.start))
    else Some("")

  // Uncompiled regex pattern to detect `line` package and members
  // `read`, `eval`, `print`, for purposes of filtering output and stack traces.
  //
  // The two name forms this is catching are the two sides of this assignment:
  //
  // $line3.$read.$iw.Bippy =
  //   $line3.$read$$iw$$Bippy@4a6a00ca
  //
  // This needs to be aware of LambdaMetafactory generated classnames to strip the correct number of '$' delimiters.
  // A lambda hosted in a module `$iw` (which has a module class `$iw$` is named `$iw$ $ $Lambda1234` (spaces added
  // here for clarification.) This differs from an explicitly declared inner classes named `$Foo`, which would be
  // `$iw$$Foo`.
  //
  // (\Q$line\E\d+(\Q$read\E)?|\Q$read\E(\.INSTANCE)?(\$\Q$iw\E)?|\Q$eval\E|\Q$print\E|\Q$iw\E)(\.this\.|\.|/|\$\$(?=\$Lambda)|\$|$)
  //
  private def linePattern: String = {
    import Regex.{quote => q}
    val sn        = sessionNames
    val lineN     = raw"${q(sn.line)}\d+"
    val lineNRead = raw"$lineN(${q(sn.read)})?"
    val lambda    = """(\.this\.|\.|/|\$\$(?=\$Lambda)|\$|$)"""
    raw"($lineNRead|${q(sn.read)}(\.INSTANCE)?(\$$${q(sn.iw)})?|${q(sn.eval)}|${q(sn.print)}|${q(sn.iw)})$lambda"
  }
  lazy val lineRegex: Regex = linePattern.r

  object sessionNames {
    // All values are configurable by passing e.g. -Dscala.repl.name.read=XXX
    final def propOr(name: String): String = propOr(name, "$" + name)
    final def propOr(name: String, default: String): String =
      sys.props.getOrElse("scala.repl.name." + name, default)

    // Prefixes used in repl machinery.  Default to $line, $read, etc.
    def line = propOr("line")
    def read = "$" + "read"
    def iw = "$" + "iw"
    def eval = propOr("eval")
    def print = propOr("print")
    def result = propOr("result")
    def packageName(lineId: Int) = line + lineId

    /** Create the name for the temp val used in the -Yclass-based REPL wrapper to refer to the state of a previous line. */
    final def lineReadValName(linePackageName: String) = s"${linePackageName}${read}"

    // The prefix for unnamed results: by default res0, res1, etc.
    def res = propOr("res", "res") // INTERPRETER_VAR_PREFIX
    // Internal ones
    def ires = propOr("ires")
  }

  /** Generates names pre0, pre1, etc. via calls to apply method */
  class NameCreator(pre: String) {
    private var x = -1
    var mostRecent: String = ""

    def apply(): String = {
      x += 1
      mostRecent = pre + x
      mostRecent
    }
    def reset(): Unit = x = -1
    def didGenerate(name: String) =
      (name startsWith pre) && ((name drop pre.length) forall (_.isDigit))
  }
}

trait Naming {
  import Naming.{NameCreator, sessionNames}
  private lazy val userVar     = new NameCreator(sessionNames.res)  // var name, like res0
  private lazy val internalVar = new NameCreator(sessionNames.ires) // internal var name, like $ires0
  private var _freshLineId = 0

  def isUserVarName(name: String)     = userVar didGenerate name
  def isInternalVarName(name: String) = internalVar didGenerate name

  def freshUserVarName() = userVar()
  def freshInternalVarName() = internalVar()
  def freshLineId() = { _freshLineId += 1 ; _freshLineId}

  def resetAllCreators(): Unit = {
    userVar.reset()
    internalVar.reset()
    _freshLineId = 0
  }

  def mostRecentVar = userVar.mostRecent
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy