<commit_before>package ammonite.repl.interp

import java.lang.reflect.InvocationTargetException
import java.net.URL

import acyclic.file
import ammonite.repl.frontend.{ReplExit, ReplAPI}
import ammonite.repl._
import java.net.URLClassLoader

import ammonite.repl.interp.Evaluator.SpecialClassloader

import scala.reflect.runtime.universe._
import scala.collection.mutable
import scala.util.Try

/**
 * Takes source code and, with the help of a compiler and preprocessor,
 * evaluates it and returns a `Result[(output: String, imports: String)]`
 * where `output` is what gets printed and `imports` are any imports that
 * need to get prepended to subsequent commands.
 */
trait Evaluator{
  def evalClass(code: String, wrapperName: String): Res[(Class[_], Seq[ImportData])]

  /**
   * _2, _1, 0, 1, 2, 3...
   *
   * Numbers starting from _ are used to represent predefined commands
   */
  def getCurrentLine: String
  def update(newImports: Seq[ImportData]): Unit

  /**
   * Takes the preprocessed `code` and `printCode` and compiles/evals/runs/etc.
   * it to provide a result. Takes `printer` as a callback, instead of returning
   * the `Iterator` as part of the output, because printing can cause side effects
   * (e.g. for Streams which are lazily printed) and can fail with an exception!
   * passing in the callback ensures the printing is still done lazily, but within
   * the exception-handling block of the `Evaluator`
   */
  def processLine(code: String, printCode: String, printer: Iterator[String] => Unit): Res[Evaluated]

  def previousImportBlock: String
  def addJar(url: URL): Unit
  def evalClassloader: SpecialClassloader
}

