All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.tools.reflect.MacroImplementations.scala Maven / Gradle / Ivy

There is a newer version: 2.11.2
Show newest version
package scala.tools.reflect

import scala.reflect.macros.{ReificationException, UnexpectedReificationException}
import scala.reflect.macros.runtime.Context
import scala.collection.mutable.ListBuffer
import scala.collection.mutable.Stack

abstract class MacroImplementations {
  val c: Context

  import c.universe._
  import definitions._

  def macro_StringInterpolation_f(parts: List[Tree], args: List[Tree], origApplyPos: c.universe.Position): 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) setPos value.pos.focus, value) setPos value.pos
      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))
        )
      )
    }

    val stdContextTags = new { val tc: c.type = c } with StdContextTags
    import stdContextTags._

    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, tagOfBigInt.tpe)
        case 'e' | 'E' | 'g' | 'G' | 'f' | 'a' | 'A'  =>
          checkType(arg, DoubleTpe, FloatTpe, tagOfBigDecimal.tpe)
        case 't' | 'T' =>
          checkType(arg, LongTpe, tagOfCalendar.tpe, tagOfDate.tpe)
        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, atPos(origApplyPos.focus)(expr)) setPos origApplyPos.makeTransparent
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy