aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-04-17 09:48:22 +0200
committerMartin Odersky <odersky@gmail.com>2013-04-17 10:16:22 +0200
commitca8dc7ada663e44aafe470944dd17256dbde151c (patch)
treed15939e204042e358e0c83064250f1f18c1c4f25 /src/dotty/tools/dotc
parente32fedb6844eab11a27e365a570b2033a0f6f78d (diff)
downloaddotty-ca8dc7ada663e44aafe470944dd17256dbde151c.tar.gz
dotty-ca8dc7ada663e44aafe470944dd17256dbde151c.tar.bz2
dotty-ca8dc7ada663e44aafe470944dd17256dbde151c.zip
Scanners added.
Moving Positions, Chars to new packages. Added Source positions. Added untyped trees module. Factored out behavior between typed and untyped trees.
Diffstat (limited to 'src/dotty/tools/dotc')
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala1
-rw-r--r--src/dotty/tools/dotc/core/Annotations.scala5
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala9
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala3
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala6
-rw-r--r--src/dotty/tools/dotc/core/SymbolLoaders.scala2
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala2
-rw-r--r--src/dotty/tools/dotc/core/Trees.scala184
-rw-r--r--src/dotty/tools/dotc/core/TypedTrees.scala79
-rw-r--r--src/dotty/tools/dotc/core/UntypedTrees.scala13
-rw-r--r--src/dotty/tools/dotc/core/pickling/ClassfileParser.scala2
-rw-r--r--src/dotty/tools/dotc/core/pickling/UnPickler.scala2
-rw-r--r--src/dotty/tools/dotc/parsing/CharArrayReader.scala131
-rw-r--r--src/dotty/tools/dotc/parsing/Scanners.scala958
-rw-r--r--src/dotty/tools/dotc/parsing/Tokens.scala171
-rw-r--r--src/dotty/tools/dotc/reporting/ConsoleReporter.scala14
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala30
-rw-r--r--src/dotty/tools/dotc/reporting/StoreReporter.scala6
-rw-r--r--src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala6
-rw-r--r--src/dotty/tools/dotc/util/Chars.scala (renamed from src/dotty/tools/dotc/core/Chars.scala)11
-rw-r--r--src/dotty/tools/dotc/util/FreshNameCreator.scala38
-rw-r--r--src/dotty/tools/dotc/util/Positions.scala (renamed from src/dotty/tools/dotc/core/Positions.scala)22
-rw-r--r--src/dotty/tools/dotc/util/SourceFile.scala109
25 files changed, 1690 insertions, 117 deletions
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index 22e8b99c8..4b583be9d 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -66,7 +66,6 @@ class ScalaSettings extends Settings.SettingGroup {
val logFreeTypes = BooleanSetting("-Xlog-free-types", "Print a message when reification resorts to generating a free type.")
val maxClassfileName = IntSetting("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, 72 to 255)
val Xmigration28 = BooleanSetting("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8.")
- val nouescape = BooleanSetting("-Xno-uescape", "Disable handling of \\u unicode escapes.")
val Xnojline = BooleanSetting("-Xnojline", "Do not use JLine for editing.")
val Xverify = BooleanSetting("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)")
val plugin = MultiStringSetting("-Xplugin", "file", "Load one or more plugins from files.")
diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala
index 98ce4fffd..9086047cd 100644
--- a/src/dotty/tools/dotc/core/Annotations.scala
+++ b/src/dotty/tools/dotc/core/Annotations.scala
@@ -1,6 +1,7 @@
-package dotty.tools.dotc.core
+package dotty.tools.dotc
+package core
-import Symbols._, Types._, Positions._, Contexts._, Constants._, TypedTrees.tpd._
+import Symbols._, Types._, util.Positions._, Contexts._, Constants._, TypedTrees.tpd._
object Annotations {
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index 29da40caf..15f1e80c5 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -8,8 +8,8 @@ import Names._
import Phases._
import Types._
import Symbols._
-import TypeComparers._, Printers._, NameOps._, SymDenotations._, Positions._
-import TypedTrees.tpd._
+import TypeComparers._, Printers._, NameOps._, SymDenotations._, util.Positions._
+import TypedTrees.tpd._, util.FreshNameCreator
import config.Settings._
import config.ScalaSettings
import reporting._
@@ -172,7 +172,7 @@ object Contexts {
/** The current source file; will be derived from current
* compilation unit.
*/
- def source = io.NoSource // for now
+ def source = util.NoSource // for now
/** Does current phase use an erased types interpretation? */
def erasedTypes: Boolean = phase.erasedTypes
@@ -289,6 +289,9 @@ object Contexts {
/** The platform */
val platform: Platform = new JavaPlatform
+ /** The standard fresh name creator */
+ val fresh = new FreshNameCreator.Default
+
/** The loader that loads the members of _root_ */
def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root)
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index f62acb015..89d785997 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -3,7 +3,7 @@ package dotc
package core
import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._
-import Flags._, Scopes._, Decorators._, NameOps._, Positions._
+import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._
import scala.annotation.{ switch, meta }
import scala.collection.{ mutable, immutable }
import PartialFunction._
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index 60ac3fba9..b31cf41d5 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -2,11 +2,12 @@ package dotty.tools.dotc
package core
import java.security.MessageDigest
-import Chars.isOperatorPart
import scala.annotation.switch
import scala.io.Codec
import Names._, StdNames._, Contexts._, Symbols._, Flags._
import Decorators.StringDecorator
+import dotty.tools.dotc.util.Chars
+import Chars.isOperatorPart
object NameOps {
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index f5c3fb845..e4311af6c 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -240,7 +240,6 @@ object StdNames {
val SETTER_SUFFIX: N = encode("_=")
val SKOLEM: N = "<skolem>"
val SPECIALIZED_INSTANCE: N = "specInstance$"
- val STAR: N = "*"
val THIS: N = "_$this"
val HK_PARAM_PREFIX: N = "_$hk$"
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index a14f28ce4..b4cc9091c 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -577,6 +577,12 @@ object SymDenotations {
final def symbolicRef(implicit ctx: Context): TypeRef =
TypeRef.withSym(owner.thisType, symbol.asType)
+ /** The termref pointing to this termsymbol
+ * @throws ClassCastException is this is not a term
+ */
+ def termRef(implicit ctx: Context): TermRef =
+ TermRef.withSym(owner.thisType, symbol.asTerm)
+
/** The variance of this type parameter as an Int, with
* +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter
*/
diff --git a/src/dotty/tools/dotc/core/SymbolLoaders.scala b/src/dotty/tools/dotc/core/SymbolLoaders.scala
index e2c5f9774..dc65b77f4 100644
--- a/src/dotty/tools/dotc/core/SymbolLoaders.scala
+++ b/src/dotty/tools/dotc/core/SymbolLoaders.scala
@@ -10,7 +10,7 @@ package core
import java.io.IOException
import scala.compat.Platform.currentTime
import dotty.tools.io.{ ClassPath, AbstractFile }
-import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, Positions._, Names._
+import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, util.Positions._, Names._
import StdNames._
import Decorators.StringDecorator
import pickling.ClassfileParser
diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala
index dced9823d..c61b7d791 100644
--- a/src/dotty/tools/dotc/core/Symbols.scala
+++ b/src/dotty/tools/dotc/core/Symbols.scala
@@ -11,7 +11,7 @@ import Decorators._
import Symbols._
import Contexts._
import SymDenotations._, util.Texts._
-import Types._, Annotations._, Positions._, StdNames._, Trees._, NameOps._
+import Types._, Annotations._, util.Positions._, StdNames._, Trees._, NameOps._
import Denotations.{ Denotation, SingleDenotation, MultiDenotation }
import collection.mutable
import io.AbstractFile
diff --git a/src/dotty/tools/dotc/core/Trees.scala b/src/dotty/tools/dotc/core/Trees.scala
index 922bf6055..c485eddc1 100644
--- a/src/dotty/tools/dotc/core/Trees.scala
+++ b/src/dotty/tools/dotc/core/Trees.scala
@@ -1,6 +1,7 @@
-package dotty.tools.dotc.core
+package dotty.tools.dotc
+package core
-import Types._, Names._, Flags._, Positions._, Contexts._, Constants._, SymDenotations._, Symbols._
+import Types._, Names._, Flags._, util.Positions._, Contexts._, Constants._, SymDenotations._, Symbols._
import Denotations._, StdNames._
import annotation.tailrec
import language.higherKinds
@@ -110,6 +111,7 @@ object Trees {
override def getMessage: String = s"type of $tree is not assigned"
}
+ type Untyped = Null
type TypedTree = Tree[Type]
type UntypedTree = Tree[Nothing]
@@ -532,6 +534,9 @@ object Trees {
extends NameTree[Nothing] with DefTree[Nothing] {
type ThisTree[T] <: NameTree[T] with DefTree[T] with ModuleDef
val pos = cpos union impl.pos
+ def derivedModuleDef(mods: Modifiers[Nothing], name: TermName, impl: Template[Nothing]) =
+ if (mods == this.mods && name == this.name && (impl eq this.impl)) this
+ else ModuleDef(mods, name, impl)
}
/** (vparams) => body */
@@ -541,6 +546,78 @@ object Trees {
val pos = unionPos(cpos union body.pos, vparams)
}
+ /** Something in parentheses */
+ case class Parens(trees: List[Tree[Nothing]])(implicit cpos: Position) extends Tree[Nothing] {
+ type ThisType[T] <: Parens
+ val pos = unionPos(cpos, trees)
+ }
+
+ // ----- Generic Tree Instances, inherited from `tpt` and `untpd`.
+
+ abstract class Instance[T] {
+
+ type Modifiers = Trees.Modifiers[T]
+ type Tree = Trees.Tree[T]
+ type TypTree = Trees.TypTree[T]
+ type TermTree = Trees.TermTree[T]
+ type PatternTree = Trees.PatternTree[T]
+ type DenotingTree = Trees.DenotingTree[T]
+ type ProxyTree = Trees.ProxyTree[T]
+ type NameTree = Trees.NameTree[T]
+ type RefTree = Trees.RefTree[T]
+ type DefTree = Trees.DefTree[T]
+
+ type TreeCopier = Trees.TreeCopier[T]
+ type TreeAccumulator[U] = Trees.TreeAccumulator[U, T]
+ type TreeTransformer = Trees.TreeTransformer[T]
+
+ type Ident = Trees.Ident[T]
+ type Select = Trees.Select[T]
+ type This = Trees.This[T]
+ type Super = Trees.Super[T]
+ type Apply = Trees.Apply[T]
+ type TypeApply = Trees.TypeApply[T]
+ type Literal = Trees.Literal[T]
+ type New = Trees.New[T]
+ type Pair = Trees.Pair[T]
+ type Typed = Trees.Typed[T]
+ type NamedArg = Trees.NamedArg[T]
+ type Assign = Trees.Assign[T]
+ type Block = Trees.Block[T]
+ type If = Trees.If[T]
+ type Match = Trees.Match[T]
+ type CaseDef = Trees.CaseDef[T]
+ type Return = Trees.Return[T]
+ type Try = Trees.Try[T]
+ type Throw = Trees.Throw[T]
+ type SeqLiteral = Trees.SeqLiteral[T]
+ type TypeTree = Trees.TypeTree[T]
+ type SingletonTypeTree = Trees.SingletonTypeTree[T]
+ type SelectFromTypeTree = Trees.SelectFromTypeTree[T]
+ type AndTypeTree = Trees.AndTypeTree[T]
+ type OrTypeTree = Trees.OrTypeTree[T]
+ type RefineTypeTree = Trees.RefineTypeTree[T]
+ type AppliedTypeTree = Trees.AppliedTypeTree[T]
+ type TypeBoundsTree = Trees.TypeBoundsTree[T]
+ type Bind = Trees.Bind[T]
+ type Alternative = Trees.Alternative[T]
+ type UnApply = Trees.UnApply[T]
+ type ValDef = Trees.ValDef[T]
+ type DefDef = Trees.DefDef[T]
+ type TypeDef = Trees.TypeDef[T]
+ type Template = Trees.Template[T]
+ type ClassDef = Trees.ClassDef[T]
+ type Import = Trees.Import[T]
+ type PackageDef = Trees.PackageDef[T]
+ type Annotated = Trees.Annotated[T]
+ type EmptyTree = Trees.EmptyTree[T]
+ type SharedTree = Trees.SharedTree[T]
+
+ protected implicit def pos(implicit ctx: Context): Position = ctx.position
+
+ def defPos(sym: Symbol)(implicit ctx: Context) = ctx.position union sym.coord.toPosition
+ }
+
// ----- Helper functions and classes ---------------------------------------
@tailrec final def unionPos(base: Position, trees: List[Tree[_]]): Position = trees match {
@@ -712,7 +789,7 @@ object Trees {
}
}
- abstract class TreeTransformer[T, C] {
+ abstract class FullTreeTransformer[T, C] {
var sharedMemo: Map[SharedTree[T], SharedTree[T]] = Map()
def transform(tree: Tree[T], c: C): Tree[T] = tree match {
@@ -861,6 +938,107 @@ object Trees {
def finishSharedTree(tree: Tree[T], old: Tree[T], c: C, plugins: Plugins) = tree
}
+ abstract class TreeTransformer[T] {
+ var sharedMemo: Map[SharedTree[T], SharedTree[T]] = Map()
+
+ def transform(tree: Tree[T]): Tree[T] = tree match {
+ case Ident(name) =>
+ tree
+ case Select(qualifier, name) =>
+ tree.derivedSelect(transform(qualifier), name)
+ case This(qual) =>
+ tree
+ case Super(qual, mix) =>
+ tree.derivedSuper(transform(qual), mix)
+ case Apply(fun, args) =>
+ tree.derivedApply(transform(fun), transform(args))
+ case TypeApply(fun, args) =>
+ tree.derivedTypeApply(transform(fun), transform(args))
+ case Literal(const) =>
+ tree
+ case New(tpt) =>
+ tree.derivedNew(transform(tpt))
+ case Pair(left, right) =>
+ tree.derivedPair(transform(left), transform(right))
+ case Typed(expr, tpt) =>
+ tree.derivedTyped(transform(expr), transform(tpt))
+ case NamedArg(name, arg) =>
+ tree.derivedNamedArg(name, transform(arg))
+ case Assign(lhs, rhs) =>
+ tree.derivedAssign(transform(lhs), transform(rhs))
+ case Block(stats, expr) =>
+ tree.derivedBlock(transform(stats), transform(expr))
+ case If(cond, thenp, elsep) =>
+ tree.derivedIf(transform(cond), transform(thenp), transform(elsep))
+ case Match(selector, cases) =>
+ tree.derivedMatch(transform(selector), transformSub(cases))
+ case CaseDef(pat, guard, body) =>
+ tree.derivedCaseDef(transform(pat), transform(guard), transform(body))
+ case Return(expr, from) =>
+ tree.derivedReturn(transform(expr), transformSub(from))
+ case Try(block, catches, finalizer) =>
+ tree.derivedTry(transform(block), transformSub(catches), transform(finalizer))
+ case Throw(expr) =>
+ tree.derivedThrow(transform(expr))
+ case SeqLiteral(elemtpt, elems) =>
+ tree.derivedSeqLiteral(transform(elemtpt), transform(elems))
+ case TypeTree(original) =>
+ tree.derivedTypeTree(transform(original))
+ case SingletonTypeTree(ref) =>
+ tree.derivedSingletonTypeTree(transform(ref))
+ case SelectFromTypeTree(qualifier, name) =>
+ tree.derivedSelectFromTypeTree(transform(qualifier), name)
+ case AndTypeTree(left, right) =>
+ tree.derivedAndTypeTree(transform(left), transform(right))
+ case OrTypeTree(left, right) =>
+ tree.derivedOrTypeTree(transform(left), transform(right))
+ case RefineTypeTree(tpt, refinements) =>
+ tree.derivedRefineTypeTree(transform(tpt), transformSub(refinements))
+ case AppliedTypeTree(tpt, args) =>
+ tree.derivedAppliedTypeTree(transform(tpt), transform(args))
+ case TypeBoundsTree(lo, hi) =>
+ tree.derivedTypeBoundsTree(transform(lo), transform(hi))
+ case Bind(name, body) =>
+ tree.derivedBind(name, transform(body))
+ case Alternative(trees) =>
+ tree.derivedAlternative(transform(trees))
+ case UnApply(fun, args) =>
+ tree.derivedUnApply(transform(fun), transform(args))
+ case ValDef(mods, name, tpt, rhs) =>
+ tree.derivedValDef(mods, name, transform(tpt), transform(rhs))
+ case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ tree.derivedDefDef(mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(rhs))
+ case TypeDef(mods, name, rhs) =>
+ tree.derivedTypeDef(mods, name, transform(rhs))
+ case Template(parents, self, body) =>
+ tree.derivedTemplate(transform(parents), transformSub(self), transform(body))
+ case ClassDef(mods, name, tparams, impl) =>
+ tree.derivedClassDef(mods, name, transformSub(tparams), transformSub(impl))
+ case Import(expr, selectors) =>
+ tree.derivedImport(transform(expr), selectors)
+ case PackageDef(pid, stats) =>
+ tree.derivedPackageDef(transformSub(pid), transform(stats))
+ case Annotated(annot, arg) =>
+ tree.derivedAnnotated(transform(annot), transform(arg))
+ case EmptyTree() =>
+ tree
+ case tree @ SharedTree(shared) =>
+ sharedMemo get tree match {
+ case Some(tree1) => tree1
+ case None =>
+ val tree1 = tree.derivedSharedTree(transform(shared))
+ sharedMemo = sharedMemo.updated(tree, tree1)
+ tree1
+ }
+ }
+ def transform(trees: List[Tree[T]]): List[Tree[T]] =
+ trees mapConserve (transform(_))
+ def transformSub(tree: Tree[T]): tree.ThisTree[T] =
+ transform(tree).asInstanceOf[tree.ThisTree[T]]
+ def transformSub[TT <: Tree[T]](trees: List[TT]): List[TT] =
+ transform(trees).asInstanceOf[List[TT]]
+ }
+
abstract class TreeAccumulator[T, U] extends ((T, Tree[U]) => T) {
var sharedMemo: Map[SharedTree[U], T] = Map()
def apply(x: T, tree: Tree[U]): T
diff --git a/src/dotty/tools/dotc/core/TypedTrees.scala b/src/dotty/tools/dotc/core/TypedTrees.scala
index 56ecc7c98..414cd6e2f 100644
--- a/src/dotty/tools/dotc/core/TypedTrees.scala
+++ b/src/dotty/tools/dotc/core/TypedTrees.scala
@@ -1,73 +1,12 @@
package dotty.tools.dotc
package core
-import Positions._, Types._, Contexts._, Constants._, Names._, Flags._
+import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._
import SymDenotations._, Symbols._, StdNames._, Annotations._
object TypedTrees {
- object tpd {
-
- type Modifiers = Trees.Modifiers[Type]
- type Tree = Trees.Tree[Type]
- type TypTree = Trees.TypTree[Type]
- type TermTree = Trees.TermTree[Type]
- type PatternTree = Trees.PatternTree[Type]
- type DenotingTree = Trees.DenotingTree[Type]
- type ProxyTree = Trees.ProxyTree[Type]
- type NameTree = Trees.NameTree[Type]
- type RefTree = Trees.RefTree[Type]
- type DefTree = Trees.DefTree[Type]
-
- type TreeCopier = Trees.TreeCopier[Type]
- type TreeAccumulator[T] = Trees.TreeAccumulator[T, Type]
- type TreeTransformer[C] = Trees.TreeTransformer[Type, C]
-
- type Ident = Trees.Ident[Type]
- type Select = Trees.Select[Type]
- type This = Trees.This[Type]
- type Super = Trees.Super[Type]
- type Apply = Trees.Apply[Type]
- type TypeApply = Trees.TypeApply[Type]
- type Literal = Trees.Literal[Type]
- type New = Trees.New[Type]
- type Pair = Trees.Pair[Type]
- type Typed = Trees.Typed[Type]
- type NamedArg = Trees.NamedArg[Type]
- type Assign = Trees.Assign[Type]
- type Block = Trees.Block[Type]
- type If = Trees.If[Type]
- type Match = Trees.Match[Type]
- type CaseDef = Trees.CaseDef[Type]
- type Return = Trees.Return[Type]
- type Try = Trees.Try[Type]
- type Throw = Trees.Throw[Type]
- type SeqLiteral = Trees.SeqLiteral[Type]
- type TypeTree = Trees.TypeTree[Type]
- type SingletonTypeTree = Trees.SingletonTypeTree[Type]
- type SelectFromTypeTree = Trees.SelectFromTypeTree[Type]
- type AndTypeTree = Trees.AndTypeTree[Type]
- type OrTypeTree = Trees.OrTypeTree[Type]
- type RefineTypeTree = Trees.RefineTypeTree[Type]
- type AppliedTypeTree = Trees.AppliedTypeTree[Type]
- type TypeBoundsTree = Trees.TypeBoundsTree[Type]
- type Bind = Trees.Bind[Type]
- type Alternative = Trees.Alternative[Type]
- type UnApply = Trees.UnApply[Type]
- type ValDef = Trees.ValDef[Type]
- type DefDef = Trees.DefDef[Type]
- type TypeDef = Trees.TypeDef[Type]
- type Template = Trees.Template[Type]
- type ClassDef = Trees.ClassDef[Type]
- type Import = Trees.Import[Type]
- type PackageDef = Trees.PackageDef[Type]
- type Annotated = Trees.Annotated[Type]
- type EmptyTree = Trees.EmptyTree[Type]
- type SharedTree = Trees.SharedTree[Type]
-
- private implicit def pos(implicit ctx: Context): Position = ctx.position
-
- def defPos(sym: Symbol)(implicit ctx: Context) = ctx.position union sym.coord.toPosition
+ object tpd extends Trees.Instance[Type] {
def Modifiers(sym: Symbol)(implicit ctx: Context): Modifiers = Trees.Modifiers[Type](
sym.flags & ModifierFlags,
@@ -664,8 +603,8 @@ object TypedTrees {
new TreeMapper(ownerMap = (sym => if (sym == from) to else sym)).apply(tree)
}
- class TreeMapper(val typeMap: TypeMap = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity)(implicit ctx: Context) extends TreeTransformer[Type, Unit] {
- override def transform(tree: tpd.Tree, c: Unit): tpd.Tree = {
+ class TreeMapper(val typeMap: TypeMap = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity)(implicit ctx: Context) extends TreeTransformer[Type] {
+ override def transform(tree: tpd.Tree): tpd.Tree = {
val tree1 =
if (tree.isEmpty) tree
else tree.withType(typeMap(tree.tpe))
@@ -681,16 +620,16 @@ object TypedTrees {
case _ =>
tree1
}
- super.transform(tree2, c)
+ super.transform(tree2)
}
- override def transform(trees: List[tpd.Tree], c: Unit) = {
+ override def transform(trees: List[tpd.Tree]) = {
val locals = localSyms(trees)
val mapped = ctx.mapSymbols(locals, typeMap, ownerMap)
- if (locals eq mapped) super.transform(trees, c)
- else withSubstitution(locals, mapped).transform(trees, c)
+ if (locals eq mapped) super.transform(trees)
+ else withSubstitution(locals, mapped).transform(trees)
}
- def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree, ()).asInstanceOf[ThisTree]
+ def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree]
def apply(annot: Annotation): Annotation = {
val tree1 = apply(annot.tree)
diff --git a/src/dotty/tools/dotc/core/UntypedTrees.scala b/src/dotty/tools/dotc/core/UntypedTrees.scala
new file mode 100644
index 000000000..c882b4982
--- /dev/null
+++ b/src/dotty/tools/dotc/core/UntypedTrees.scala
@@ -0,0 +1,13 @@
+package dotty.tools.dotc
+package core
+
+import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._
+import SymDenotations._, Symbols._, StdNames._, Annotations._
+
+object UntypedTrees {
+
+ object untpd extends Trees.Instance[Nothing] {
+ }
+
+}
+
diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
index 4ba4842e1..02ee9d9cc 100644
--- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
+++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
@@ -4,7 +4,7 @@ package core
package pickling
import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._
-import SymDenotations._, UnPickler._, Constants._, Annotations._, Positions._
+import SymDenotations._, UnPickler._, Constants._, Annotations._, util.Positions._
import TypedTrees.tpd._
import java.io.{ File, IOException }
import java.lang.Integer.toHexString
diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
index 3168bb37c..ee8d9de83 100644
--- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala
+++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
@@ -9,7 +9,7 @@ import java.lang.Double.longBitsToDouble
import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._
import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._
-import Positions._, TypedTrees.tpd._, TypedTrees.TreeOps
+import util.Positions._, TypedTrees.tpd._, TypedTrees.TreeOps
import util.Texts._
import io.AbstractFile
import scala.reflect.internal.pickling.PickleFormat._
diff --git a/src/dotty/tools/dotc/parsing/CharArrayReader.scala b/src/dotty/tools/dotc/parsing/CharArrayReader.scala
new file mode 100644
index 000000000..29346b78a
--- /dev/null
+++ b/src/dotty/tools/dotc/parsing/CharArrayReader.scala
@@ -0,0 +1,131 @@
+package dotty.tools
+package dotc
+package parsing
+
+import scala.reflect.internal.Chars._
+
+abstract class CharArrayReader { self =>
+
+ val buf: Array[Char]
+
+ /** Switch whether unicode should be decoded */
+ protected def decodeUni: Boolean = true
+
+ /** An error routine to call on bad unicode escapes \\uxxxx. */
+ protected def error(msg: String, offset: Int): Unit
+
+ /** the last read character */
+ var ch: Char = _
+
+ /** The offset one past the last read character */
+ var charOffset: Int = 0
+
+ /** The offset before the last read character */
+ var lastCharOffset: Int = 0
+
+ /** The start offset of the current line */
+ var lineStartOffset: Int = 0
+
+ /** The start offset of the line before the current one */
+ var lastLineStartOffset: Int = 0
+
+ private var lastUnicodeOffset = -1
+
+ /** Is last character a unicode escape \\uxxxx? */
+ def isUnicodeEscape = charOffset == lastUnicodeOffset
+
+ /** Advance one character; reducing CR;LF pairs to just LF */
+ final def nextChar(): Unit = {
+ val idx = charOffset
+ lastCharOffset = idx
+ if (idx >= buf.length) {
+ ch = SU
+ } else {
+ val c = buf(idx)
+ ch = c
+ charOffset = idx + 1
+ if (c == '\\') potentialUnicode()
+ else if (c < ' ') { skipCR(); potentialLineEnd() }
+ }
+ }
+
+ def getc() = { nextChar() ; ch }
+
+ /** Advance one character, leaving CR;LF pairs intact.
+ * This is for use in multi-line strings, so there are no
+ * "potential line ends" here.
+ */
+ final def nextRawChar(): Unit = {
+ val idx = charOffset
+ lastCharOffset = idx
+ if (idx >= buf.length) {
+ ch = SU
+ } else {
+ val c = buf(charOffset)
+ ch = c
+ charOffset = idx + 1
+ if (c == '\\') potentialUnicode()
+ }
+ }
+
+ /** Interpret \\uxxxx escapes */
+ private def potentialUnicode() {
+ def evenSlashPrefix: Boolean = {
+ var p = charOffset - 2
+ while (p >= 0 && buf(p) == '\\') p -= 1
+ (charOffset - p) % 2 == 0
+ }
+ def udigit: Int = {
+ if (charOffset >= buf.length) {
+ // Since the positioning code is very insistent about throwing exceptions,
+ // we have to decrement the position so our error message can be seen, since
+ // we are one past EOF. This happens with e.g. val x = \ u 1 <EOF>
+ error("incomplete unicode escape", charOffset - 1)
+ SU
+ }
+ else {
+ val d = digit2int(buf(charOffset), 16)
+ if (d >= 0) charOffset += 1
+ else error("error in unicode escape", charOffset)
+ d
+ }
+ }
+ if (charOffset < buf.length && buf(charOffset) == 'u' && decodeUni && evenSlashPrefix) {
+ do charOffset += 1
+ while (charOffset < buf.length && buf(charOffset) == 'u')
+ val code = udigit << 12 | udigit << 8 | udigit << 4 | udigit
+ lastUnicodeOffset = charOffset
+ ch = code.toChar
+ }
+ }
+
+ /** replace CR;LF by LF */
+ private def skipCR() {
+ if (ch == CR)
+ if (charOffset < buf.length && buf(charOffset) == LF) {
+ charOffset += 1
+ ch = LF
+ }
+ }
+
+ /** Handle line ends */
+ private def potentialLineEnd() {
+ if (ch == LF || ch == FF) {
+ lastLineStartOffset = lineStartOffset
+ lineStartOffset = charOffset
+ }
+ }
+
+ def isAtEnd = charOffset >= buf.length
+
+ /** A new reader that takes off at the current character position */
+ def lookaheadReader = new CharArrayLookaheadReader
+
+ class CharArrayLookaheadReader extends CharArrayReader {
+ val buf = self.buf
+ charOffset = self.charOffset
+ ch = self.ch
+ override def decodeUni = self.decodeUni
+ def error(msg: String, offset: Int) = self.error(msg, offset)
+ }
+}
diff --git a/src/dotty/tools/dotc/parsing/Scanners.scala b/src/dotty/tools/dotc/parsing/Scanners.scala
new file mode 100644
index 000000000..2b3ec9bc2
--- /dev/null
+++ b/src/dotty/tools/dotc/parsing/Scanners.scala
@@ -0,0 +1,958 @@
+package dotty.tools
+package dotc
+package parsing
+
+import Tokens._
+import core.Names._, core.Contexts._, core.Decorators._, util.Positions._
+import core.StdNames._
+import util.SourceFile
+import java.lang.Character.isDigit
+import scala.reflect.internal.Chars._
+import Tokens._
+import scala.annotation.{ switch, tailrec }
+import scala.collection.{ mutable, immutable }
+import mutable.{ ListBuffer, ArrayBuffer }
+import scala.xml.Utility.isNameStart
+
+object Scanners {
+
+ /** Offset into source character array */
+ type Offset = Int
+
+ /** An undefined offset */
+ val NoOffset: Offset = -1
+
+ case class Comment(pos: Position, chrs: String) {
+ def isDocComment = chrs.startsWith("/**")
+ }
+
+ type Token = Int
+
+ trait TokenData {
+
+ /** the next token */
+ var token: Token = EMPTY
+
+ /** the offset of the first character of the current token */
+ var offset: Offset = 0
+
+ /** the offset of the character following the token preceding this one */
+ var lastOffset: Offset = 0
+
+ /** the name of an identifier */
+ var name: TermName = null
+
+ /** the string value of a literal */
+ var strVal: String = null
+
+ /** the base of a number */
+ var base: Int = 0
+
+ def copyFrom(td: TokenData) = {
+ this.token = td.token
+ this.offset = td.offset
+ this.lastOffset = td.lastOffset
+ this.name = td.name
+ this.strVal = td.strVal
+ this.base = td.base
+ }
+ }
+
+ class Scanner(source: SourceFile)(implicit ctx: Context) extends CharArrayReader with TokenData {
+
+ val buf = source.content
+
+ var keepComments = false
+
+ /** All comments in the reverse order of their position in the source.
+ * set only when `keepComments` is true.
+ */
+ var revComments: List[Comment] = Nil
+
+ /** the last error offset
+ */
+ var errOffset: Offset = NoOffset
+
+ /** A buffer for comments */
+ val commentBuf = new StringBuilder
+
+ /** A character buffer for literals
+ */
+ val litBuf = new StringBuilder
+
+ /** append Unicode character to "litBuf" buffer
+ */
+ protected def putChar(c: Char): Unit = litBuf.append(c)
+
+ /** Clear buffer and set string */
+ private def setStrVal() = flushBuf(litBuf)
+
+ private class TokenData0 extends TokenData
+
+ /** we need one token lookahead and one token history
+ */
+ private val next : TokenData = new TokenData0
+ private val prev : TokenData = new TokenData0
+
+ /** a stack of tokens which indicates whether line-ends can be statement separators
+ * also used for keeping track of nesting levels.
+ * We keep track of the closing symbol of a region. This can be
+ * RPAREN if region starts with '('
+ * RBRACKET if region starts with '['
+ * RBRACE if region starts with '{'
+ * ARROW if region starts with `case'
+ * STRINGLIT if region is a string interpolation expression starting with '${'
+ * (the STRINGLIT appears twice in succession on the stack iff the
+ * expression is a multiline string literal).
+ */
+ var sepRegions: List[Token] = List()
+
+// Get next token ------------------------------------------------------------
+
+ /** Are we directly in a string interpolation expression?
+ */
+ private def inStringInterpolation =
+ sepRegions.nonEmpty && sepRegions.head == STRINGLIT
+
+ /** Are we directly in a multiline string interpolation expression?
+ * @pre inStringInterpolation
+ */
+ private def inMultiLineInterpolation =
+ inStringInterpolation && sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART
+
+ /** read next token and return last offset
+ */
+ def skipToken(): Offset = {
+ val off = offset
+ nextToken()
+ off
+ }
+
+ def adjustSepRegions(lastToken: Token): Unit = (lastToken: @switch) match {
+ case LPAREN =>
+ sepRegions = RPAREN :: sepRegions
+ case LBRACKET =>
+ sepRegions = RBRACKET :: sepRegions
+ case LBRACE =>
+ sepRegions = RBRACE :: sepRegions
+ case CASE =>
+ sepRegions = ARROW :: sepRegions
+ case RBRACE =>
+ while (!sepRegions.isEmpty && sepRegions.head != RBRACE)
+ sepRegions = sepRegions.tail
+ if (!sepRegions.isEmpty) sepRegions = sepRegions.tail
+ case RBRACKET | RPAREN =>
+ if (!sepRegions.isEmpty && sepRegions.head == lastToken)
+ sepRegions = sepRegions.tail
+ case ARROW =>
+ if (!sepRegions.isEmpty && sepRegions.head == lastToken)
+ sepRegions = sepRegions.tail
+ case STRINGLIT =>
+ if (inMultiLineInterpolation)
+ sepRegions = sepRegions.tail.tail
+ else if (inStringInterpolation)
+ sepRegions = sepRegions.tail
+ case _ =>
+ }
+
+ /** Produce next token, filling TokenData fields of Scanner.
+ */
+ def nextToken() {
+ val lastToken = token
+ adjustSepRegions(lastToken)
+
+ // Read a token or copy it from `next` tokenData
+ if (next.token == EMPTY) {
+ lastOffset = lastCharOffset
+ if (inStringInterpolation) fetchStringPart()
+ else fetchToken()
+ if (token == ERROR) adjustSepRegions(STRINGLIT)
+ } else {
+ this copyFrom next
+ next.token = EMPTY
+ }
+
+ /** Insert NEWLINE or NEWLINES if
+ * - we are after a newline
+ * - we are within a { ... } or on toplevel (wrt sepRegions)
+ * - the current token can start a statement and the one before can end it
+ * insert NEWLINES if we are past a blank line, NEWLINE otherwise
+ */
+ if (isAfterLineEnd() &&
+ (canEndStatTokens contains lastToken) &&
+ (canStartStatTokens contains token) &&
+ (sepRegions.isEmpty || sepRegions.head == RBRACE)) {
+ next copyFrom this
+ offset = lineStartOffset min lastLineStartOffset
+ token = if (pastBlankLine()) NEWLINES else NEWLINE
+ }
+
+ postProcessToken()
+// print("["+this+"]")
+ }
+
+ def postProcessToken() = {
+ // Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE
+ if (token == CASE) {
+ prev copyFrom this
+ val nextLastOffset = lastCharOffset
+ fetchToken()
+ def resetOffset() {
+ offset = prev.offset
+ lastOffset = prev.lastOffset
+ }
+ if (token == CLASS) {
+ token = CASECLASS
+ resetOffset()
+ } else if (token == OBJECT) {
+ token = CASEOBJECT
+ resetOffset()
+ } else {
+ lastOffset = nextLastOffset
+ next copyFrom this
+ this copyFrom prev
+ }
+ } else if (token == SEMI) {
+ prev copyFrom this
+ fetchToken()
+ if (token != ELSE) {
+ next copyFrom this
+ this copyFrom prev
+ }
+ }
+ }
+
+ /** Is current token first one after a newline? */
+ def isAfterLineEnd(): Boolean =
+ lastOffset < lineStartOffset &&
+ (lineStartOffset <= offset ||
+ lastOffset < lastLineStartOffset && lastLineStartOffset <= offset)
+
+ /** Is there a blank line between the current token and the last one?
+ * @pre afterLineEnd().
+ */
+ private def pastBlankLine(): Boolean = {
+ val end = offset
+ def recur(idx: Offset, isBlank: Boolean): Boolean =
+ idx < end && {
+ val ch = buf(idx)
+ if (ch == LF || ch == FF) isBlank || recur(idx + 1, true)
+ else recur(idx + 1, isBlank && ch <= ' ')
+ }
+ recur(lastOffset, false)
+ }
+
+ /** read next token, filling TokenData fields of Scanner.
+ */
+ protected final def fetchToken() {
+ offset = charOffset - 1
+ (ch: @switch) match {
+ case ' ' | '\t' | CR | LF | FF =>
+ nextChar()
+ fetchToken()
+ case 'A' | 'B' | 'C' | 'D' | 'E' |
+ 'F' | 'G' | 'H' | 'I' | 'J' |
+ 'K' | 'L' | 'M' | 'N' | 'O' |
+ 'P' | 'Q' | 'R' | 'S' | 'T' |
+ 'U' | 'V' | 'W' | 'X' | 'Y' |
+ 'Z' | '$' | '_' |
+ 'a' | 'b' | 'c' | 'd' | 'e' |
+ 'f' | 'g' | 'h' | 'i' | 'j' |
+ 'k' | 'l' | 'm' | 'n' | 'o' |
+ 'p' | 'q' | 'r' | 's' | 't' |
+ 'u' | 'v' | 'w' | 'x' | 'y' |
+ 'z' =>
+ putChar(ch)
+ nextChar()
+ getIdentRest()
+ if (ch == '"' && token == IDENTIFIER)
+ token = INTERPOLATIONID
+ case '<' => // is XMLSTART?
+ def fetchLT() = {
+ val last = if (charOffset >= 2) buf(charOffset - 2) else ' '
+ nextChar()
+ last match {
+ case ' ' | '\t' | '\n' | '{' | '(' | '>' if isNameStart(ch) || ch == '!' || ch == '?' =>
+ token = XMLSTART
+ case _ =>
+ // Console.println("found '<', but last is '"+in.last+"'"); // DEBUG
+ putChar('<')
+ getOperatorRest()
+ }
+ }
+ fetchLT
+ case '~' | '!' | '@' | '#' | '%' |
+ '^' | '*' | '+' | '-' | /*'<' | */
+ '>' | '?' | ':' | '=' | '&' |
+ '|' | '\\' =>
+ putChar(ch)
+ nextChar()
+ getOperatorRest()
+ case '/' =>
+ if (skipComment()) {
+ fetchToken()
+ } else {
+ putChar('/')
+ getOperatorRest()
+ }
+ case '0' =>
+ def fetchZero() = {
+ putChar(ch)
+ nextChar()
+ if (ch == 'x' || ch == 'X') {
+ nextChar()
+ base = 16
+ } else {
+ /**
+ * What should leading 0 be in the future? It is potentially dangerous
+ * to let it be base-10 because of history. Should it be an error? Is
+ * there a realistic situation where one would need it?
+ */
+ if (isDigit(ch))
+ error("Non-zero numbers may not have a leading zero.")
+ }
+ getNumber()
+ }
+ fetchZero
+ case '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
+ base = 10
+ getNumber()
+ case '`' =>
+ getBackquotedIdent()
+ case '\"' =>
+ def fetchDoubleQuote() = {
+ if (token == INTERPOLATIONID) {
+ nextRawChar()
+ if (ch == '\"') {
+ nextRawChar()
+ if (ch == '\"') {
+ nextRawChar()
+ getStringPart(multiLine = true)
+ sepRegions = STRINGPART :: sepRegions // indicate string part
+ sepRegions = STRINGLIT :: sepRegions // once more to indicate multi line string part
+ } else {
+ token = STRINGLIT
+ strVal = ""
+ }
+ } else {
+ getStringPart(multiLine = false)
+ sepRegions = STRINGLIT :: sepRegions // indicate single line string part
+ }
+ } else {
+ nextChar()
+ if (ch == '\"') {
+ nextChar()
+ if (ch == '\"') {
+ nextRawChar()
+ getRawStringLit()
+ } else {
+ token = STRINGLIT
+ strVal = ""
+ }
+ } else {
+ getStringLit()
+ }
+ }
+ }
+ fetchDoubleQuote
+ case '\'' =>
+ def fetchSingleQuote() = {
+ nextChar()
+ if (isIdentifierStart(ch))
+ charLitOr(getIdentRest)
+ else if (isOperatorPart(ch) && (ch != '\\'))
+ charLitOr(getOperatorRest)
+ else {
+ getLitChar()
+ if (ch == '\'') {
+ nextChar()
+ token = CHARLIT
+ setStrVal()
+ } else {
+ error("unclosed character literal")
+ }
+ }
+ }
+ fetchSingleQuote
+ case '.' =>
+ nextChar()
+ if ('0' <= ch && ch <= '9') {
+ putChar('.'); getFraction(); setStrVal()
+ } else {
+ token = DOT
+ }
+ case ';' =>
+ nextChar(); token = SEMI
+ case ',' =>
+ nextChar(); token = COMMA
+ case '(' =>
+ nextChar(); token = LPAREN
+ case '{' =>
+ nextChar(); token = LBRACE
+ case ')' =>
+ nextChar(); token = RPAREN
+ case '}' =>
+ nextChar(); token = RBRACE
+ case '[' =>
+ nextChar(); token = LBRACKET
+ case ']' =>
+ nextChar(); token = RBRACKET
+ case SU =>
+ if (isAtEnd) token = EOF
+ else {
+ error("illegal character")
+ nextChar()
+ }
+ case _ =>
+ def fetchOther() = {
+ if (ch == '\u21D2') {
+ nextChar(); token = ARROW
+ } else if (ch == '\u2190') {
+ nextChar(); token = LARROW
+ } else if (Character.isUnicodeIdentifierStart(ch)) {
+ putChar(ch)
+ nextChar()
+ getIdentRest()
+ } else if (isSpecial(ch)) {
+ putChar(ch)
+ nextChar()
+ getOperatorRest()
+ } else {
+ error(f"illegal character '\\u${ch: Int}%04x'")
+ nextChar()
+ }
+ }
+ fetchOther
+ }
+ }
+
+ private def skipComment(): Boolean = {
+ def appendToComment(ch: Char) =
+ if (keepComments) commentBuf.append(ch)
+ def nextChar() = {
+ appendToComment(ch)
+ Scanner.this.nextChar()
+ }
+ def skipLine(): Unit = {
+ nextChar()
+ if ((ch != CR) && (ch != LF) && (ch != SU)) skipLine()
+ }
+ @tailrec
+ def skipBlock(openComments: Int): Unit = {
+ val last = ch
+ nextChar()
+ if (ch == '/')
+ if (last == '*') {
+ if (openComments > 0) skipBlock(openComments - 1)
+ } else {
+ nextChar()
+ if (ch == '*') { nextChar(); skipBlock(openComments + 1) }
+ else skipBlock(openComments)
+ }
+ else if (ch == SU) incompleteInputError("unclosed comment")
+ else skipBlock(openComments)
+ }
+ val start = lastCharOffset
+ def finishComment(): Boolean = {
+ if (keepComments) {
+ val pos = Position(start, charOffset)
+ nextChar()
+ revComments = Comment(pos, flushBuf(commentBuf)) :: revComments
+ }
+ true
+ }
+ nextChar()
+ if (ch == '/') { skipLine(); finishComment() }
+ else if (ch == '*') { nextChar(); skipBlock(0); finishComment() }
+ else false
+ }
+
+// Identifiers ---------------------------------------------------------------
+
+ private def getBackquotedIdent() {
+ nextChar()
+ getLitChars('`')
+ if (ch == '`') {
+ nextChar()
+ finishNamed(BACKQUOTED_IDENT)
+ if (name.length == 0)
+ error("empty quoted identifier")
+ else if (name == nme.WILDCARD)
+ error("wildcard invalid as backquoted identifier")
+ }
+ else error("unclosed quoted identifier")
+ }
+
+ private def getIdentRest(): Unit = (ch: @switch) match {
+ case 'A' | 'B' | 'C' | 'D' | 'E' |
+ 'F' | 'G' | 'H' | 'I' | 'J' |
+ 'K' | 'L' | 'M' | 'N' | 'O' |
+ 'P' | 'Q' | 'R' | 'S' | 'T' |
+ 'U' | 'V' | 'W' | 'X' | 'Y' |
+ 'Z' | '$' |
+ 'a' | 'b' | 'c' | 'd' | 'e' |
+ 'f' | 'g' | 'h' | 'i' | 'j' |
+ 'k' | 'l' | 'm' | 'n' | 'o' |
+ 'p' | 'q' | 'r' | 's' | 't' |
+ 'u' | 'v' | 'w' | 'x' | 'y' |
+ 'z' |
+ '0' | '1' | '2' | '3' | '4' |
+ '5' | '6' | '7' | '8' | '9' =>
+ putChar(ch)
+ nextChar()
+ getIdentRest()
+ case '_' =>
+ putChar(ch)
+ nextChar()
+ getIdentOrOperatorRest()
+ case SU => // strangely enough, Character.isUnicodeIdentifierPart(SU) returns true!
+ finishNamed()
+ case _ =>
+ if (Character.isUnicodeIdentifierPart(ch)) {
+ putChar(ch)
+ nextChar()
+ getIdentRest()
+ } else {
+ finishNamed()
+ }
+ }
+
+ private def getOperatorRest(): Unit = (ch: @switch) match {
+ case '~' | '!' | '@' | '#' | '%' |
+ '^' | '*' | '+' | '-' | '<' |
+ '>' | '?' | ':' | '=' | '&' |
+ '|' | '\\' =>
+ putChar(ch); nextChar(); getOperatorRest()
+ case '/' =>
+ if (skipComment()) finishNamed()
+ else { putChar('/'); getOperatorRest() }
+ case _ =>
+ if (isSpecial(ch)) { putChar(ch); nextChar(); getOperatorRest() }
+ else finishNamed()
+ }
+
+ private def getIdentOrOperatorRest() {
+ if (isIdentifierPart(ch))
+ getIdentRest()
+ else ch match {
+ case '~' | '!' | '@' | '#' | '%' |
+ '^' | '*' | '+' | '-' | '<' |
+ '>' | '?' | ':' | '=' | '&' |
+ '|' | '\\' | '/' =>
+ getOperatorRest()
+ case _ =>
+ if (isSpecial(ch)) getOperatorRest()
+ else finishNamed()
+ }
+ }
+
+
+// Literals -----------------------------------------------------------------
+
+ private def getStringLit() = {
+ getLitChars('"')
+ if (ch == '"') {
+ setStrVal()
+ nextChar()
+ token = STRINGLIT
+ } else error("unclosed string literal")
+ }
+
+ private def getRawStringLit(): Unit = {
+ if (ch == '\"') {
+ nextRawChar()
+ if (isTripleQuote()) {
+ setStrVal()
+ token = STRINGLIT
+ } else
+ getRawStringLit()
+ } else if (ch == SU) {
+ incompleteInputError("unclosed multi-line string literal")
+ } else {
+ putChar(ch)
+ nextRawChar()
+ getRawStringLit()
+ }
+ }
+
+ @annotation.tailrec private def getStringPart(multiLine: Boolean): Unit = {
+ def finishStringPart() = {
+ setStrVal()
+ token = STRINGPART
+ next.lastOffset = charOffset - 1
+ next.offset = charOffset - 1
+ }
+ if (ch == '"') {
+ if (multiLine) {
+ nextRawChar()
+ if (isTripleQuote()) {
+ setStrVal()
+ token = STRINGLIT
+ } else
+ getStringPart(multiLine)
+ } else {
+ nextChar()
+ setStrVal()
+ token = STRINGLIT
+ }
+ } else if (ch == '$') {
+ nextRawChar()
+ if (ch == '$') {
+ putChar(ch)
+ nextRawChar()
+ getStringPart(multiLine)
+ } else if (ch == '{') {
+ finishStringPart()
+ nextRawChar()
+ next.token = LBRACE
+ } else if (Character.isUnicodeIdentifierStart(ch)) {
+ finishStringPart()
+ do {
+ putChar(ch)
+ nextRawChar()
+ } while (ch != SU && Character.isUnicodeIdentifierPart(ch))
+ finishNamed(target = next)
+ } else {
+ error("invalid string interpolation: `$$', `$'ident or `$'BlockExpr expected")
+ }
+ } else {
+ val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF)))
+ if (isUnclosedLiteral) {
+ if (multiLine)
+ incompleteInputError("unclosed multi-line string literal")
+ else
+ error("unclosed string literal")
+ }
+ else {
+ putChar(ch)
+ nextRawChar()
+ getStringPart(multiLine)
+ }
+ }
+ }
+
+ private def fetchStringPart() = {
+ offset = charOffset - 1
+ getStringPart(multiLine = inMultiLineInterpolation)
+ }
+
+ private def isTripleQuote(): Boolean =
+ if (ch == '"') {
+ nextRawChar()
+ if (ch == '"') {
+ nextChar()
+ while (ch == '"') {
+ putChar('"')
+ nextChar()
+ }
+ true
+ } else {
+ putChar('"')
+ putChar('"')
+ false
+ }
+ } else {
+ putChar('"')
+ false
+ }
+
+ /** copy current character into litBuf, interpreting any escape sequences,
+ * and advance to next character.
+ */
+ protected def getLitChar(): Unit =
+ if (ch == '\\') {
+ nextChar()
+ if ('0' <= ch && ch <= '7') {
+ val leadch: Char = ch
+ var oct: Int = digit2int(ch, 8)
+ nextChar()
+ if ('0' <= ch && ch <= '7') {
+ oct = oct * 8 + digit2int(ch, 8)
+ nextChar()
+ if (leadch <= '3' && '0' <= ch && ch <= '7') {
+ oct = oct * 8 + digit2int(ch, 8)
+ nextChar()
+ }
+ }
+ putChar(oct.toChar)
+ } else {
+ ch match {
+ case 'b' => putChar('\b')
+ case 't' => putChar('\t')
+ case 'n' => putChar('\n')
+ case 'f' => putChar('\f')
+ case 'r' => putChar('\r')
+ case '\"' => putChar('\"')
+ case '\'' => putChar('\'')
+ case '\\' => putChar('\\')
+ case _ => invalidEscape()
+ }
+ nextChar()
+ }
+ } else {
+ putChar(ch)
+ nextChar()
+ }
+
+ protected def invalidEscape(): Unit = {
+ error("invalid escape character", charOffset - 1)
+ putChar(ch)
+ }
+
+ private def getLitChars(delimiter: Char) = {
+ while (ch != delimiter && !isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape))
+ getLitChar()
+ }
+
+ /** read fractional part and exponent of floating point number
+ * if one is present.
+ */
+ protected def getFraction() {
+ token = DOUBLELIT
+ while ('0' <= ch && ch <= '9') {
+ putChar(ch)
+ nextChar()
+ }
+ if (ch == 'e' || ch == 'E') {
+ val lookahead = lookaheadReader
+ lookahead.nextChar()
+ if (lookahead.ch == '+' || lookahead.ch == '-') {
+ lookahead.nextChar()
+ }
+ if ('0' <= lookahead.ch && lookahead.ch <= '9') {
+ putChar(ch)
+ nextChar()
+ if (ch == '+' || ch == '-') {
+ putChar(ch)
+ nextChar()
+ }
+ while ('0' <= ch && ch <= '9') {
+ putChar(ch)
+ nextChar()
+ }
+ }
+ token = DOUBLELIT
+ }
+ if (ch == 'd' || ch == 'D') {
+ putChar(ch)
+ nextChar()
+ token = DOUBLELIT
+ } else if (ch == 'f' || ch == 'F') {
+ putChar(ch)
+ nextChar()
+ token = FLOATLIT
+ }
+ checkNoLetter()
+ }
+ def checkNoLetter() {
+ if (isIdentifierPart(ch) && ch >= ' ')
+ error("Invalid literal number")
+ }
+
+ /** Read a number into strVal and set base
+ */
+ protected def getNumber() {
+ while (digit2int(ch, base) >= 0) {
+ putChar(ch)
+ nextChar()
+ }
+ token = INTLIT
+ if (base == 10 && ch == '.') {
+ val isDefinitelyNumber = {
+ val lookahead = lookaheadReader
+ val c = lookahead.getc()
+ (c: @switch) match {
+ /** Another digit is a giveaway. */
+ case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' =>
+ true
+
+ /** Backquoted idents like 22.`foo`. */
+ case '`' =>
+ false
+
+ /** These letters may be part of a literal, or a method invocation on an Int.
+ */
+ case 'd' | 'D' | 'f' | 'F' =>
+ !isIdentifierPart(lookahead.getc())
+
+ /** A little more special handling for e.g. 5e7 */
+ case 'e' | 'E' =>
+ val ch = lookahead.getc()
+ !isIdentifierPart(ch) || (isDigit(ch) || ch == '+' || ch == '-')
+
+ case x =>
+ !isIdentifierStart(x)
+ }
+ }
+ if (isDefinitelyNumber) {
+ putChar(ch)
+ nextChar()
+ getFraction()
+ }
+ } else (ch: @switch) match {
+ case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' if base == 10 =>
+ getFraction()
+ case 'l' | 'L' =>
+ nextChar()
+ token = LONGLIT
+ case _ =>
+ }
+ setStrVal()
+ }
+
+ /** Parse character literal if current character is followed by \',
+ * or follow with given op and return a symbol literal token
+ */
+ def charLitOr(op: () => Unit) {
+ putChar(ch)
+ nextChar()
+ if (ch == '\'') {
+ nextChar()
+ token = CHARLIT
+ setStrVal()
+ } else {
+ op()
+ token = SYMBOLLIT
+ strVal = name.toString
+ }
+ }
+
+// Setting token data ----------------------------------------------------
+
+ /** Clear buffer and set name and token */
+ def finishNamed(idtoken: Token = IDENTIFIER, target: TokenData = this): Unit = {
+ target.name = flushBuf(litBuf).toTermName
+ target.token = idtoken
+ if (idtoken == IDENTIFIER) {
+ val idx = target.name.start
+ if (idx >= 0 && idx < kwArray.length) target.token = kwArray(idx)
+ }
+ }
+
+ /** Return buffer contents and clear */
+ def flushBuf(buf: StringBuilder): String = {
+ val str = buf.toString
+ buf.clear()
+ str
+ }
+
+ /** Convert current strVal to char value
+ */
+ def charVal: Char = if (strVal.length > 0) strVal.charAt(0) else 0
+
+ /** Convert current strVal, base to long value
+ * This is tricky because of max negative value.
+ */
+ def intVal(negated: Boolean): Long = {
+ if (token == CHARLIT && !negated) {
+ charVal
+ } else {
+ var value: Long = 0
+ val divider = if (base == 10) 1 else 2
+ val limit: Long =
+ if (token == LONGLIT) Long.MaxValue else Int.MaxValue
+ var i = 0
+ val len = strVal.length
+ while (i < len) {
+ val d = digit2int(strVal charAt i, base)
+ if (d < 0) {
+ error("malformed integer number")
+ return 0
+ }
+ if (value < 0 ||
+ limit / (base / divider) < value ||
+ limit - (d / divider) < value * (base / divider) &&
+ !(negated && limit == value * base - 1 + d)) {
+ error("integer number too large")
+ return 0
+ }
+ value = value * base + d
+ i += 1
+ }
+ if (negated) -value else value
+ }
+ }
+
+ def intVal: Long = intVal(false)
+
+ /** Convert current strVal, base to double value
+ */
+ def floatVal(negated: Boolean): Double = {
+ val limit: Double =
+ if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue
+ try {
+ val value: Double = java.lang.Double.valueOf(strVal).doubleValue()
+ if (value > limit)
+ error("floating point number too large")
+ if (negated) -value else value
+ } catch {
+ case _: NumberFormatException =>
+ error("malformed floating point number")
+ 0.0
+ }
+ }
+
+ def floatVal: Double = floatVal(false)
+
+ override def toString = showTokenDetailed(token)
+
+ def show: String = token match {
+ case IDENTIFIER | BACKQUOTED_IDENT => s"id($name)"
+ case CHARLIT => s"char($intVal)"
+ case INTLIT => s"int($intVal)"
+ case LONGLIT => s"long($intVal)"
+ case FLOATLIT => s"float($floatVal)"
+ case DOUBLELIT => s"double($floatVal)"
+ case STRINGLIT => s"string($strVal)"
+ case STRINGPART => s"stringpart($strVal)"
+ case INTERPOLATIONID => s"interpolationid($name)"
+ case SEMI => ";"
+ case NEWLINE => ";"
+ case NEWLINES => ";;"
+ case COMMA => ","
+ case _ => showToken(token)
+ }
+
+// (does not seem to be needed) def flush = { charOffset = offset; nextChar(); this }
+
+ /* Resume normal scanning after XML */
+ def resume(lastToken: Token) = {
+ token = lastToken
+ if (next.token != EMPTY && !ctx.reporter.hasErrors)
+ error("unexpected end of input: possible missing '}' in XML block")
+
+ nextToken()
+ }
+
+// Errors -----------------------------------------------------------------
+
+ /** Generate an error at the given offset */
+ def error(msg: String, off: Offset = offset) = {
+ ctx.error(msg, source atPos Position(off))
+ token = ERROR
+ errOffset = off
+ }
+
+ /** signal an error where the input ended in the middle of a token */
+ def incompleteInputError(msg: String) {
+ ctx.reporter.incompleteInputError(msg, source atPos Position(offset))
+ token = EOF
+ errOffset = offset
+ }
+
+ /* Initialization: read first char, then first token */
+ nextChar()
+ nextToken()
+ } // end Scanner
+
+ // ------------- keyword configuration -----------------------------------
+
+ private val kwArray: Array[Token] = {
+ def start(tok: Token) = tok.toString.toTermName.start
+ val sourceKeywords = keywords.filterNot(_.toString contains " ")
+ val lastIdx = sourceKeywords.map(start).max
+ val arr = Array.fill(lastIdx + 1)(IDENTIFIER)
+ for (kw <- sourceKeywords) arr(start(kw)) = kw
+ arr
+ }
+}
diff --git a/src/dotty/tools/dotc/parsing/Tokens.scala b/src/dotty/tools/dotc/parsing/Tokens.scala
new file mode 100644
index 000000000..f573df49d
--- /dev/null
+++ b/src/dotty/tools/dotc/parsing/Tokens.scala
@@ -0,0 +1,171 @@
+package dotty.tools
+package dotc
+package parsing
+
+import collection.mutable
+import collection.immutable.BitSet
+import scala.annotation.switch
+
+object Tokens {
+
+ final val minToken = EMPTY
+ final val maxToken = XMLSTART
+
+ type TokenSet = BitSet
+
+ def tokenRange(lo: Int, hi: Int): TokenSet = BitSet(lo to hi: _*)
+
+ def showTokenDetailed(token: Int) = debugString(token)
+
+ def showToken(token: Int) = {
+ val str = tokenString(token)
+ if (keywords contains token) s"'$str'" else str
+ }
+
+ val tokenString, debugString = new Array[String](maxToken + 1)
+
+ def enter(token: Int, str: String, debugStr: String = ""): Unit = {
+ tokenString(token) = str
+ debugString(token) = if (debugStr.isEmpty) str else debugStr
+ }
+
+ /** special tokens */
+ final val EMPTY = 0; enter(EMPTY, "<empty>") // a missing token, used in lookahead
+ final val ERROR = 1; enter(ERROR, "erroneous token") // an erroneous token
+ final val EOF = 2; enter(EOF, "eof")
+
+ /** literals */
+ final val CHARLIT = 3; enter(CHARLIT, "character literal")
+ final val INTLIT = 4; enter(INTLIT, "integer literal")
+ final val LONGLIT = 5; enter(LONGLIT, "long literal")
+ final val FLOATLIT = 6; enter(FLOATLIT, "float literal")
+ final val DOUBLELIT = 7; enter(DOUBLELIT, "double literal")
+ final val STRINGLIT = 8; enter(STRINGLIT, "string literal")
+ final val STRINGPART = 9; enter(STRINGPART, "string literal", "string literal part")
+ final val INTERPOLATIONID = 10; enter(INTERPOLATIONID, "string interpolator")
+ final val SYMBOLLIT = 11; enter(SYMBOLLIT, "symbol literal") // TODO: deprecate
+
+ final val literalTokens = tokenRange(CHARLIT, SYMBOLLIT)
+
+ /** identifiers */
+ final val IDENTIFIER = 12; enter(IDENTIFIER, "identifier")
+ final val BACKQUOTED_IDENT = 13; enter(BACKQUOTED_IDENT, "identifier", "backquoted ident")
+
+ final val identifierTokens = BitSet(IDENTIFIER, BACKQUOTED_IDENT)
+
+ def isIdentifier(token : Int) =
+ token >= IDENTIFIER && token <= BACKQUOTED_IDENT
+
+ /** alphabetic keywords */
+ final val IF = 20; enter(IF, "if")
+ final val FOR = 21; enter(FOR, "for")
+ final val ELSE = 22; enter(ELSE, "else")
+ final val THIS = 23; enter(THIS, "this")
+ final val NULL = 24; enter(NULL, "null")
+ final val NEW = 25; enter(NEW, "new")
+ final val WITH = 26; enter(WITH, "with")
+ final val SUPER = 27; enter(SUPER, "super")
+ final val CASE = 28; enter(CASE, "case")
+ final val CASECLASS = 29; enter(CASECLASS, "case class")
+ final val CASEOBJECT = 30; enter(CASEOBJECT, "case object")
+ final val VAL = 31; enter(VAL, "val")
+ final val ABSTRACT = 32; enter(ABSTRACT, "abstract")
+ final val FINAL = 33; enter(FINAL, "final")
+ final val PRIVATE = 34; enter(PRIVATE, "private")
+ final val PROTECTED = 35; enter(PROTECTED, "protected")
+ final val OVERRIDE = 36; enter(OVERRIDE, "override")
+ final val IMPLICIT = 37; enter(IMPLICIT, "implicit")
+ final val VAR = 38; enter(VAR, "var")
+ final val DEF = 39; enter(DEF, "def")
+ final val TYPE = 40; enter(TYPE, "type")
+ final val EXTENDS = 41; enter(EXTENDS, "extends")
+ final val TRUE = 42; enter(TRUE, "true")
+ final val FALSE = 43; enter(FALSE, "false")
+ final val OBJECT = 44; enter(OBJECT, "object")
+ final val CLASS = 45; enter(CLASS, "class")
+ final val IMPORT = 46; enter(IMPORT, "import")
+ final val PACKAGE = 47; enter(PACKAGE, "package")
+ final val YIELD = 48; enter(YIELD, "yield")
+ final val DO = 49; enter(DO, "do")
+ final val TRAIT = 50; enter(TRAIT, "trait")
+ final val SEALED = 51; enter(SEALED, "sealed")
+ final val THROW = 52; enter(THROW, "throw")
+ final val TRY = 53; enter(TRY, "try")
+ final val CATCH = 54; enter(CATCH, "catch")
+ final val FINALLY = 55; enter(FINALLY, "finally")
+ final val WHILE = 56; enter(WHILE, "while")
+ final val RETURN = 57; enter(RETURN, "return")
+ final val MATCH = 58; enter(MATCH, "match")
+ final val FORSOME = 59; enter(FORSOME, "forSome") // TODO: deprecate
+ final val LAZY = 61; enter(LAZY, "lazy")
+ final val THEN = 62; enter(THEN, "then")
+
+ final val alphaKeywords = tokenRange(IF, LAZY)
+
+ /** special symbols */
+ final val COMMA = 70; enter(COMMA, "','")
+ final val SEMI = 71; enter(DOT, "'.'")
+ final val DOT = 72; enter(SEMI, "';'")
+ final val NEWLINE = 78; enter(NEWLINE, "';'", "new line")
+ final val NEWLINES = 79; enter(NEWLINES, "';'", "new lines")
+
+ /** special keywords */
+ final val USCORE = 73; enter(USCORE, "_")
+ final val COLON = 74; enter(COLON, ":")
+ final val EQUALS = 75; enter(EQUALS, "==")
+ final val LARROW = 76; enter(LARROW, "<-")
+ final val ARROW = 77; enter(ARROW, "=>")
+ final val SUBTYPE = 80; enter(SUBTYPE, "<:")
+ final val SUPERTYPE = 81; enter(SUPERTYPE, ">:")
+ final val HASH = 82; enter(HASH, "#")
+ final val AT = 83; enter(AT, "@")
+ final val VIEWBOUND = 84; enter(VIEWBOUND, "<%") // TODO: deprecate
+
+ final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND)
+ final val symbolicTokens = tokenRange(COMMA, VIEWBOUND)
+ final val keywords = alphaKeywords | symbolicKeywords
+
+ /** parentheses */
+ final val LPAREN = 90; enter(LPAREN, "'('")
+ final val RPAREN = 91; enter(RPAREN, "')'")
+ final val LBRACKET = 92; enter(LBRACKET, "'['")
+ final val RBRACKET = 93; enter(RBRACKET, "']'")
+ final val LBRACE = 94; enter(LBRACE, "'{'")
+ final val RBRACE = 95; enter(RBRACE, "'}'")
+
+ /** XML mode */
+ final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
+
+ final val allTokens = tokenRange(minToken, maxToken)
+
+ final val atomicExprTokens = literalTokens | identifierTokens | BitSet(
+ USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, XMLSTART)
+
+ final val canStartExpressionTokens = atomicExprTokens | BitSet(
+ LBRACE, LPAREN, IF, DO, WHILE, FOR, NEW, TRY, THROW)
+
+ final val canStartTypeTokens = literalTokens | identifierTokens | BitSet(
+ THIS, SUPER, USCORE, LPAREN, AT)
+
+ final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, CASECLASS, CASEOBJECT)
+
+ final val dclIntroTokens = BitSet(DEF, VAL, VAR, TYPE)
+
+ final val defIntroTokens = templateIntroTokens | dclIntroTokens
+
+ final val localModifierTokens = BitSet(
+ ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY)
+
+ final val modifierTokens = localModifierTokens | BitSet(
+ PRIVATE, PROTECTED, OVERRIDE)
+
+ /** Is token only legal as start of statement (eof also included)? */
+ final val mustStartStatTokens = defIntroTokens | modifierTokens | BitSet(
+ CASE, IMPORT, PACKAGE)
+
+ final val canStartStatTokens = canStartExpressionTokens | mustStartStatTokens | BitSet(
+ AT)
+
+ final val canEndStatTokens = atomicExprTokens | BitSet(
+ TYPE, RPAREN, RBRACE, RBRACKET)
+}
diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
index 88fdfa386..2372485f4 100644
--- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
+++ b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala
@@ -3,7 +3,7 @@ package dotc
package reporting
import scala.collection.mutable
-import core.Positions.Position
+import util.Positions.SourcePosition
import core.Contexts._
import Reporter.Severity.{Value => Severity, _}
import java.io.{ BufferedReader, IOException, PrintWriter }
@@ -24,24 +24,24 @@ class ConsoleReporter(
/** maximal number of error messages to be printed */
protected def ErrorLimit = 100
- def formatMessage(msg: String, pos: Position)(implicit ctx: Context) = msg // for now
+ def formatMessage(msg: String, pos: SourcePosition)(implicit ctx: Context) = msg // for now
/** Prints the message. */
def printMessage(msg: String) { writer.print(msg + "\n"); writer.flush() }
/** Prints the message with the given position indication. */
- def printMessage(msg: String, pos: Position)(implicit ctx: Context) {
+ def printMessage(msg: String, pos: SourcePosition)(implicit ctx: Context) {
printMessage(formatMessage(msg, pos))
}
- def printMessage(msg: String, severity: Severity, pos: Position)(implicit ctx: Context) {
+ def printMessage(msg: String, severity: Severity, pos: SourcePosition)(implicit ctx: Context) {
printMessage(label(severity) + msg, pos)
}
/**
* @param pos ...
- def printSourceLine(pos: Position) {
+ def printSourceLine(pos: SourcePosition) {
printMessage(pos.lineContent.stripLineEnd)
printColumnMarker(pos)
}
@@ -50,7 +50,7 @@ class ConsoleReporter(
*
* @param pos ...
*/
- def printColumnMarker(pos: Position) =
+ def printColumnMarker(pos: SourcePosition) =
if (pos.isDefined) { printMessage(" " * (pos.column - 1) + "^") }
*/
@@ -60,7 +60,7 @@ class ConsoleReporter(
if ( count(ERROR) > 0) printMessage(countString(ERROR ) + " found")
}
- override def report(msg: String, severity: Severity, pos: Position)(implicit ctx: Context) {
+ override def report(msg: String, severity: Severity, pos: SourcePosition)(implicit ctx: Context) {
if (severity != ERROR || count(severity) <= ErrorLimit)
printMessage(msg, severity, pos)
if (ctx.settings.prompt.value) displayPrompt()
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index 2c892938c..3e7eaecde 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -3,16 +3,16 @@ package dotc
package reporting
import core.Contexts._
-import core.Positions._
+import util.Positions._
+import util.{SourceFile, NoSource}
import core.Decorators.PhaseListDecorator
import collection.mutable
-import io.{SourceFile, NoSource}
import java.lang.System.currentTimeMillis
trait Reporting { this: Context =>
- def error(msg: String, pos: Position = NoPosition): Unit = reporter.error(msg, pos)
- def warning(msg: String, pos: Position = NoPosition): Unit = reporter.warning(msg, pos)
- def inform(msg: String, pos: Position = NoPosition): Unit = reporter.info(msg, pos)
+ def error(msg: String, pos: SourcePosition = NoSourcePosition): Unit = reporter.error(msg, pos)
+ def warning(msg: String, pos: SourcePosition = NoSourcePosition): Unit = reporter.warning(msg, pos)
+ def inform(msg: String, pos: SourcePosition = NoSourcePosition): Unit = reporter.info(msg, pos)
def log(msg: => String): Unit =
if (this.settings.log.value.containsPhase(phase))
@@ -24,6 +24,8 @@ trait Reporting { this: Context =>
def informTime(msg: => String, start: Long): Unit =
informProgress(msg + elapsed(start))
+ def deprecationWarning(msg: String, pos: SourcePosition): Unit = ???
+
private def elapsed(start: Long) =
" in " + (currentTimeMillis - start) + "ms"
@@ -78,9 +80,9 @@ abstract class Reporter {
import Reporter.Severity.{Value => Severity, _}
- protected def report(msg: String, severity: Severity, pos: Position)(implicit ctx: Context): Unit
+ protected def report(msg: String, severity: Severity, pos: SourcePosition)(implicit ctx: Context): Unit
- protected def isHidden(severity: Severity, pos: Position)(implicit ctx: Context) = false
+ protected def isHidden(severity: Severity, pos: SourcePosition)(implicit ctx: Context) = false
val count = new mutable.HashMap[Severity, Int]() {
override def default(key: Severity) = 0
@@ -99,7 +101,7 @@ abstract class Reporter {
finally _truncationOK = saved
}
- type ErrorHandler = (String, Position, Context) => Unit
+ type ErrorHandler = (String, SourcePosition, Context) => Unit
private var incompleteHandler: ErrorHandler = error(_, _)(_)
def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = {
val saved = incompleteHandler
@@ -112,26 +114,26 @@ abstract class Reporter {
def hasWarnings = count(WARNING) > 0
/** For sending messages that are printed only if -verbose is set */
- def info(msg: String, pos: Position = NoPosition)(implicit ctx: Context): Unit =
+ def info(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
if (ctx.settings.verbose.value) info0(msg, INFO, pos)
/** For sending a message which should not be labeled as a warning/error,
* but also shouldn't require -verbose to be visible.
*/
- def echo(msg: String, pos: Position = NoPosition)(implicit ctx: Context): Unit =
+ def echo(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
info0(msg, INFO, pos)
- def warning(msg: String, pos: Position = NoPosition)(implicit ctx: Context): Unit =
+ def warning(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
if (!ctx.settings.nowarn.value)
withoutTruncating(info0(msg, WARNING, pos))
- def error(msg: String, pos: Position = NoPosition)(implicit ctx: Context): Unit =
+ def error(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
withoutTruncating(info0(msg, ERROR, pos))
- def incompleteInputError(msg: String, pos: Position = NoPosition)(implicit ctx: Context): Unit =
+ def incompleteInputError(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
incompleteHandler(msg, pos, ctx)
- private def info0(msg: String, severity: Severity, pos: Position)(implicit ctx: Context): Unit = {
+ private def info0(msg: String, severity: Severity, pos: SourcePosition)(implicit ctx: Context): Unit = {
if (!isHidden(severity, pos)) {
count(severity) += 1
report(msg, severity, pos)
diff --git a/src/dotty/tools/dotc/reporting/StoreReporter.scala b/src/dotty/tools/dotc/reporting/StoreReporter.scala
index 5b9553509..31df36e6f 100644
--- a/src/dotty/tools/dotc/reporting/StoreReporter.scala
+++ b/src/dotty/tools/dotc/reporting/StoreReporter.scala
@@ -4,7 +4,7 @@ package reporting
import core.Contexts.Context
import scala.collection.mutable
-import core.Positions.Position
+import util.Positions.SourcePosition
import Reporter.Severity.{Value => Severity}
/**
@@ -12,12 +12,12 @@ import Reporter.Severity.{Value => Severity}
*/
class StoreReporter extends Reporter {
- class Info(val msg: String, val severity: Severity, val pos: Position) {
+ class Info(val msg: String, val severity: Severity, val pos: SourcePosition) {
override def toString() = "pos: " + pos + " " + msg + " " + severity
}
val infos = new mutable.LinkedHashSet[Info]
- protected def report(msg: String, severity: Severity, pos: Position)(implicit ctx: Context): Unit = {
+ protected def report(msg: String, severity: Severity, pos: SourcePosition)(implicit ctx: Context): Unit = {
infos += new Info(msg, severity, pos)
}
diff --git a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
index 5df999aab..704317f23 100644
--- a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
+++ b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala
@@ -3,9 +3,9 @@ package dotc
package reporting
import scala.collection.mutable
-import core.Positions.Position
+import util.Positions.SourcePosition
+import util.SourceFile
import Reporter.Severity.{Value => Severity}
-import io.SourceFile
import core.Contexts.Context
/**
@@ -20,7 +20,7 @@ trait UniqueMessagePositions extends Reporter {
/** Logs a position and returns true if it was already logged.
* @note Two positions are considered identical for logging if they have the same point.
*/
- override def isHidden(severity: Severity, pos: Position)(implicit ctx: Context): Boolean =
+ override def isHidden(severity: Severity, pos: SourcePosition)(implicit ctx: Context): Boolean =
pos.exists && {
positions get (ctx.source, pos.point) match {
case Some(level) if level >= severity => true
diff --git a/src/dotty/tools/dotc/core/Chars.scala b/src/dotty/tools/dotc/util/Chars.scala
index 6b796aa31..99aad620a 100644
--- a/src/dotty/tools/dotc/core/Chars.scala
+++ b/src/dotty/tools/dotc/util/Chars.scala
@@ -3,11 +3,16 @@
* @author Martin Odersky
*/
package dotty.tools.dotc
-package core
+package util
-import scala.annotation.{ tailrec, switch }
+import scala.annotation.switch
import java.lang.{ Character => JCharacter }
-import scala.language.postfixOps
+import java.lang.{Character => JCharacter}
+import java.lang.Character.LETTER_NUMBER
+import java.lang.Character.LOWERCASE_LETTER
+import java.lang.Character.OTHER_LETTER
+import java.lang.Character.TITLECASE_LETTER
+import java.lang.Character.UPPERCASE_LETTER
/** Contains constants and classifier methods for characters */
object Chars {
diff --git a/src/dotty/tools/dotc/util/FreshNameCreator.scala b/src/dotty/tools/dotc/util/FreshNameCreator.scala
new file mode 100644
index 000000000..d0c007d94
--- /dev/null
+++ b/src/dotty/tools/dotc/util/FreshNameCreator.scala
@@ -0,0 +1,38 @@
+package dotty.tools
+package dotc
+package util
+
+import scala.collection.mutable
+
+trait FreshNameCreator {
+ def newName(): String
+ def newName(prefix: String): String
+
+ @deprecated("use newName(prefix)", "2.9.0")
+ def newName(pos: scala.reflect.internal.util.Position, prefix: String): String = newName(prefix)
+ @deprecated("use newName()", "2.9.0")
+ def newName(pos: scala.reflect.internal.util.Position): String = newName()
+}
+
+object FreshNameCreator {
+ class Default extends FreshNameCreator {
+ protected var counter = 0
+ protected val counters = mutable.HashMap[String, Int]() withDefaultValue 0
+
+ /**
+ * Create a fresh name with the given prefix. It is guaranteed
+ * that the returned name has never been returned by a previous
+ * call to this function (provided the prefix does not end in a digit).
+ */
+ def newName(prefix: String): String = {
+ val safePrefix = prefix.replaceAll("""[<>]""", """\$""")
+ counters(safePrefix) += 1
+
+ safePrefix + counters(safePrefix)
+ }
+ def newName(): String = {
+ counter += 1
+ "$" + counter + "$"
+ }
+ }
+}
diff --git a/src/dotty/tools/dotc/core/Positions.scala b/src/dotty/tools/dotc/util/Positions.scala
index 5e4673ac4..31eb2ff0d 100644
--- a/src/dotty/tools/dotc/core/Positions.scala
+++ b/src/dotty/tools/dotc/util/Positions.scala
@@ -1,4 +1,5 @@
-package dotty.tools.dotc.core
+package dotty.tools.dotc
+package util
/** Position format in little endian:
* Start: unsigned 26 Bits (works for source files up to 64M)
@@ -33,6 +34,16 @@ object Positions {
}
def exists = this != NoPosition
+
+ def shift(offset: Int) =
+ if (exists) Position(start + offset, end + offset, point - start)
+ else this
+
+ def focus = Position(point)
+
+ def withStart(start: Int) = Position(start, this.end, this.point - start)
+ def withEnd(end: Int) = Position(this.start, end, this.point - this.start)
+ def withPoint(point: Int) = Position(this.start, this.end, point - this.start)
}
def Position(start: Int, end: Int, pointOffset: Int = 0): Position =
@@ -45,6 +56,15 @@ object Positions {
val NoPosition = new Position(-1L)
+ case class SourcePosition(source: SourceFile, pos: Position) {
+ def point: Int = pos.point
+ def start: Int = pos.start
+ def end: Int = pos.end
+ def exists = pos.exists
+ }
+
+ val NoSourcePosition = SourcePosition(NoSource, NoPosition)
+
/** The coordinate of a symbol. This is either an index or
* a point position.
*/
diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala
new file mode 100644
index 000000000..a7a677560
--- /dev/null
+++ b/src/dotty/tools/dotc/util/SourceFile.scala
@@ -0,0 +1,109 @@
+package dotty.tools
+package dotc
+package util
+
+import scala.collection.mutable.ArrayBuffer
+import dotty.tools.io._
+import annotation.tailrec
+import java.util.regex.Pattern
+import java.io.IOException
+import Chars._
+import ScriptSourceFile._
+import Positions._
+
+object ScriptSourceFile {
+ private val headerPattern = Pattern.compile("""^(::)?!#.*(\r|\n|\r\n)""", Pattern.MULTILINE)
+ private val headerStarts = List("#!", "::#!")
+
+ def apply(file: AbstractFile, content: Array[Char]) = {
+ /** Length of the script header from the given content, if there is one.
+ * The header begins with "#!" or "::#!" and ends with a line starting
+ * with "!#" or "::!#".
+ */
+ val headerLength =
+ if (headerStarts exists (content startsWith _)) {
+ val matcher = headerPattern matcher content.mkString
+ if (matcher.find) matcher.end
+ else throw new IOException("script file does not close its header with !# or ::!#")
+ } else 0
+ new SourceFile(file, content drop headerLength) {
+ override val underlying = new SourceFile(file, content)
+ }
+ }
+}
+
+case class SourceFile(file: AbstractFile, content: Array[Char]) {
+
+ def this(_file: AbstractFile) = this(_file, _file.toCharArray)
+ def this(sourceName: String, cs: Seq[Char]) = this(new VirtualFile(sourceName), cs.toArray)
+ def this(file: AbstractFile, cs: Seq[Char]) = this(file, cs.toArray)
+
+ override def equals(that : Any) = that match {
+ case that : SourceFile => file.path == that.file.path && start == that.start
+ case _ => false
+ }
+ override def hashCode = file.path.## + start.##
+
+ val length = content.length
+
+ /** true for all source files except `NoSource` */
+ def exists: Boolean = true
+
+ /** The underlying source file */
+ def underlying: SourceFile = this
+
+ /** The start of this file in the underlying source file */
+ def start = 0
+
+ def atPos(pos: Position): SourcePosition =
+ SourcePosition(underlying, pos)
+
+ def isSelfContained = underlying eq this
+
+ /** Map a position to a position in the underlying source file.
+ * For regular source files, simply return the argument.
+ */
+ def positionInUltimateSource(position: SourcePosition): SourcePosition =
+ SourcePosition(underlying, position.pos shift start)
+
+ def isLineBreak(idx: Int) =
+ if (idx >= length) false else {
+ val ch = content(idx)
+ // don't identify the CR in CR LF as a line break, since LF will do.
+ if (ch == CR) (idx + 1 == length) || (content(idx + 1) != LF)
+ else isLineBreakChar(ch)
+ }
+
+ def calculateLineIndices(cs: Array[Char]) = {
+ val buf = new ArrayBuffer[Int]
+ buf += 0
+ for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1
+ buf += cs.length // sentinel, so that findLine below works smoother
+ buf.toArray
+ }
+ private lazy val lineIndices: Array[Int] = calculateLineIndices(content)
+
+ /** Map line to offset of first character in line */
+ def lineToOffset(index : Int): Int = lineIndices(index)
+
+ /** A cache to speed up offsetToLine searches to similar lines */
+ private var lastLine = 0
+
+ /** Convert offset to line in this source file
+ * Lines are numbered from 0
+ */
+ def offsetToLine(offset: Int): Int = {
+ val lines = lineIndices
+ def findLine(lo: Int, hi: Int, mid: Int): Int =
+ if (offset < lines(mid)) findLine(lo, mid - 1, (lo + mid - 1) / 2)
+ else if (offset >= lines(mid + 1)) findLine(mid + 1, hi, (mid + 1 + hi) / 2)
+ else mid
+ lastLine = findLine(0, lines.length, lastLine)
+ lastLine
+ }
+}
+
+object NoSource extends SourceFile("<no source>", Nil) {
+ override def exists = false
+}
+