object Evaluator{
  def apply(currentClassloader: ClassLoader,
            compile: => (Array[Byte], String => Unit) => Compiler.Output,
            startingLine: Int): Evaluator = new Evaluator{
    
    /**
     * Imports which are required by earlier commands to the REPL. Imports
     * have a specified key, so that later imports of the same name (e.g.
     * defining a variable twice) can kick the earlier import out of the
     * map. Otherwise if you import the same name twice you get compile
     * errors instead of the desired shadowing.
     */
    lazy val previousImports = {
      def namesFor(t: scala.reflect.runtime.universe.Type): Set[String] = {
        val yours = t.members.map(_.name.toString).toSet
        val default = typeOf[Object].members.map(_.name.toString)
        yours -- default
      }

      def importsFor[T: TypeTag](name: String) = {
        namesFor(typeOf[T]).map(n => n -> ImportData(n, n, "", name)).toSeq
      }
      mutable.Map(
        importsFor[ReplAPI]("ReplBridge.shell") ++
        importsFor[ammonite.repl.IvyConstructor]("ammonite.repl.IvyConstructor") ++
        // For some reason this is neccessary for implicit resolution to work properly
        Seq("pprint.PPrint" -> ImportData("FinalRepr", "FinalRepr", "", "pprint.PPrint")) ++
        Seq("pprint.PPrint" -> ImportData("Contra", "Contra", "", "pprint.PPrint")) ++
        Seq("pprint" -> ImportData("pprintln", "pprintln", "", "pprint"))
        :_*
      )
    }

    /**
     * The current line number of the REPL, used to make sure every snippet
     * evaluated can have a distinct name that doesn't collide.
     */
    var currentLine = startingLine

    /**
     * Weird indirection only necessary because of
     * https://issues.scala-lang.org/browse/SI-7085
     */
    def getCurrentLine = currentLine.toString.replace("-", "_")

    /**
     * Performs the conversion of our pre-compiled `Array[Byte]`s into
     * actual classes with methods we can execute.
     *
     * Structured such that when a class is desired:
     *<commit_msg>fixbuild<commit_after>package ammonite.repl.interp

import java.lang.reflect.InvocationTargetException
import java.net.URL

import acyclic.file
import ammonite.repl.frontend.{ReplExit, ReplAPI}
import ammonite.repl._
import java.net.URLClassLoader

import ammonite.repl.interp.Evaluator.SpecialClassloader

import scala.reflect.runtime.universe._
import scala.collection.mutable
import scala.util.Try

/**
 * Takes source code and, with the help of a compiler and preprocessor,
 * evaluates it and returns a `Result[(output: String, imports: String)]`
 * where `output` is what gets printed and `imports` are any imports that
 * need to get prepended to subsequent commands.
 */
trait Evaluator{
  def evalClass(code: String, wrapperName: String): Res[(Class[_], Seq[ImportData])]

  /**
   * _2, _1, 0, 1, 2, 3...
   *
   * Numbers starting from _ are used to represent predefined commands
   */
  def getCurrentLine: String
  def update(newImports: Seq[ImportData]): Unit

  /**
   * Takes the preprocessed `code` and `printCode` and compiles/evals/runs/etc.
   * it to provide a result. Takes `printer` as a callback, instead of returning
   * the `Iterator` as part of the output, because printing can cause side effects
   * (e.g. for Streams which are lazily printed) and can fail with an exception!
   * passing in the callback ensures the printing is still done lazily, but within
   * the exception-handling block of the `Evaluator`
   */
  def processLine(code: String, printCode: String, printer: Iterator[String] => Unit): Res[Evaluated]

  def previousImportBlock: String
  def addJar(url: URL): Unit
  def evalClassloader: SpecialClassloader
}

object Evaluator{
  def apply(currentClassloader: ClassLoader,
            compile: => (Array[Byte], String => Unit) => Compiler.Output,
            startingLine: Int): Evaluator = new Evaluator{
    
    /**
     * Imports which are required by earlier commands to the REPL. Imports
     * have a specified key, so that later imports of the same name (e.g.
     * defining a variable twice) can kick the earlier import out of the
     * map. Otherwise if you import the same name twice you get compile
     * errors instead of the desired shadowing.
     */
    lazy val previousImports = {
      def namesFor(t: scala.reflect.runtime.universe.Type): Set[String] = {
        val yours = t.members.map(_.name.toString).toSet
        val default = typeOf[Object].members.map(_.name.toString)
        yours -- default
      }

      def importsFor[T: TypeTag](name: String) = {
        namesFor(typeOf[T]).map(n => n -> ImportData(n, n, "", name)).toSeq
      }

      // Having these fails in 2.10.x; not having them fails in 2.11.x, both
      // with obscure implicit-type-inference problems =( So just hard-code it
      // for now
      val pprintImports =
        if (!scala.util.Properties.versionString.contains("2.10"))
          Seq(
            "pprint.PPrint" -> ImportData("FinalRepr", "FinalRepr", "", "pprint.PPrint"),
            "pprint.PPrint" -> ImportData("Contra", "Contra", "", "pprint.PPrint")
          )
        else
          Nil

      mutable.Map(
        importsFor[ReplAPI]("ReplBridge.shell") ++
        importsFor[ammonite.repl.IvyConstructor]("ammonite.repl.IvyConstructor") ++
        pprintImports ++
        Seq("pprint" -> ImportData("pprintln", "pprintln", "", "pprint"))
        :_*
      )
    }

    /**
     * The current line number of the REPL, used to make sure every snippet
     * evaluated can have a distinct name that doesn't collide.
     */
    var currentLine = startingLine

    /**
     * Weird indirection only necessary because of
     * https://issues.scala-lang.org/browse/SI-7085
     */
    def getCurrentLine = currentLine.toString.replace("-", "_")

    /**
     * Performs the conversion of our pre-compiled `Array[Byte]`s into
     * actual classes with methods we can execute.
     *
     * Structured such that when a class is desired:
     *
{'fraction_non_alphanumeric': 0.10814679854242581, 'fraction_numerical': 0.0037740760020822488, 'mean_word_length': 3.0899414582224587, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2365828', 'n_tokens_mistral': 2345, 'n_tokens_neox': 2292, 'n_words': 732}