summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-07-06 14:04:27 +0200
committerDominik Gruntz <dominik.gruntz@fhnw.ch>2012-07-06 14:50:57 +0200
commit623b739bde7abce5271416fbd07e91dd547fd33f (patch)
tree76e439e3a0cf4530a96953cda68b7c302f8a12f1 /src/compiler
parentc39c7276c38f9ef66fd7054609ef33627efe5177 (diff)
downloadscala-623b739bde7abce5271416fbd07e91dd547fd33f.tar.gz
scala-623b739bde7abce5271416fbd07e91dd547fd33f.tar.bz2
scala-623b739bde7abce5271416fbd07e91dd547fd33f.zip
macro-based string interpolation formatter
This commit provides a macro based string interpolation formatter. The macro is implemented in MacroImplementations.scala. In order to still be able to build a new STARR, the implementation in StringContext.f is not yet changed. This will be replaced in a later commit.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/reflect/FastTrack.scala6
-rw-r--r--src/compiler/scala/tools/reflect/MacroImplementations.scala148
2 files changed, 152 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/reflect/FastTrack.scala b/src/compiler/scala/tools/reflect/FastTrack.scala
index 59160f87d0..8ea66979bc 100644
--- a/src/compiler/scala/tools/reflect/FastTrack.scala
+++ b/src/compiler/scala/tools/reflect/FastTrack.scala
@@ -15,8 +15,9 @@ trait FastTrack {
import definitions._
import language.implicitConversions
- private implicit def context2taggers(c0: MacroContext) : Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers
- private implicit def context2contextreifiers(c0: MacroContext) : ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers
+ private implicit def context2taggers(c0: MacroContext): Taggers { val c: c0.type } = new { val c: c0.type = c0 } with Taggers
+ private implicit def context2contextreifiers(c0: MacroContext): ContextReifiers { val c: c0.type } = new { val c: c0.type = c0 } with ContextReifiers
+ private implicit def context2macroimplementations(c0: MacroContext): MacroImplementations { val c: c0.type } = new { val c: c0.type = c0 } with MacroImplementations
implicit def fastTrackEntry2MacroRuntime(entry: FastTrackEntry): MacroRuntime = args => entry.run(args)
type FastTrackExpander = PartialFunction[(MacroContext, Tree), Tree]
@@ -42,6 +43,7 @@ trait FastTrack {
ApiUniverseReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExpr(c.prefix.tree, EmptyTree, expr) }
MacroContextReify bindTo { case (c, Apply(TypeApply(_, List(tt)), List(expr))) => c.materializeExprForMacroContext(c.prefix.tree, expr) }
ReflectRuntimeCurrentMirror bindTo { case (c, _) => scala.reflect.runtime.Macros.currentMirror(c).tree }
+ StringContext_f bindTo { case (c, Apply(Select(Apply(_, parts), _), args)) => c.macro_StringInterpolation_f(parts, args) }
registry
}
} \ No newline at end of file
diff --git a/src/compiler/scala/tools/reflect/MacroImplementations.scala b/src/compiler/scala/tools/reflect/MacroImplementations.scala
new file mode 100644
index 0000000000..8e5f4d8d04
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/MacroImplementations.scala
@@ -0,0 +1,148 @@
+package scala.tools.reflect
+
+import scala.reflect.makro.{ReificationError, UnexpectedReificationError}
+import scala.reflect.makro.runtime.Context
+import scala.collection.mutable.ListBuffer
+import scala.collection.mutable.Stack
+
+abstract class MacroImplementations {
+ val c: Context
+
+ import c.universe._
+
+ def macro_StringInterpolation_f(parts: List[Tree], args: List[Tree]): Tree = {
+ // the parts all have the same position information (as the expression is generated by the compiler)
+ // the args have correct position information
+
+ // the following conditions can only be violated if invoked directly
+ if (parts.length != args.length + 1) {
+ if(parts.length == 0)
+ c.abort(c.prefix.tree.pos, "too few parts")
+ else if(args.length + 1 < parts.length)
+ c.abort(if(args.length==0) c.enclosingPosition else args.last.pos,
+ "too few arguments for interpolated string")
+ else
+ c.abort(args(parts.length-1).pos,
+ "too many arguments for interpolated string")
+ }
+
+ val stringParts = parts map {
+ case Literal(Constant(s: String)) => s;
+ case _ => throw new IllegalArgumentException("argument parts must be a list of string literals")
+ }
+
+ val pi = stringParts.iterator
+ val bldr = new java.lang.StringBuilder
+ val evals = ListBuffer[ValDef]()
+ val ids = ListBuffer[Ident]()
+ val argsStack = Stack(args : _*)
+
+ def defval(value: Tree, tpe: Type): Unit = {
+ val freshName = newTermName(c.fresh("arg$"))
+ evals += ValDef(Modifiers(), freshName, TypeTree(tpe), value)
+ ids += Ident(freshName)
+ }
+
+ def isFlag(ch: Char): Boolean = {
+ ch match {
+ case '-' | '#' | '+' | ' ' | '0' | ',' | '(' => true
+ case _ => false
+ }
+ }
+
+ def checkType(arg: Tree, variants: Type*): Option[Type] = {
+ variants.find(arg.tpe <:< _).orElse(
+ variants.find(c.inferImplicitView(arg, arg.tpe, _) != EmptyTree).orElse(
+ Some(variants(0))
+ )
+ )
+ }
+
+ def conversionType(ch: Char, arg: Tree): Option[Type] = {
+ ch match {
+ case 'b' | 'B' =>
+ if(arg.tpe <:< NullTpe) Some(NullTpe) else Some(BooleanTpe)
+ case 'h' | 'H' =>
+ Some(AnyTpe)
+ case 's' | 'S' =>
+ Some(AnyTpe)
+ case 'c' | 'C' =>
+ checkType(arg, CharTpe, ByteTpe, ShortTpe, IntTpe)
+ case 'd' | 'o' | 'x' | 'X' =>
+ checkType(arg, IntTpe, LongTpe, ByteTpe, ShortTpe, typeOf[BigInt], typeOf[java.math.BigInteger])
+ // BigInteger can be removed as soon as an implicit conversion is defined from BigInteger to BigInt
+ case 'e' | 'E' | 'g' | 'G' | 'f' | 'a' | 'A' =>
+ checkType(arg, DoubleTpe, FloatTpe, typeOf[BigDecimal])
+ case 't' | 'T' =>
+ checkType(arg, LongTpe, typeOf[java.util.Calendar], typeOf[java.util.Date])
+ case _ => None
+ }
+ }
+
+ def copyString(first: Boolean): Unit = {
+ val str = StringContext.treatEscapes(pi.next())
+ val strLen = str.length
+ val strIsEmpty = strLen == 0
+ var start = 0
+ var idx = 0
+
+ if (!first) {
+ val arg = argsStack.pop
+ if (strIsEmpty || (str charAt 0) != '%') {
+ bldr append "%s"
+ defval(arg, AnyTpe)
+ } else {
+ // PRE str is not empty and str(0) == '%'
+ // argument index parameter is not allowed, thus parse
+ // [flags][width][.precision]conversion
+ var pos = 1
+ while(pos < strLen && isFlag(str charAt pos)) pos += 1
+ while(pos < strLen && Character.isDigit(str charAt pos)) pos += 1
+ if(pos < strLen && str.charAt(pos) == '.') { pos += 1
+ while(pos < strLen && Character.isDigit(str charAt pos)) pos += 1
+ }
+ if(pos < strLen) {
+ conversionType(str charAt pos, arg) match {
+ case Some(tpe) => defval(arg, tpe)
+ case None => c.error(arg.pos, "illegal conversion character")
+ }
+ } else {
+ // TODO: place error message on conversion string
+ c.error(arg.pos, "wrong conversion string")
+ }
+ }
+ idx = 1
+ }
+ if (!strIsEmpty) {
+ val len = str.length
+ while (idx < len) {
+ if (str(idx) == '%') {
+ bldr append (str substring (start, idx)) append "%%"
+ start = idx + 1
+ }
+ idx += 1
+ }
+ bldr append (str substring (start, idx))
+ }
+ }
+
+ copyString(first = true)
+ while (pi.hasNext) {
+ copyString(first = false)
+ }
+
+ val fstring = bldr.toString
+// val expr = c.reify(fstring.format((ids.map(id => Expr(id).eval)) : _*))
+// https://issues.scala-lang.org/browse/SI-5824, therefore
+ val expr =
+ Apply(
+ Select(
+ Literal(Constant(fstring)),
+ newTermName("format")),
+ List(ids: _* )
+ );
+
+ Block(evals.toList, expr)
+ }
+
+} \ No newline at end of file