diff --git a/amm/compiler/src/main/scala-3.0.0-3.3.1/ammonite/compiler/AmmonitePhase.scala b/amm/compiler/src/main/scala-3.0.0-3.3.1/ammonite/compiler/AmmonitePhase.scala deleted file mode 100644 index 333645aa7..000000000 --- a/amm/compiler/src/main/scala-3.0.0-3.3.1/ammonite/compiler/AmmonitePhase.scala +++ /dev/null @@ -1,264 +0,0 @@ -package ammonite.compiler - -import ammonite.util.{ImportData, Imports, Name => AmmName, Printer, Util} - -import dotty.tools.dotc -import dotty.tools.dotc.core.StdNames.nme -import dotc.ast.Trees._ -import dotc.ast.{tpd, untpd} -import dotc.core.Flags -import dotc.core.Contexts._ -import dotc.core.Names.Name -import dotc.core.Phases.Phase -import dotc.core.Symbols.{NoSymbol, Symbol, newSymbol} -import dotc.core.Types.{TermRef, Type, TypeTraverser} - -import scala.collection.mutable - -class AmmonitePhase( - userCodeNestingLevel: => Int, - needsUsedEarlierDefinitions: => Boolean -) extends Phase: - import tpd._ - - def phaseName: String = "ammonite" - - private var myImports = new mutable.ListBuffer[(Boolean, String, String, Seq[AmmName])] - private var usedEarlierDefinitions0 = new mutable.ListBuffer[String] - - def importData: Seq[ImportData] = - val grouped = myImports - .toList - .distinct - .groupBy { case (a, b, c, d) => (b, c, d) } - .mapValues(_.map(_._1)) - - val open = for { - ((fromName, toName, importString), items) <- grouped - if !CompilerUtil.ignoredNames(fromName) - } yield { - val importType = items match{ - case Seq(true) => ImportData.Type - case Seq(false) => ImportData.Term - case Seq(_, _) => ImportData.TermType - } - - ImportData(AmmName(fromName), AmmName(toName), importString, importType) - } - - open.toVector.sortBy(x => Util.encodeScalaSourcePath(x.prefix)) - - def usedEarlierDefinitions: Seq[String] = - usedEarlierDefinitions0.toList.distinct - - private def saneSym(name: Name, sym: Symbol)(using Context): Boolean = - !name.decode.toString.contains('$') && - sym.exists && - // !sym.is(Flags.Synthetic) && - !scala.util.Try(sym.is(Flags.Private)).toOption.getOrElse(true) && - !scala.util.Try(sym.is(Flags.Protected)).toOption.getOrElse(true) && - // sym.is(Flags.Public) && - !CompilerUtil.ignoredSyms(sym.toString) && - !CompilerUtil.ignoredNames(name.decode.toString) - - private def saneSym(sym: Symbol)(using Context): Boolean = - saneSym(sym.name, sym) - - private def processTree(t: tpd.Tree)(using Context): Unit = { - val sym = t.symbol - val name = t match { - case t: tpd.ValDef => t.name - case _ => sym.name - } - if (saneSym(name, sym)) { - val name = sym.name.decode.toString - myImports.addOne((sym.isType, name, name, Nil)) - } - } - - private def processImport(i: tpd.Import)(using Context): Unit = { - val expr = i.expr - val selectors = i.selectors - - // Most of that logic was adapted from AmmonitePlugin, the Scala 2 counterpart - // of this file. - - val prefix = - val (_ :: nameListTail, symbolHead :: _) = { - def rec(expr: tpd.Tree): List[(Name, Symbol)] = { - expr match { - case s @ tpd.Select(lhs, _) => (s.symbol.name -> s.symbol) :: rec(lhs) - case i @ tpd.Ident(name) => List(name -> i.symbol) - case t @ tpd.This(pkg) => List(pkg.name -> t.symbol) - } - } - rec(expr).reverse.unzip - } - - val headFullPath = symbolHead.fullName.decode.toString.split('.') - .map(n => if (n.endsWith("$")) n.stripSuffix("$") else n) // meh - // prefix package imports with `_root_` to try and stop random - // variables from interfering with them. If someone defines a value - // called `_root_`, this will still break, but that's their problem - val rootPrefix = if(symbolHead.denot.is(Flags.Package)) Seq("_root_") else Nil - val tailPath = nameListTail.map(_.decode.toString) - - (rootPrefix ++ headFullPath ++ tailPath).map(AmmName(_)) - - def isMask(sel: untpd.ImportSelector) = sel.name != nme.WILDCARD && sel.rename == nme.WILDCARD - - val renameMap = - - /** - * A map of each name importable from `expr`, to a `Seq[Boolean]` - * containing a `true` if there's a type-symbol you can import, `false` - * if there's a non-type symbol and both if there are both type and - * non-type symbols that are importable for that name - */ - val importableIsTypes = - expr.tpe - .allMembers - .map(_.symbol) - .filter(saneSym(_)) - .groupBy(_.name.decode.toString) - .mapValues(_.map(_.isType).toVector) - - val renamings = for{ - t @ untpd.ImportSelector(name, renameTree, _) <- selectors - if !isMask(t) - // getOrElse just in case... - isType <- importableIsTypes.getOrElse(name.name.decode.toString, Nil) - Ident(rename) <- Option(renameTree) - } yield ((isType, rename.decode.toString), name.name.decode.toString) - - renamings.toMap - - - def isUnimportableUnlessRenamed(sym: Symbol): Boolean = - sym eq NoSymbol - - @scala.annotation.tailrec - def transformImport(selectors: List[untpd.ImportSelector], sym: Symbol): List[Symbol] = - selectors match { - case Nil => Nil - case sel :: Nil if sel.isWildcard => - if (isUnimportableUnlessRenamed(sym)) Nil - else List(sym) - case (sel @ untpd.ImportSelector(from, to, _)) :: _ - if from.name == (if (from.isTerm) sym.name.toTermName else sym.name.toTypeName) => - if (isMask(sel)) Nil - else List( - newSymbol(sym.owner, sel.rename, sym.flags, sym.info, sym.privateWithin, sym.coord) - ) - case _ :: rest => transformImport(rest, sym) - } - - val symNames = - for { - sym <- expr.tpe.allMembers.map(_.symbol).flatMap(transformImport(selectors, _)) - if saneSym(sym) - } yield (sym.isType, sym.name.decode.toString) - - val syms = for { - // For some reason `info.allImportedSymbols` does not show imported - // type aliases when they are imported directly e.g. - // - // import scala.reflect.macros.Context - // - // As opposed to via import scala.reflect.macros._. - // Thus we need to combine allImportedSymbols with the renameMap - (isType, sym) <- (symNames.toList ++ renameMap.keys).distinct - } yield (isType, renameMap.getOrElse((isType, sym), sym), sym, prefix) - - myImports ++= syms - } - - private def updateUsedEarlierDefinitions( - wrapperSym: Symbol, - stats: List[tpd.Tree] - )(using Context): Unit = { - /* - * We list the variables from the first wrapper - * used from the user code. - * - * E.g. if, after wrapping, the code looks like - * ``` - * class cmd2 { - * - * val cmd0 = ??? - * val cmd1 = ??? - * - * import cmd0.{ - * n - * } - * - * class Helper { - * // user-typed code - * val n0 = n + 1 - * } - * } - * ``` - * this would process the tree of `val n0 = n + 1`, find `n` as a tree like - * `cmd2.this.cmd0.n`, and put `cmd0` in `uses`. - */ - - val typeTraverser: TypeTraverser = new TypeTraverser { - def traverse(tpe: Type) = tpe match { - case tr: TermRef if tr.prefix.typeSymbol == wrapperSym => - tr.designator match { - case n: Name => usedEarlierDefinitions0 += n.decode.toString - case s: Symbol => usedEarlierDefinitions0 += s.name.decode.toString - case _ => // can this happen? - } - case _ => - traverseChildren(tpe) - } - } - - val traverser: TreeTraverser = new TreeTraverser { - def traverse(tree: Tree)(using Context) = tree match { - case tpd.Select(node, name) if node.symbol == wrapperSym => - usedEarlierDefinitions0 += name.decode.toString - case tt @ tpd.TypeTree() => - typeTraverser.traverse(tt.tpe) - case _ => - traverseChildren(tree) - } - } - - for (tree <- stats) - traverser.traverse(tree) - } - - private def unpkg(tree: tpd.Tree): List[tpd.Tree] = - tree match { - case PackageDef(_, elems) => elems.flatMap(unpkg) - case _ => List(tree) - } - - def run(using Context): Unit = - val elems = unpkg(ctx.compilationUnit.tpdTree) - def mainStats(trees: List[tpd.Tree]): List[tpd.Tree] = - trees - .reverseIterator - .collectFirst { - case TypeDef(name, rhs0: Template) => rhs0.body - } - .getOrElse(Nil) - - val rootStats = mainStats(elems) - val stats = (1 until userCodeNestingLevel) - .foldLeft(rootStats)((trees, _) => mainStats(trees)) - - if (needsUsedEarlierDefinitions) { - val wrapperSym = elems.last.symbol - updateUsedEarlierDefinitions(wrapperSym, stats) - } - - stats.foreach { - case i: Import => processImport(i) - case t: tpd.DefDef => processTree(t) - case t: tpd.ValDef => processTree(t) - case t: tpd.TypeDef => processTree(t) - case _ => - } diff --git a/amm/compiler/src/main/scala-3.0.0-3.3.1/ammonite/compiler/Preprocessor.scala b/amm/compiler/src/main/scala-3.0.0-3.3.1/ammonite/compiler/Preprocessor.scala deleted file mode 100644 index 273005258..000000000 --- a/amm/compiler/src/main/scala-3.0.0-3.3.1/ammonite/compiler/Preprocessor.scala +++ /dev/null @@ -1,332 +0,0 @@ -package ammonite.compiler - -import java.util.function.{Function => JFunction} - -import ammonite.compiler.iface.{Compiler => _, Parser => _, Preprocessor => IPreprocessor, _} -import ammonite.util.{Imports, Name, Res} -import ammonite.util.Util.CodeSource -import pprint.Util - -import dotty.tools.dotc -import dotc.ast.desugar -import dotc.ast.untpd -import dotc.core.Contexts._ -import dotc.core.{Flags, Names} -import dotc.parsing.Parsers.Parser -import dotc.parsing.Tokens -import dotc.util.SourceFile - -class Preprocessor( - ctx: Context, - markGeneratedSections: Boolean -) extends IPreprocessor { - - // FIXME Quite some duplication with DefaultProcessor for Scala 2.x - - private case class Expanded(code: String, printer: Seq[String]) - - private def parse(source: String): Either[Seq[String], List[untpd.Tree]] = { - val reporter = Compiler.newStoreReporter() - val sourceFile = SourceFile.virtual("foo", source) - val parseCtx = ctx.fresh.setReporter(reporter).withSource(sourceFile) - val parser = new DottyParser(sourceFile)(using parseCtx) - val stats = parser.blockStatSeq() - parser.accept(Tokens.EOF) - if (reporter.hasErrors) { - val errorsStr = reporter - .allErrors - // .map(rendering.formatError) - .map(e => scala.util.Try(e.msg.toString).toOption.getOrElse("???")) - Left(errorsStr) - } else - Right(stats) - } - - def transform( - stmts: Seq[String], - resultIndex: String, - leadingSpaces: String, - codeSource: CodeSource, - indexedWrapper: Name, - imports: Imports, - printerTemplate: String => String, - extraCode: String, - skipEmpty: Boolean, - markScript: Boolean, - codeWrapper: CodeWrapper - ): Res[IPreprocessor.Output] = { - - // println(s"transformOrNull(${stmts.toSeq})") - - // All code Ammonite compiles must be rooted in some package within - // the `ammonite` top-level package - assert(codeSource.pkgName.head == Name("ammonite")) - - expandStatements(stmts, resultIndex, skipEmpty).map { - case Expanded(code, printer) => - val (wrappedCode, importsLength, userCodeNestingLevel) = wrapCode( - codeSource, indexedWrapper, leadingSpaces + code, - printerTemplate(printer.mkString(", ")), - imports, extraCode, markScript, codeWrapper - ) - IPreprocessor.Output(wrappedCode, importsLength, userCodeNestingLevel) - } - } - - private def expandStatements( - stmts: Seq[String], - wrapperIndex: String, - skipEmpty: Boolean - ): Res[Expanded] = - stmts match{ - // In the REPL, we do not process empty inputs at all, to avoid - // unnecessarily incrementing the command counter - // - // But in scripts, we process empty inputs and create an empty object, - // to ensure that when the time comes to cache/load the class it exists - case Nil if skipEmpty => Res.Skip - case postSplit => - Res(complete(stmts.mkString(""), wrapperIndex, postSplit)) - - } - - private def wrapCode( - codeSource: CodeSource, - indexedWrapperName: Name, - code: String, - printCode: String, - imports: Imports, - extraCode: String, - markScript: Boolean, - codeWrapper: CodeWrapper - ) = { - - //we need to normalize topWrapper and bottomWrapper in order to ensure - //the snippets always use the platform-specific newLine - val extraCode0 = - if (markScript) extraCode + "/**/" - else extraCode - val (topWrapper, bottomWrapper, userCodeNestingLevel) = - codeWrapper(code, codeSource, imports, printCode, indexedWrapperName, extraCode0) - val (topWrapper0, bottomWrapper0) = - if (markScript) (topWrapper + "/**/ /**/" + bottomWrapper) - else (topWrapper, bottomWrapper) - val importsLen = topWrapper0.length - - (topWrapper0 + code + bottomWrapper0, importsLen, userCodeNestingLevel) - } - - // Large parts of the logic below is adapted from DefaultProcessor, - // the Scala 2 counterpart of this file. - - private def isPrivate(tree: untpd.Tree): Boolean = - tree match { - case m: untpd.MemberDef => m.mods.is(Flags.Private) - case _ => false - } - - private def Processor(cond: PartialFunction[(String, String, untpd.Tree), Expanded]) = - (code: String, name: String, tree: untpd.Tree) => cond.lift(name, code, tree) - - private def pprintSignature(ident: String, customMsg: Option[String]): String = - val customCode = customMsg.fold("_root_.scala.None")(x => s"""_root_.scala.Some("$x")""") - s""" - _root_.ammonite - .repl - .ReplBridge - .value - .Internal - .print($ident, ${Util.literalize(ident)}, $customCode) - """ - private def definedStr(definitionLabel: String, name: String) = - s""" - _root_.ammonite - .repl - .ReplBridge - .value - .Internal - .printDef("$definitionLabel", ${Util.literalize(name)}) - """ - private def pprint(ident: String) = pprintSignature(ident, None) - - /** - * Processors for declarations which all have the same shape - */ - private def DefProc(definitionLabel: String)(cond: PartialFunction[untpd.Tree, Names.Name]) = - (code: String, name: String, tree: untpd.Tree) => - cond.lift(tree).map{ name => - val printer = - if (isPrivate(tree)) Nil - else - val definedName = - if name.isEmpty then "" - else Name.backtickWrap(name.decode.toString) - Seq(definedStr(definitionLabel, definedName)) - Expanded( - code, - printer - ) - } - - private val ObjectDef = DefProc("object"){case m: untpd.ModuleDef => m.name} - private val ClassDef = DefProc("class"){ - case m: untpd.TypeDef if m.isClassDef && !m.mods.flags.is(Flags.Trait) => - m.name - } - private val TraitDef = DefProc("trait"){ - case m: untpd.TypeDef if m.isClassDef && m.mods.flags.is(Flags.Trait) => - m.name - } - private val DefDef = DefProc("function"){ - case m: untpd.DefDef if m.mods.flags.is(Flags.Given) && m.name.isEmpty => - given Context = ctx - desugar.inventGivenOrExtensionName(m.tpt) - case m: untpd.DefDef => - m.name - } - - private val ExtDef = DefProc("extension methods") { - case ext: untpd.ExtMethods => Names.EmptyTermName - } - private val TypeDef = DefProc("type"){ case m: untpd.TypeDef => m.name } - - private val VarDef = Processor { case (name, code, t: untpd.ValDef) => - Expanded( - //Only wrap rhs in function if it is not a function - //Wrapping functions causes type inference errors. - code, - // Try to leave out all synthetics; we don't actually have proper - // synthetic flags right now, because we're dumb-parsing it and not putting - // it through a full compilation - if (isPrivate(t) || t.name.decode.toString.contains("$")) Nil - else if (t.mods.flags.is(Flags.Given)) { - given Context = ctx - val name0 = if (t.name.isEmpty) desugar.inventGivenOrExtensionName(t.tpt) else t.name - Seq(pprintSignature(Name.backtickWrap(name0.decode.toString), Some(""))) - } - else if (t.mods.flags.is(Flags.Lazy)) - Seq(pprintSignature(Name.backtickWrap(t.name.decode.toString), Some(""))) - else Seq(pprint(Name.backtickWrap(t.name.decode.toString))) - ) - } - - private val PatDef = Processor { case (name, code, t: untpd.PatDef) => - val isLazy = t.mods.flags.is(Flags.Lazy) - val printers = - if (isPrivate(t)) Nil - else - t.pats - .flatMap { - case untpd.Tuple(trees) => trees - case elem => List(elem) - } - .flatMap { - case untpd.Ident(name) => - val decoded = name.decode.toString - if (decoded.contains("$")) Nil - else if (isLazy) Seq(pprintSignature(Name.backtickWrap(decoded), Some(""))) - else Seq(pprint(Name.backtickWrap(decoded))) - case _ => Nil // can this happen? - } - Expanded(code, printers) - } - - private val Import = Processor { - case (name, code, tree: untpd.Import) => - val Array(keyword, body) = code.split(" ", 2) - val tq = "\"\"\"" - Expanded(code, Seq( - s""" - _root_.ammonite - .repl - .ReplBridge - .value - .Internal - .printImport(${Util.literalize(body)}) - """ - )) - } - - private val Expr = Processor { - //Expressions are lifted to anon function applications so they will be JITed - case (name, code, tree) => - val expandedCode = - if (markGeneratedSections) - s"/**/val $name = /**/$code" - else - s"val $name = $code" - Expanded( - expandedCode, - if (isPrivate(tree)) Nil else Seq(pprint(name)) - ) - } - - private val decls = Seq[(String, String, untpd.Tree) => Option[Expanded]]( - ObjectDef, ClassDef, TraitDef, DefDef, ExtDef, TypeDef, VarDef, PatDef, Import, Expr - ) - - private def complete( - code: String, - resultIndex: String, - postSplit: Seq[String] - ): Either[String, Expanded] = { - val reParsed = postSplit.map(p => (parse(p), p)) - val errors = reParsed.collect{case (Left(e), _) => e }.flatten - if (errors.length != 0) Left(errors.mkString(System.lineSeparator())) - else { - val allDecls = for { - ((Right(trees), code), i) <- reParsed.zipWithIndex if trees.nonEmpty - } yield { - // Suffix the name of the result variable with the index of - // the tree if there is more than one statement in this command - val suffix = if (reParsed.length > 1) "_" + i else "" - def handleTree(t: untpd.Tree) = { - // println(s"handleTree($t)") - val it = decls.iterator.flatMap(_.apply(code, "res" + resultIndex + suffix, t)) - if (it.hasNext) - it.next() - else { - sys.error(s"Don't know how to handle ${t.getClass}: $t") - } - } - trees match { - case Seq(tree) => handleTree(tree) - - // This handles the multi-import case `import a.b, c.d` - case trees if trees.forall(_.isInstanceOf[untpd.Import]) => handleTree(trees(0)) - - // AFAIK this can only happen for pattern-matching multi-assignment, - // which for some reason parse into a list of statements. In such a - // scenario, aggregate all their printers, but only output the code once - case trees => - val printers = for { - tree <- trees - if tree.isInstanceOf[untpd.ValDef] - Expanded(_, printers) = handleTree(tree) - printer <- printers - } yield printer - - Expanded(code, printers) - } - } - - val expanded = allDecls match{ - case Seq(first, rest@_*) => - val allDeclsWithComments = Expanded(first.code, first.printer) +: rest - allDeclsWithComments.reduce { (a, b) => - Expanded( - // We do not need to separate the code with our own semi-colons - // or newlines, as each expanded code snippet itself comes with - // it's own trailing newline/semicolons as a result of the - // initial split - a.code + b.code, - a.printer ++ b.printer - ) - } - case Nil => Expanded("", Nil) - } - - Right(expanded) - } - } -} diff --git a/amm/compiler/src/main/scala-3.3.2+/ammonite/compiler/AmmonitePhase.scala b/amm/compiler/src/main/scala-3.3.2+/ammonite/compiler/AmmonitePhase.scala deleted file mode 100644 index 333645aa7..000000000 --- a/amm/compiler/src/main/scala-3.3.2+/ammonite/compiler/AmmonitePhase.scala +++ /dev/null @@ -1,264 +0,0 @@ -package ammonite.compiler - -import ammonite.util.{ImportData, Imports, Name => AmmName, Printer, Util} - -import dotty.tools.dotc -import dotty.tools.dotc.core.StdNames.nme -import dotc.ast.Trees._ -import dotc.ast.{tpd, untpd} -import dotc.core.Flags -import dotc.core.Contexts._ -import dotc.core.Names.Name -import dotc.core.Phases.Phase -import dotc.core.Symbols.{NoSymbol, Symbol, newSymbol} -import dotc.core.Types.{TermRef, Type, TypeTraverser} - -import scala.collection.mutable - -class AmmonitePhase( - userCodeNestingLevel: => Int, - needsUsedEarlierDefinitions: => Boolean -) extends Phase: - import tpd._ - - def phaseName: String = "ammonite" - - private var myImports = new mutable.ListBuffer[(Boolean, String, String, Seq[AmmName])] - private var usedEarlierDefinitions0 = new mutable.ListBuffer[String] - - def importData: Seq[ImportData] = - val grouped = myImports - .toList - .distinct - .groupBy { case (a, b, c, d) => (b, c, d) } - .mapValues(_.map(_._1)) - - val open = for { - ((fromName, toName, importString), items) <- grouped - if !CompilerUtil.ignoredNames(fromName) - } yield { - val importType = items match{ - case Seq(true) => ImportData.Type - case Seq(false) => ImportData.Term - case Seq(_, _) => ImportData.TermType - } - - ImportData(AmmName(fromName), AmmName(toName), importString, importType) - } - - open.toVector.sortBy(x => Util.encodeScalaSourcePath(x.prefix)) - - def usedEarlierDefinitions: Seq[String] = - usedEarlierDefinitions0.toList.distinct - - private def saneSym(name: Name, sym: Symbol)(using Context): Boolean = - !name.decode.toString.contains('$') && - sym.exists && - // !sym.is(Flags.Synthetic) && - !scala.util.Try(sym.is(Flags.Private)).toOption.getOrElse(true) && - !scala.util.Try(sym.is(Flags.Protected)).toOption.getOrElse(true) && - // sym.is(Flags.Public) && - !CompilerUtil.ignoredSyms(sym.toString) && - !CompilerUtil.ignoredNames(name.decode.toString) - - private def saneSym(sym: Symbol)(using Context): Boolean = - saneSym(sym.name, sym) - - private def processTree(t: tpd.Tree)(using Context): Unit = { - val sym = t.symbol - val name = t match { - case t: tpd.ValDef => t.name - case _ => sym.name - } - if (saneSym(name, sym)) { - val name = sym.name.decode.toString - myImports.addOne((sym.isType, name, name, Nil)) - } - } - - private def processImport(i: tpd.Import)(using Context): Unit = { - val expr = i.expr - val selectors = i.selectors - - // Most of that logic was adapted from AmmonitePlugin, the Scala 2 counterpart - // of this file. - - val prefix = - val (_ :: nameListTail, symbolHead :: _) = { - def rec(expr: tpd.Tree): List[(Name, Symbol)] = { - expr match { - case s @ tpd.Select(lhs, _) => (s.symbol.name -> s.symbol) :: rec(lhs) - case i @ tpd.Ident(name) => List(name -> i.symbol) - case t @ tpd.This(pkg) => List(pkg.name -> t.symbol) - } - } - rec(expr).reverse.unzip - } - - val headFullPath = symbolHead.fullName.decode.toString.split('.') - .map(n => if (n.endsWith("$")) n.stripSuffix("$") else n) // meh - // prefix package imports with `_root_` to try and stop random - // variables from interfering with them. If someone defines a value - // called `_root_`, this will still break, but that's their problem - val rootPrefix = if(symbolHead.denot.is(Flags.Package)) Seq("_root_") else Nil - val tailPath = nameListTail.map(_.decode.toString) - - (rootPrefix ++ headFullPath ++ tailPath).map(AmmName(_)) - - def isMask(sel: untpd.ImportSelector) = sel.name != nme.WILDCARD && sel.rename == nme.WILDCARD - - val renameMap = - - /** - * A map of each name importable from `expr`, to a `Seq[Boolean]` - * containing a `true` if there's a type-symbol you can import, `false` - * if there's a non-type symbol and both if there are both type and - * non-type symbols that are importable for that name - */ - val importableIsTypes = - expr.tpe - .allMembers - .map(_.symbol) - .filter(saneSym(_)) - .groupBy(_.name.decode.toString) - .mapValues(_.map(_.isType).toVector) - - val renamings = for{ - t @ untpd.ImportSelector(name, renameTree, _) <- selectors - if !isMask(t) - // getOrElse just in case... - isType <- importableIsTypes.getOrElse(name.name.decode.toString, Nil) - Ident(rename) <- Option(renameTree) - } yield ((isType, rename.decode.toString), name.name.decode.toString) - - renamings.toMap - - - def isUnimportableUnlessRenamed(sym: Symbol): Boolean = - sym eq NoSymbol - - @scala.annotation.tailrec - def transformImport(selectors: List[untpd.ImportSelector], sym: Symbol): List[Symbol] = - selectors match { - case Nil => Nil - case sel :: Nil if sel.isWildcard => - if (isUnimportableUnlessRenamed(sym)) Nil - else List(sym) - case (sel @ untpd.ImportSelector(from, to, _)) :: _ - if from.name == (if (from.isTerm) sym.name.toTermName else sym.name.toTypeName) => - if (isMask(sel)) Nil - else List( - newSymbol(sym.owner, sel.rename, sym.flags, sym.info, sym.privateWithin, sym.coord) - ) - case _ :: rest => transformImport(rest, sym) - } - - val symNames = - for { - sym <- expr.tpe.allMembers.map(_.symbol).flatMap(transformImport(selectors, _)) - if saneSym(sym) - } yield (sym.isType, sym.name.decode.toString) - - val syms = for { - // For some reason `info.allImportedSymbols` does not show imported - // type aliases when they are imported directly e.g. - // - // import scala.reflect.macros.Context - // - // As opposed to via import scala.reflect.macros._. - // Thus we need to combine allImportedSymbols with the renameMap - (isType, sym) <- (symNames.toList ++ renameMap.keys).distinct - } yield (isType, renameMap.getOrElse((isType, sym), sym), sym, prefix) - - myImports ++= syms - } - - private def updateUsedEarlierDefinitions( - wrapperSym: Symbol, - stats: List[tpd.Tree] - )(using Context): Unit = { - /* - * We list the variables from the first wrapper - * used from the user code. - * - * E.g. if, after wrapping, the code looks like - * ``` - * class cmd2 { - * - * val cmd0 = ??? - * val cmd1 = ??? - * - * import cmd0.{ - * n - * } - * - * class Helper { - * // user-typed code - * val n0 = n + 1 - * } - * } - * ``` - * this would process the tree of `val n0 = n + 1`, find `n` as a tree like - * `cmd2.this.cmd0.n`, and put `cmd0` in `uses`. - */ - - val typeTraverser: TypeTraverser = new TypeTraverser { - def traverse(tpe: Type) = tpe match { - case tr: TermRef if tr.prefix.typeSymbol == wrapperSym => - tr.designator match { - case n: Name => usedEarlierDefinitions0 += n.decode.toString - case s: Symbol => usedEarlierDefinitions0 += s.name.decode.toString - case _ => // can this happen? - } - case _ => - traverseChildren(tpe) - } - } - - val traverser: TreeTraverser = new TreeTraverser { - def traverse(tree: Tree)(using Context) = tree match { - case tpd.Select(node, name) if node.symbol == wrapperSym => - usedEarlierDefinitions0 += name.decode.toString - case tt @ tpd.TypeTree() => - typeTraverser.traverse(tt.tpe) - case _ => - traverseChildren(tree) - } - } - - for (tree <- stats) - traverser.traverse(tree) - } - - private def unpkg(tree: tpd.Tree): List[tpd.Tree] = - tree match { - case PackageDef(_, elems) => elems.flatMap(unpkg) - case _ => List(tree) - } - - def run(using Context): Unit = - val elems = unpkg(ctx.compilationUnit.tpdTree) - def mainStats(trees: List[tpd.Tree]): List[tpd.Tree] = - trees - .reverseIterator - .collectFirst { - case TypeDef(name, rhs0: Template) => rhs0.body - } - .getOrElse(Nil) - - val rootStats = mainStats(elems) - val stats = (1 until userCodeNestingLevel) - .foldLeft(rootStats)((trees, _) => mainStats(trees)) - - if (needsUsedEarlierDefinitions) { - val wrapperSym = elems.last.symbol - updateUsedEarlierDefinitions(wrapperSym, stats) - } - - stats.foreach { - case i: Import => processImport(i) - case t: tpd.DefDef => processTree(t) - case t: tpd.ValDef => processTree(t) - case t: tpd.TypeDef => processTree(t) - case _ => - } diff --git a/amm/compiler/src/main/scala-3.3.2+/ammonite/compiler/Preprocessor.scala b/amm/compiler/src/main/scala-3.3.2+/ammonite/compiler/Preprocessor.scala deleted file mode 100644 index 273005258..000000000 --- a/amm/compiler/src/main/scala-3.3.2+/ammonite/compiler/Preprocessor.scala +++ /dev/null @@ -1,332 +0,0 @@ -package ammonite.compiler - -import java.util.function.{Function => JFunction} - -import ammonite.compiler.iface.{Compiler => _, Parser => _, Preprocessor => IPreprocessor, _} -import ammonite.util.{Imports, Name, Res} -import ammonite.util.Util.CodeSource -import pprint.Util - -import dotty.tools.dotc -import dotc.ast.desugar -import dotc.ast.untpd -import dotc.core.Contexts._ -import dotc.core.{Flags, Names} -import dotc.parsing.Parsers.Parser -import dotc.parsing.Tokens -import dotc.util.SourceFile - -class Preprocessor( - ctx: Context, - markGeneratedSections: Boolean -) extends IPreprocessor { - - // FIXME Quite some duplication with DefaultProcessor for Scala 2.x - - private case class Expanded(code: String, printer: Seq[String]) - - private def parse(source: String): Either[Seq[String], List[untpd.Tree]] = { - val reporter = Compiler.newStoreReporter() - val sourceFile = SourceFile.virtual("foo", source) - val parseCtx = ctx.fresh.setReporter(reporter).withSource(sourceFile) - val parser = new DottyParser(sourceFile)(using parseCtx) - val stats = parser.blockStatSeq() - parser.accept(Tokens.EOF) - if (reporter.hasErrors) { - val errorsStr = reporter - .allErrors - // .map(rendering.formatError) - .map(e => scala.util.Try(e.msg.toString).toOption.getOrElse("???")) - Left(errorsStr) - } else - Right(stats) - } - - def transform( - stmts: Seq[String], - resultIndex: String, - leadingSpaces: String, - codeSource: CodeSource, - indexedWrapper: Name, - imports: Imports, - printerTemplate: String => String, - extraCode: String, - skipEmpty: Boolean, - markScript: Boolean, - codeWrapper: CodeWrapper - ): Res[IPreprocessor.Output] = { - - // println(s"transformOrNull(${stmts.toSeq})") - - // All code Ammonite compiles must be rooted in some package within - // the `ammonite` top-level package - assert(codeSource.pkgName.head == Name("ammonite")) - - expandStatements(stmts, resultIndex, skipEmpty).map { - case Expanded(code, printer) => - val (wrappedCode, importsLength, userCodeNestingLevel) = wrapCode( - codeSource, indexedWrapper, leadingSpaces + code, - printerTemplate(printer.mkString(", ")), - imports, extraCode, markScript, codeWrapper - ) - IPreprocessor.Output(wrappedCode, importsLength, userCodeNestingLevel) - } - } - - private def expandStatements( - stmts: Seq[String], - wrapperIndex: String, - skipEmpty: Boolean - ): Res[Expanded] = - stmts match{ - // In the REPL, we do not process empty inputs at all, to avoid - // unnecessarily incrementing the command counter - // - // But in scripts, we process empty inputs and create an empty object, - // to ensure that when the time comes to cache/load the class it exists - case Nil if skipEmpty => Res.Skip - case postSplit => - Res(complete(stmts.mkString(""), wrapperIndex, postSplit)) - - } - - private def wrapCode( - codeSource: CodeSource, - indexedWrapperName: Name, - code: String, - printCode: String, - imports: Imports, - extraCode: String, - markScript: Boolean, - codeWrapper: CodeWrapper - ) = { - - //we need to normalize topWrapper and bottomWrapper in order to ensure - //the snippets always use the platform-specific newLine - val extraCode0 = - if (markScript) extraCode + "/**/" - else extraCode - val (topWrapper, bottomWrapper, userCodeNestingLevel) = - codeWrapper(code, codeSource, imports, printCode, indexedWrapperName, extraCode0) - val (topWrapper0, bottomWrapper0) = - if (markScript) (topWrapper + "/**/ /**/" + bottomWrapper) - else (topWrapper, bottomWrapper) - val importsLen = topWrapper0.length - - (topWrapper0 + code + bottomWrapper0, importsLen, userCodeNestingLevel) - } - - // Large parts of the logic below is adapted from DefaultProcessor, - // the Scala 2 counterpart of this file. - - private def isPrivate(tree: untpd.Tree): Boolean = - tree match { - case m: untpd.MemberDef => m.mods.is(Flags.Private) - case _ => false - } - - private def Processor(cond: PartialFunction[(String, String, untpd.Tree), Expanded]) = - (code: String, name: String, tree: untpd.Tree) => cond.lift(name, code, tree) - - private def pprintSignature(ident: String, customMsg: Option[String]): String = - val customCode = customMsg.fold("_root_.scala.None")(x => s"""_root_.scala.Some("$x")""") - s""" - _root_.ammonite - .repl - .ReplBridge - .value - .Internal - .print($ident, ${Util.literalize(ident)}, $customCode) - """ - private def definedStr(definitionLabel: String, name: String) = - s""" - _root_.ammonite - .repl - .ReplBridge - .value - .Internal - .printDef("$definitionLabel", ${Util.literalize(name)}) - """ - private def pprint(ident: String) = pprintSignature(ident, None) - - /** - * Processors for declarations which all have the same shape - */ - private def DefProc(definitionLabel: String)(cond: PartialFunction[untpd.Tree, Names.Name]) = - (code: String, name: String, tree: untpd.Tree) => - cond.lift(tree).map{ name => - val printer = - if (isPrivate(tree)) Nil - else - val definedName = - if name.isEmpty then "" - else Name.backtickWrap(name.decode.toString) - Seq(definedStr(definitionLabel, definedName)) - Expanded( - code, - printer - ) - } - - private val ObjectDef = DefProc("object"){case m: untpd.ModuleDef => m.name} - private val ClassDef = DefProc("class"){ - case m: untpd.TypeDef if m.isClassDef && !m.mods.flags.is(Flags.Trait) => - m.name - } - private val TraitDef = DefProc("trait"){ - case m: untpd.TypeDef if m.isClassDef && m.mods.flags.is(Flags.Trait) => - m.name - } - private val DefDef = DefProc("function"){ - case m: untpd.DefDef if m.mods.flags.is(Flags.Given) && m.name.isEmpty => - given Context = ctx - desugar.inventGivenOrExtensionName(m.tpt) - case m: untpd.DefDef => - m.name - } - - private val ExtDef = DefProc("extension methods") { - case ext: untpd.ExtMethods => Names.EmptyTermName - } - private val TypeDef = DefProc("type"){ case m: untpd.TypeDef => m.name } - - private val VarDef = Processor { case (name, code, t: untpd.ValDef) => - Expanded( - //Only wrap rhs in function if it is not a function - //Wrapping functions causes type inference errors. - code, - // Try to leave out all synthetics; we don't actually have proper - // synthetic flags right now, because we're dumb-parsing it and not putting - // it through a full compilation - if (isPrivate(t) || t.name.decode.toString.contains("$")) Nil - else if (t.mods.flags.is(Flags.Given)) { - given Context = ctx - val name0 = if (t.name.isEmpty) desugar.inventGivenOrExtensionName(t.tpt) else t.name - Seq(pprintSignature(Name.backtickWrap(name0.decode.toString), Some(""))) - } - else if (t.mods.flags.is(Flags.Lazy)) - Seq(pprintSignature(Name.backtickWrap(t.name.decode.toString), Some(""))) - else Seq(pprint(Name.backtickWrap(t.name.decode.toString))) - ) - } - - private val PatDef = Processor { case (name, code, t: untpd.PatDef) => - val isLazy = t.mods.flags.is(Flags.Lazy) - val printers = - if (isPrivate(t)) Nil - else - t.pats - .flatMap { - case untpd.Tuple(trees) => trees - case elem => List(elem) - } - .flatMap { - case untpd.Ident(name) => - val decoded = name.decode.toString - if (decoded.contains("$")) Nil - else if (isLazy) Seq(pprintSignature(Name.backtickWrap(decoded), Some(""))) - else Seq(pprint(Name.backtickWrap(decoded))) - case _ => Nil // can this happen? - } - Expanded(code, printers) - } - - private val Import = Processor { - case (name, code, tree: untpd.Import) => - val Array(keyword, body) = code.split(" ", 2) - val tq = "\"\"\"" - Expanded(code, Seq( - s""" - _root_.ammonite - .repl - .ReplBridge - .value - .Internal - .printImport(${Util.literalize(body)}) - """ - )) - } - - private val Expr = Processor { - //Expressions are lifted to anon function applications so they will be JITed - case (name, code, tree) => - val expandedCode = - if (markGeneratedSections) - s"/**/val $name = /**/$code" - else - s"val $name = $code" - Expanded( - expandedCode, - if (isPrivate(tree)) Nil else Seq(pprint(name)) - ) - } - - private val decls = Seq[(String, String, untpd.Tree) => Option[Expanded]]( - ObjectDef, ClassDef, TraitDef, DefDef, ExtDef, TypeDef, VarDef, PatDef, Import, Expr - ) - - private def complete( - code: String, - resultIndex: String, - postSplit: Seq[String] - ): Either[String, Expanded] = { - val reParsed = postSplit.map(p => (parse(p), p)) - val errors = reParsed.collect{case (Left(e), _) => e }.flatten - if (errors.length != 0) Left(errors.mkString(System.lineSeparator())) - else { - val allDecls = for { - ((Right(trees), code), i) <- reParsed.zipWithIndex if trees.nonEmpty - } yield { - // Suffix the name of the result variable with the index of - // the tree if there is more than one statement in this command - val suffix = if (reParsed.length > 1) "_" + i else "" - def handleTree(t: untpd.Tree) = { - // println(s"handleTree($t)") - val it = decls.iterator.flatMap(_.apply(code, "res" + resultIndex + suffix, t)) - if (it.hasNext) - it.next() - else { - sys.error(s"Don't know how to handle ${t.getClass}: $t") - } - } - trees match { - case Seq(tree) => handleTree(tree) - - // This handles the multi-import case `import a.b, c.d` - case trees if trees.forall(_.isInstanceOf[untpd.Import]) => handleTree(trees(0)) - - // AFAIK this can only happen for pattern-matching multi-assignment, - // which for some reason parse into a list of statements. In such a - // scenario, aggregate all their printers, but only output the code once - case trees => - val printers = for { - tree <- trees - if tree.isInstanceOf[untpd.ValDef] - Expanded(_, printers) = handleTree(tree) - printer <- printers - } yield printer - - Expanded(code, printers) - } - } - - val expanded = allDecls match{ - case Seq(first, rest@_*) => - val allDeclsWithComments = Expanded(first.code, first.printer) +: rest - allDeclsWithComments.reduce { (a, b) => - Expanded( - // We do not need to separate the code with our own semi-colons - // or newlines, as each expanded code snippet itself comes with - // it's own trailing newline/semicolons as a result of the - // initial split - a.code + b.code, - a.printer ++ b.printer - ) - } - case Nil => Expanded("", Nil) - } - - Right(expanded) - } - } -} diff --git a/amm/compiler/src/main/scala-3.4.2+/ammonite/compiler/AmmonitePhase.scala b/amm/compiler/src/main/scala-3/ammonite/compiler/AmmonitePhase.scala similarity index 100% rename from amm/compiler/src/main/scala-3.4.2+/ammonite/compiler/AmmonitePhase.scala rename to amm/compiler/src/main/scala-3/ammonite/compiler/AmmonitePhase.scala diff --git a/amm/compiler/src/main/scala-3.4.2+/ammonite/compiler/Preprocessor.scala b/amm/compiler/src/main/scala-3/ammonite/compiler/Preprocessor.scala similarity index 100% rename from amm/compiler/src/main/scala-3.4.2+/ammonite/compiler/Preprocessor.scala rename to amm/compiler/src/main/scala-3/ammonite/compiler/Preprocessor.scala