Skip to content

Commit

Permalink
Everything becomes better in paradise.
Browse files Browse the repository at this point in the history
Added detection of `"...".stripMargin` as string literals.
  • Loading branch information
makkarpov committed Apr 13, 2016
1 parent a8700f0 commit c1c7b73
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 217 deletions.
14 changes: 12 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ val common = Seq(
version := "0.2",

crossPaths := true,
scalaVersion := "2.11.7",
scalaVersion := "2.10.4",
crossScalaVersions := Seq("2.10.4", "2.11.7"),
scalacOptions ++= Seq( "-Xfatal-warnings", "-feature", "-deprecation" ),

Expand Down Expand Up @@ -62,7 +62,17 @@ lazy val scalingua = project
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided",
"org.scalatest" %% "scalatest" % "2.2.6" % Test
)
),

libraryDependencies ++= {
CrossVersion.binaryScalaVersion(scalaVersion.value) match {
case "2.10" => Seq(
compilerPlugin("org.scalamacros" % "paradise" % "2.0.0" cross CrossVersion.full),
"org.scalamacros" %% "quasiquotes" % "2.0.0"
)
case _ => Nil
}
}
)
.dependsOn(core)

Expand Down
123 changes: 1 addition & 122 deletions scalingua/src/main/scala-2.10/ru/makkarpov/scalingua/Compat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,128 +16,7 @@

package ru.makkarpov.scalingua

import ru.makkarpov.scalingua.extract.MessageExtractor

