Skip to content

Commit 44c1ec1

Browse files
committed
Reformulate core/Decorators.scala with extension methods
1 parent 68e2c9f commit 44c1ec1

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package dotty.tools.dotc
2+
package core
3+
4+
import annotation.tailrec
5+
import Symbols._
6+
import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer
7+
import util.Spans.Span, util.SourcePosition
8+
import collection.mutable.ListBuffer
9+
import dotty.tools.dotc.transform.MegaPhase
10+
import ast.tpd._
11+
import scala.language.implicitConversions
12+
import printing.Formatting._
13+
14+
/** This object provides useful conversions and extension methods for types defined elsewhere */
15+
object DecoratorsX {
16+
17+
/** Turns Strings into PreNames, adding toType/TermName methods */
18+
class StringPreName(s: String) extends AnyVal with PreName {
19+
def toTypeName: TypeName = typeName(s)
20+
def toTermName: TermName = termName(s)
21+
def toText(printer: Printer): Text = Str(s)
22+
}
23+
implied for Conversion[String, StringPreName] = new StringPreName(_)
24+
25+
final val MaxFilterRecursions = 1000
26+
27+
implied {
28+
def (s: String) splitWhere (f: Char => Boolean, doDropIndex: Boolean): Option[(String, String)] = {
29+
def splitAt(idx: Int, doDropIndex: Boolean): Option[(String, String)] =
30+
if (idx == -1) None
31+
else Some((s.take(idx), s.drop(if (doDropIndex) idx + 1 else idx)))
32+
33+
splitAt(s.indexWhere(f), doDropIndex)
34+
}
35+
36+
/** Implements a findSymbol method on iterators of Symbols that
37+
* works like find but avoids Option, replacing None with NoSymbol.
38+
*/
39+
def (it: Iterator[Symbol]) findSymbol(p: Symbol => Boolean): Symbol = {
40+
while (it.hasNext) {
41+
val sym = it.next()
42+
if (p(sym)) return sym
43+
}
44+
NoSymbol
45+
}
46+
47+
def (xs: List[T]) mapconserve [T, U] (f: T => U): List[U] = {
48+
@tailrec
49+
def loop(mapped: ListBuffer[U], unchanged: List[U], pending: List[T]): List[U] =
50+
if (pending.isEmpty) {
51+
if (mapped eq null) unchanged
52+
else mapped.prependToList(unchanged)
53+
} else {
54+
val head0 = pending.head
55+
val head1 = f(head0)
56+
57+
if (head1.asInstanceOf[AnyRef] eq head0.asInstanceOf[AnyRef])
58+
loop(mapped, unchanged, pending.tail)
59+
else {
60+
val b = if (mapped eq null) new ListBuffer[U] else mapped
61+
var xc = unchanged
62+
while (xc ne pending) {
63+
b += xc.head
64+
xc = xc.tail
65+
}
66+
b += head1
67+
val tail0 = pending.tail
68+
loop(b, tail0.asInstanceOf[List[U]], tail0)
69+
}
70+
}
71+
loop(null, xs.asInstanceOf[List[U]], xs)
72+
}
73+
74+
/** Like `xs filter p` but returns list `xs` itself - instead of a copy -
75+
* if `p` is true for all elements and `xs` is not longer
76+
* than `MaxFilterRecursions`.
77+
*/
78+
def (xs: List[T]) filterConserve [T] (p: T => Boolean): List[T] = {
79+
def loop(xs: List[T], nrec: Int): List[T] = xs match {
80+
case Nil => xs
81+
case x :: xs1 =>
82+
if (nrec < MaxFilterRecursions) {
83+
val ys1 = loop(xs1, nrec + 1)
84+
if (p(x))
85+
if (ys1 eq xs1) xs else x :: ys1
86+
else
87+
ys1
88+
} else xs filter p
89+
}
90+
loop(xs, 0)
91+
}
92+
93+
/** Like `(xs, ys).zipped.map(f)`, but returns list `xs` itself
94+
* - instead of a copy - if function `f` maps all elements of
95+
* `xs` to themselves. Also, it is required that `ys` is at least
96+
* as long as `xs`.
97+
*/
98+
def (xs: List[T]) zipWithConserve [T, U] (ys: List[U])(f: (T, U) => T): List[T] =
99+
if (xs.isEmpty || ys.isEmpty) Nil
100+
else {
101+
val x1 = f(xs.head, ys.head)
102+
val xs1 = xs.tail.zipWithConserve(ys.tail)(f)
103+
if ((x1.asInstanceOf[AnyRef] eq xs.head.asInstanceOf[AnyRef]) &&
104+
(xs1 eq xs.tail)) xs
105+
else x1 :: xs1
106+
}
107+
108+
def (xs: List[T]) hasSameLengthAs [T, U] (ys: List[U]): Boolean = {
109+
@tailrec def loop(xs: List[T], ys: List[U]): Boolean =
110+
if (xs.isEmpty) ys.isEmpty
111+
else ys.nonEmpty && loop(xs.tail, ys.tail)
112+
loop(xs, ys)
113+
}
114+
115+
@tailrec
116+
def (xs: List[T]) eqElements [T] (ys: List[AnyRef]): Boolean = xs match {
117+
case x :: _ =>
118+
ys match {
119+
case y :: _ =>
120+
x.asInstanceOf[AnyRef].eq(y) &&
121+
xs.tail.eqElements(ys.tail)
122+
case _ => false
123+
}
124+
case nil => ys.isEmpty
125+
}
126+
127+
def (xs: List[T]) | [T] (ys: List[T]): List[T] = xs ::: (ys filterNot (xs contains _))
128+
129+
/** Intersection on lists seen as sets */
130+
def (xs: List[T]) & [T] (ys: List[T]): List[T] = xs filter (ys contains _)
131+
132+
def (xss: List[List[T]]) nestedMap [T, U] (f: T => U): List[List[U]] = xss map (_ map f)
133+
def (xss: List[List[T]]) nestedMapconserve [T, U](f: T => U): List[List[U]] = xss mapconserve (_ mapconserve f)
134+
135+
def (text: Text) show given (ctx: Context): String =
136+
text.mkString(ctx.settings.pageWidth.value, ctx.settings.printLines.value)
137+
138+
/** Test whether a list of strings representing phases contains
139+
* a given phase. See [[config.CompilerCommand#explainAdvanced]] for the
140+
* exact meaning of "contains" here.
141+
*/
142+
def (names: List[String]) containsPhase (phase: Phase): Boolean =
143+
names.nonEmpty && {
144+
phase match {
145+
case phase: MegaPhase => phase.miniPhases.exists(names.containsPhase(_))
146+
case _ =>
147+
names exists { name =>
148+
name == "all" || {
149+
val strippedName = name.stripSuffix("+")
150+
val logNextPhase = name != strippedName
151+
phase.phaseName.startsWith(strippedName) ||
152+
(logNextPhase && phase.prev.phaseName.startsWith(strippedName))
153+
}
154+
}
155+
}
156+
}
157+
158+
def (x: T) reporting [T] (op: T => String, printer: config.Printers.Printer = config.Printers.default): T = {
159+
printer.println(op(x))
160+
x
161+
}
162+
163+
def (x: T) assertingErrorsReported [T] given Context: T = {
164+
assert(the[Context].reporter.errorsReported)
165+
x
166+
}
167+
168+
def (x: T) assertingErrorsReported [T] (msg: => String) given Context: T = {
169+
assert(the[Context].reporter.errorsReported, msg)
170+
x
171+
}
172+
173+
/** General purpose string formatting */
174+
def (sc: StringContext) i (args: Any*) given Context: String =
175+
new StringFormatter(sc).assemble(args)
176+
177+
/** Formatting for error messages: Like `i` but suppress follow-on
178+
* error messages after the first one if some of their arguments are "non-sensical".
179+
*/
180+
def (sc: StringContext) em (args: Any*) given Context: String =
181+
new ErrorMessageFormatter(sc).assemble(args)
182+
183+
/** Formatting with added explanations: Like `em`, but add explanations to
184+
* give more info about type variables and to disambiguate where needed.
185+
*/
186+
def (sc: StringContext) ex (args: Any*) given Context: String =
187+
explained(sc.em(args: _*))
188+
189+
/** Formatter that adds syntax highlighting to all interpolated values */
190+
def (sc: StringContext) hl (args: Any*) given Context: String =
191+
new SyntaxFormatter(sc).assemble(args).stripMargin
192+
193+
def (arr: Array[T]) binarySearch [T <: AnyRef] (x: T): Int =
194+
java.util.Arrays.binarySearch(arr.asInstanceOf[Array[Object]], x)
195+
}
196+
197+
/** Entrypoint for explanation string interpolator:
198+
*
199+
* ```
200+
* ex"disambiguate $tpe1 and $tpe2"
201+
* ```
202+
*/
203+
def explained(op: given Context => String) given Context: String =
204+
printing.Formatting.explained(ctx => op given ctx)
205+
}

0 commit comments

Comments
 (0)