Skip to content

Commit

Permalink
fixed the compilation for 2.10
Browse files Browse the repository at this point in the history
  • Loading branch information
etorreborre committed May 13, 2015
1 parent 05c518b commit 3bc0bf1
Showing 1 changed file with 109 additions and 51 deletions.
160 changes: 109 additions & 51 deletions common/src/main/scala-2.10/org/specs2/execute/Typecheck.scala
Expand Up @@ -21,41 +21,60 @@ object Typecheck {
* If the code doesn't parse there will be a compile-time error
*/
def apply(code: String): Typechecked = macro typecheckImpl
def apply(params: TypecheckParams)(code: String): Typechecked = macro typecheckExceptImpl

/** alias for apply */
def typecheck(code: String): Typechecked = macro typecheckImpl

def typecheckImpl(c: Context)(code: c.Expr[String]): c.Expr[Typechecked] = {
import c.universe._
code match {
case Expr(Literal(Constant(codeString: String))) =>
try {
c.typeCheck(c.parse(codeString))
c.Expr(q"Typechecked($codeString, TypecheckSuccess)")
} catch {
case TypecheckException(_, m) => c.Expr(q"Typechecked($codeString, TypecheckError($m))")
}

case other => c.Expr(q"""Typechecked("", CanTypecheckLiteralsOnly)""")
}
typecheckImplementation(c)(code, c.Expr(q"org.specs2.execute.TypecheckParams()"))
}

/**
* Typecheck code and fail at runtime if the code doesn't parse or typecheck
*/
def parseAndTypecheck(code: String): Typechecked = macro parseAndTypecheckImpl
def typecheckWith(params: TypecheckParams)(code: String): Typechecked = macro typecheckExceptImpl
def typecheckExceptImpl(c: Context)(params: c.Expr[TypecheckParams])(code: c.Expr[String]): c.Expr[Typechecked] =
typecheckImplementation(c)(code, params)

def parseAndTypecheckImpl(c: Context)(code: c.Expr[String]): c.Expr[Typechecked] = {
def typecheckImplementation(c: Context)(code: c.Expr[String], params: c.Expr[TypecheckParams]): c.Expr[Typechecked] = {
import c.universe._
code match {
case Expr(Literal(Constant(codeString: String))) =>
// evaluate the parameters
val ps = try {
c.eval(c.Expr(c.resetLocalAttrs(params.tree))).asInstanceOf[TypecheckParams]
} catch { case e: Exception => c.abort(c.enclosingPosition, "typecheck parameters must be passed directly to the typecheck macro") }
try {
c.typeCheck(c.parse(codeString))
c.Expr(q"Typechecked($codeString, TypecheckSuccess)")
// parse without macros first
val parsed = try {
c.parse(codeString)
} catch {
case ParseException(_, m) if ps.deferParsing =>
q"Typechecked($codeString, ParseError($m))"
case ParseException(_, m) if !ps.deferParsing =>
c.abort(c.enclosingPosition, m)
case e: Exception =>
q"Typechecked($codeString, UnexpectedTypecheckError(${e.getMessage}))"
}
c.typeCheck(parsed, withMacrosDisabled = true)
// if that's ok parse with macros
try {
c.typeCheck(parsed)
c.Expr(q"Typechecked($codeString, TypecheckSuccess)")
} catch {
// got a typecheck exception with macros
case TypecheckException(_, m) if ps.deferMacros =>
c.Expr(q"Typechecked($codeString, TypecheckError($m))")
case TypecheckException(_, m) if !ps.deferMacros =>
c.abort(c.enclosingPosition, m)
case e: Exception =>
c.Expr(q"Typechecked($codeString, UnexpectedTypecheckError(${e.getMessage}))")
}
} catch {
case TypecheckException(_, m) => c.Expr(q"Typechecked($codeString, TypecheckError($m))")
case ParseException(_, m) => c.Expr(q"Typechecked($codeString, ParseError($m))")
case e: Exception => c.Expr(q"Typechecked($codeString, UnexpectedTypecheckError(${e.getMessage}))")
// got a typecheck exception without macros
// if implicit errors are not deferred and this is an implicit error
case TypecheckException(_, m) if !ps.deferImplicits && m.startsWith("could not find implicit value") =>
c.abort(c.enclosingPosition, m)
case TypecheckException(_, m) =>
c.Expr(q"Typechecked($codeString, TypecheckError($m))")
}

case other => c.Expr(q"""Typechecked("", CanTypecheckLiteralsOnly)""")
Expand All @@ -69,47 +88,86 @@ object Typecheck {
* @return the parsed/typechecked code if it is ok or a Typechecked object otherwise
*/
implicit class typecheckQuote(val sc: StringContext) extends AnyVal {
def tc(variables: Any*): Any = macro typecheckCode
def tc(variables: Any*): Any = macro typecheckCodeImpl
}

def typecheckCode(c: Context)(variables: c.Expr[Any]*) : c.Expr[Any] = {
import c.{universe => u}; import u.{ Position => _, _ }

val texts = c.prefix.tree match { case Apply(_, List(Apply(_, ts))) => ts }
if (texts.size != 1)
c.Expr(q"""Typechecked(${texts.mkString}, TypecheckError("can only typecheck an interpolated string with no variables at the moment"))""")
else {
val code = texts.head.asInstanceOf[Literal].value.value.asInstanceOf[String]
try c.Expr(c.typeCheck(c.parse(code)))
catch {
case TypecheckException(_, m) => c.Expr(q"Typechecked($code, TypecheckError($m))")
}
}
def typecheckCodeImpl(c: Context)(variables: c.Expr[Any]*): c.Expr[Typechecked] = {
import c.universe._
typecheckCodeImplementation(c)(variables:_*)(c.Expr(q"org.specs2.execute.TypecheckParams()"))
}

/**
* Typecheck code and fail at runtime if the code doesn't parse or typecheck
*
* @return the parsed/typechecked code if it is ok or a Typechecked object otherwise
*/
implicit class parseAndTypecheckQuote(val sc: StringContext) extends AnyVal {
def ptc(variables: Any*): Any = macro parseAndTypecheckCode
implicit class typecheckWithQuote(val sc: StringContext) extends AnyVal {
def tcw(variables: Any*)(params: TypecheckParams): Any = macro typecheckCodeImplementation
}

def parseAndTypecheckCode(c: Context)(variables: c.Expr[Any]*) : c.Expr[Any] = {
def typecheckCodeImplementation(c: Context)(variables: c.Expr[Any]*)(params: c.Expr[TypecheckParams]) : c.Expr[Typechecked] = {
import c.{universe => u}; import u.{ Position => _, _ }

val texts = c.prefix.tree match { case Apply(_, List(Apply(_, ts))) => ts }
if (texts.size != 1)
c.Expr(q"""Typechecked(${texts.mkString}, TypecheckError("can only typecheck an interpolated string with no variables at the moment"))""")
else {
val code = texts.head.asInstanceOf[Literal].value.value.asInstanceOf[String]
try c.Expr(c.typeCheck(c.parse(code)))
catch {
case TypecheckException(_, m) => c.Expr(q"Typechecked($code, TypecheckError($m))")
case ParseException(_, m) => c.Expr(q"Typechecked($code, ParseError($m))")
case e: Exception => c.Expr(q"Typechecked($code, UnexpectedTypecheckError(${e.getMessage}))")
val codeString = texts.head.asInstanceOf[Literal].value.value.asInstanceOf[String]
// evaluate the parameters
val ps = try {
c.eval(c.Expr(c.resetLocalAttrs(params.tree))).asInstanceOf[TypecheckParams]
} catch { case e: Exception => c.abort(c.enclosingPosition, "typecheck parameters must be passed directly to the typecheck macro") }
try {
// parse without macros first
val parsed = try {
c.parse(codeString)
} catch {
case ParseException(_, m) if ps.deferParsing =>
q"Typechecked($codeString, ParseError($m))"
case ParseException(_, m) if !ps.deferParsing =>
c.abort(c.enclosingPosition, m)
case e: Exception =>
q"Typechecked($codeString, UnexpectedTypecheckError(${e.getMessage}))"
}
c.typeCheck(parsed, withMacrosDisabled = true)
// if that's ok parse with macros
try {
c.Expr(c.typeCheck(parsed))
} catch {
// got a typecheck exception with macros
case TypecheckException(_, m) if ps.deferMacros =>
c.Expr(q"Typechecked($codeString, TypecheckError($m))")
case TypecheckException(_, m) if !ps.deferMacros =>
c.abort(c.enclosingPosition, m)
case e: Exception =>
c.Expr(q"Typechecked($codeString, UnexpectedTypecheckError(${e.getMessage}))")
}
} catch {
// got a typecheck exception without macros
// if implicit errors are not deferred and this is an implicit error
case TypecheckException(_, m) if !ps.deferImplicits && m.startsWith("could not find implicit value") =>
c.abort(c.enclosingPosition, m)
case TypecheckException(_, m) =>
c.Expr(q"Typechecked($codeString, TypecheckError($m))")
}
}
}
}

val macrosAtCompileTime = TypecheckParams(_deferMacros = Some(false))
val implicitsAtCompileTime = TypecheckParams(_deferImplicits = Some(false))
val parsingAtRuntime = TypecheckParams(_deferParsing = Some(true))
}

case class TypecheckParams(_deferMacros: Option[Boolean] = None, _deferImplicits: Option[Boolean] = None, _deferParsing: Option[Boolean] = None) {

// by default we want macro expansion errors at runtime
def deferMacros = _deferMacros.getOrElse(true)

// by default we want implicit errors at runtime
def deferImplicits = _deferImplicits.getOrElse(true)

// by default we want parsing errors at compile time
def deferParsing = _deferParsing.getOrElse(false)

def <|(other: TypecheckParams): TypecheckParams =
TypecheckParams(
_deferMacros = _deferMacros.orElse(other._deferMacros),
_deferImplicits = _deferImplicits.orElse(other._deferImplicits),
_deferParsing = _deferParsing.orElse(other._deferParsing)
)
}

0 comments on commit 3bc0bf1

Please sign in to comment.