object Compat {
type Context = scala.reflect.macros.Context

def showCode(c: Context)(e: c.Tree): String = c.universe.show(e)

def tupleLiteral[T](c: Context)(e: c.Expr[(String, T)]): (String, c.Expr[T]) = {
import c.universe._

val (aTree, bTree) = e.tree match {
// matches AST for a -> b
case Apply(TypeApply(
Select(
Apply(TypeApply(
// scala.Predef.arrowAssoc (no extractor for type names. so match them manually)
Select(Select(This(tnScala: TypeName), tnPredef: Name), tnArrAss: TermName), List(TypeTree())
), List(a)),
// $minus$greater
tnMg: TermName
), List(TypeTree())), List(b)
) if (tnScala.decodedName.toString == "scala") && (tnPredef.decodedName.toString == "Predef") &&
(tnArrAss.decodedName.toString == "any2ArrowAssoc") && (tnMg.decodedName.toString == "->") => (a, b)

// matches AST for (a, b)
case Apply(
TypeApply(
Select(Select(Ident(tnScala), tnTuple), tnApply),
List(TypeTree(), TypeTree())
), List(a, b)
) if (tnScala.decodedName.toString == "scala") && (tnTuple.decodedName.toString == "Tuple2") &&
(tnApply.decodedName.toString == "apply") => (a, b)

case _ => c.abort(c.enclosingPosition, s"Expected tuple definition `x -> y` or `(x, y)`, got instead ${showCode(c)(e.tree)}")
}

(aTree match {
case Literal(Constant(x: String)) => x
case _ => c.abort(c.enclosingPosition, s"Expected string literal as first entry of tuple, got ${showCode(c)(aTree)} instead")
}, c.Expr[T](bTree))
}

def generateSingular[T: c.WeakTypeTag]
(c: Context)
(ctx: Option[String], str: String, args: Map[String, c.Tree])
(lang: c.Expr[Language], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{

MessageExtractor.singular(c)(ctx, str)

import c.universe._

val strLit = c.literal(str)
val tr = ctx match {
case Some(s) =>
val ctxLit = c.literal(s)
reify { lang.splice.singular(ctxLit.splice, strLit.splice) }
case None => reify { lang.splice.singular(strLit.splice) }
}

generateInterpolation[T](c)(tr, args, outputFormat).asInstanceOf[c.Expr[T]]
}

def generatePlural[T: c.WeakTypeTag]
(c: Context)
(ctx: Option[String], str: String, strPlural: String, n: c.Expr[Long], args: Map[String, c.Tree])
(lang: c.Expr[Language], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{

MessageExtractor.plural(c)(ctx, str, strPlural)

import c.universe._

val strLit = c.literal(str)
val strPluralLit = c.literal(strPlural)

val tr = ctx match {
case Some(s) =>
val ctxLit = c.literal(s)
reify { lang.splice.plural(ctxLit.splice, strLit.splice, strPluralLit.splice, n.splice) }

case None => reify { lang.splice.plural(strLit.splice, strPluralLit.splice, n.splice) }
}

generateInterpolation[T](c)(tr, args, outputFormat).asInstanceOf[c.Expr[T]]
}

private def generateInterpolation[T: c.WeakTypeTag]
(c: Context)
(str: c.Expr[String], args: Map[String, c.Tree], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{

import c.universe._

if (args.nonEmpty) {
val argList = flipSeq(c)(args.map {
case (name, value) =>
val nlit = c.literal(name)
val vlit = c.Expr[Any](value)

reify { nlit.splice -> vlit.splice }
}.toSeq)

reify {
StringUtils.interpolate[T](
str.splice,
argList.splice:_*
)(outputFormat.splice)
}
} else {
reify {
outputFormat.splice.convert(str.splice)
}
}
}

private def flipSeq[T](c: Context)(t: Seq[c.Expr[T]]): c.Expr[Seq[T]] = {
import c.universe._

c.Expr[Seq[T]](Apply(
Select(reify(Seq).tree, newTermName("apply")),
t.map(_.tree).toList
))
}
def prettyPrint(c: Context)(e: c.Tree): String = c.universe.show(e)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,75 +22,5 @@ import scala.reflect.macros.whitebox

object Compat {
type Context = whitebox.Context

// Everything becomes better with quasiquotes.

def showCode(c: Context)(e: c.Tree): String = c.universe.showCode(e)

def tupleLiteral[T](c: Context)(e: c.Expr[(String, T)]): (String, c.Expr[T]) = {
import c.universe._

val (aTree, bTree) = e.tree match {
case q"scala.this.Predef.ArrowAssoc[$_]($a).->[$_]($b)" => (a, b)
case q"($a, $b)" => (a, b)
case _ =>
c.abort(c.enclosingPosition, s"Expected tuple definition `x -> y` or `(x, y)`, got instead ${c.universe.showCode(e.tree)}")
}

val keyLiteral = aTree match {
case Literal(Constant(x: String)) => x
case _ => c.abort(c.enclosingPosition, s"Expected string literal as first entry of tuple, got ${c.universe.showCode(e.tree)} instead")
}

(keyLiteral, c.Expr[T](bTree))
}

def generateSingular[T: c.WeakTypeTag]
(c: Context)
(ctx: Option[String], str: String, args: Map[String, c.Tree])
(lang: c.Expr[Language], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{

MessageExtractor.singular(c)(ctx, str)

import c.universe._

val tr = c.Expr[String](ctx match {
case Some(s) => q"${lang.tree}.singular($s, $str)"
case None => q"${lang.tree}.singular($str)"
})

generateInterpolation[T](c)(tr, args, outputFormat).asInstanceOf[c.Expr[T]]
}

def generatePlural[T: c.WeakTypeTag]
(c: Context)
(ctx: Option[String], str: String, strPlural: String, n: c.Expr[Long], args: Map[String, c.Tree])
(lang: c.Expr[Language], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{

MessageExtractor.plural(c)(ctx, str, strPlural)

import c.universe._

val tr = c.Expr[String](ctx match {
case Some(s) => q"${lang.tree}.plural($s, $str, $strPlural, ${n.tree})"
case None => q"${lang.tree}.plural($str, $strPlural, ${n.tree})"
})

generateInterpolation[T](c)(tr, args, outputFormat).asInstanceOf[c.Expr[T]]
}

private def generateInterpolation[T: c.WeakTypeTag]
(c: Context)
(str: c.Expr[String], args: Map[String, c.Tree], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{

import c.universe._

c.Expr[T](if (args.nonEmpty) {
val argList = args.map { case (k, v) => q"$k -> $v" }
q"_root_.ru.makkarpov.scalingua.StringUtils.interpolate[${weakTypeOf[T]}](${str.tree}, ..$argList)(${outputFormat.tree})"
} else q"${outputFormat.tree}.convert(${str.tree})")
}
def prettyPrint(c: Context)(e: c.Tree): String = c.universe.showCode(e)
}
112 changes: 112 additions & 0 deletions scalingua/src/main/scala/ru/makkarpov/scalingua/MacroUtils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/******************************************************************************
* Copyright © 2016 Maxim Karpov *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/

package ru.makkarpov.scalingua

import Compat._
import ru.makkarpov.scalingua.extract.MessageExtractor

protected object MacroUtils {
def stringLiteral[T](c: Context)(e: c.Tree): String = {
import c.universe._

e match {
case Literal(Constant(s: String)) => s

case q"scala.this.Predef.augmentString($str).stripMargin" =>
str match {
case Literal(Constant(s: String)) => s.stripMargin.trim
case _ => c.abort(c.enclosingPosition, s"Expected string literal, got instead ${prettyPrint(c)(str)}")
}

case q"scala.this.Predef.augmentString($str).stripMargin($ch)" =>
(str, ch) match {
case (Literal(Constant(s: String)), Literal(Constant(c: Char))) => s.stripMargin(c).trim
case (Literal(Constant(s: String)), _) =>
c.abort(c.enclosingPosition, s"Expected character literal, got instead ${prettyPrint(c)(ch)}")
case _ => c.abort(c.enclosingPosition, s"Expected string literal, got instead ${prettyPrint(c)(str)}")
}

case _ =>
c.abort(c.enclosingPosition, s"Expected string literal or multi-line string, got instead ${prettyPrint(c)(e)}")
}
}

def tupleLiteral[T](c: Context)(e: c.Expr[(String, T)]): (String, c.Expr[T]) = {
import c.universe._

val (aTree, bTree) = e.tree match {
case q"scala.this.Predef.ArrowAssoc[$aType]($a).->[$bType]($b)" => (a, b) // 2.11
case q"scala.this.Predef.any2ArrowAssoc[$aType]($a).->[$bType]($b)" => (a, b) // 2.10
case q"($a, $b)" => (a, b)
case _ =>
c.abort(c.enclosingPosition, s"Expected tuple definition `x -> y` or `(x, y)`, got instead ${prettyPrint(c)(e.tree)}")
}

val keyLiteral = aTree match {
case Literal(Constant(x: String)) => x
case _ => c.abort(c.enclosingPosition, s"Expected string literal as first entry of tuple, got ${prettyPrint(c)(e.tree)} instead")
}

(keyLiteral, c.Expr[T](bTree))
}

def generateSingular[T: c.WeakTypeTag]
(c: Context)
(ctx: Option[String], str: String, args: Map[String, c.Tree])
(lang: c.Expr[Language], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{
MessageExtractor.singular(c)(ctx, str)

import c.universe._

val tr = c.Expr[String](ctx match {
case Some(s) => q"${lang.tree}.singular($s, $str)"
case None => q"${lang.tree}.singular($str)"
})

generateInterpolation[T](c)(tr, args, outputFormat).asInstanceOf[c.Expr[T]]
}

def generatePlural[T: c.WeakTypeTag]
(c: Context)
(ctx: Option[String], str: String, strPlural: String, n: c.Expr[Long], args: Map[String, c.Tree])
(lang: c.Expr[Language], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{
MessageExtractor.plural(c)(ctx, str, strPlural)

import c.universe._

val tr = c.Expr[String](ctx match {
case Some(s) => q"${lang.tree}.plural($s, $str, $strPlural, ${n.tree})"
case None => q"${lang.tree}.plural($str, $strPlural, ${n.tree})"
})

generateInterpolation[T](c)(tr, args, outputFormat).asInstanceOf[c.Expr[T]]
}

private def generateInterpolation[T: c.WeakTypeTag]
(c: Context)
(str: c.Expr[String], args: Map[String, c.Tree], outputFormat: c.Expr[OutputFormat[T]]): c.Expr[T] =
{
import c.universe._

c.Expr[T](if (args.nonEmpty) {
val argList = args.map { case (k, v) => q"$k -> $v" }
q"_root_.ru.makkarpov.scalingua.StringUtils.interpolate[${weakTypeOf[T]}](${str.tree}, ..$argList)(${outputFormat.tree})"
} else q"${outputFormat.tree}.convert(${str.tree})")
}
}
Loading

0 comments on commit c1c7b73

Please sign in to comment.