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

loci.utility.Platform.scala Maven / Gradle / Ivy

The newest version!
package loci
package utility

import scala.reflect.NameTransformer
import scala.reflect.macros.{TypecheckException, blackbox}

object platform {
  def annotation(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    val q"new $expr(...$exprss).macroTransform(...$_)" = c.macroApplication: @unchecked

    if (exprss.size != 1 || exprss.head.size != 1)
      c.abort(expr.pos, "wrong number of arguments")

    val arg =
      try
        exprss.head.head match {
          case q"cond = $arg" => c typecheck arg
          case arg @ q"$name = $_" => c.abort(arg.pos, s"unknown parameter name: $name")
          case arg => c typecheck arg
        }
      catch {
        case TypecheckException(pos: Position @unchecked, msg) =>
          c.abort(pos, msg)
      }

    val keep = evaluateBooleanExpr(c)(arg)

    def compileTimeOnly(mods: Modifiers) = mods mapAnnotations {
      q"""new ${termNames.ROOTPKG}.scala.annotation.compileTimeOnly("Not available on current platform")""" :: _
    }

    def clearStatements(impl: Template): Template = {
      val body = impl.body flatMap {
        case tree @ ClassDef(mods, name, tparams, impl) =>
          Some(treeCopy.ClassDef(tree, mods, name, tparams, clearStatements(impl)))
        case tree @ ModuleDef(mods, name, impl) =>
          Some(treeCopy.ModuleDef(tree, mods, name, clearStatements(impl)))
        case tree: MemberDef =>
          Some(tree)
        case _ =>
          None
      }
      treeCopy.Template(impl, impl.parents, impl.self, body)
    }

    annottees match {
      case (annottee @ ClassDef(mods, name, tparams, impl)) :: companion =>
        val tree = if (keep) annottee else treeCopy.ClassDef(annottee, compileTimeOnly(mods), name, tparams, clearStatements(impl))
        companion.headOption.fold[Tree](tree) { companion => q"$tree; $companion" }

      case (annottee @ ModuleDef(mods, name, impl)) :: Nil =>
        if (keep) annottee else treeCopy.ModuleDef(annottee, compileTimeOnly(mods), name, clearStatements(impl))

      case (annottee @ DefDef(mods, name, tparams, vparamss, tpt, rhs)) :: Nil =>
        if (keep) annottee else treeCopy.DefDef(annottee, compileTimeOnly(mods), name, tparams, vparamss, tpt, rhs)

      case (annottee @ ValDef(mods, name, tpt, rhs)) :: Nil =>
        if (keep) annottee else treeCopy.ValDef(annottee, compileTimeOnly(mods), name, tpt, rhs)

      case _ =>
        c.abort(c.enclosingPosition,
          "platform annotation only applicable to classes, traits, objects or member definitions")
    }
  }

  def apply[T](c: blackbox.Context)(cond: c.Tree)(body: c.Tree): c.Tree = {
    import c.universe._
    if (evaluateBooleanExpr(c)(cond)) body else q"()"
  }

  def value[T](c: blackbox.Context)(selection: c.Tree*): c.Tree = {
    import c.universe._

    val unconditionalSelection = symbolOf[loci.platform.UnconditionalSelection]
    val conditionalSelection = symbolOf[loci.platform.Selection.type]
    val arrowAssoc = symbolOf[ArrowAssoc[_]]
    val tuple2 = symbolOf[Tuple2[_, _]]

    val selections = selection map {
      case q"$conditional[..$_]($selection)"
          if conditional.symbol.owner == conditionalSelection &&
             conditional.symbol.name.toString == "conditional" =>
        val conditionalExpr = selection match {
          case q"$arrow[..$_]($condition).->[..$_]($expr)"
              if arrow.symbol.isMethod &&
                 arrow.symbol.asMethod.returnType.typeSymbol == arrowAssoc =>
            Right(condition -> expr)
          case q"new $arrow[..$_]($condition).->[..$_]($expr)"
              if arrow.symbol == arrowAssoc =>
            Right(condition -> expr)
          case q"$pair[..$_]($condition, $expr)"
              if pair.symbol.isMethod &&
                 pair.symbol.asMethod.returnType.typeSymbol == tuple2 =>
            Right(condition -> expr)
          case q"new $pair[..$_]($condition, $expr)"
              if pair.symbol == tuple2 =>
            Right(condition -> expr)
          case q"($condition, $expr)" =>
            Right(condition -> expr)
          case _ =>
            Left(selection.pos)
        }

        compatibility.either.map(conditionalExpr) { case (condition, expr) =>
          evaluateBooleanExpr(c)(condition) -> expr
        }

      case q"$unconditional[..$_]($expr)"
          if unconditional.symbol.owner == unconditionalSelection &&
             unconditional.symbol.name.toString == "unconditional" =>
        Right(true -> expr)

      case selection
        if selection.tpe != null &&
           (selection.tpe <:< definitions.NothingTpe ||
            selection.tpe <:< definitions.NullTpe) =>
        Right(true -> selection)

      case selection =>
        Left(selection.pos)
    }

    selections foreach {
      case Left(pos) => c.abort(pos, "unexpected platform selection")
      case _ =>
    }

    (selections
      collectFirst { case Right((true, expr)) => expr }
      getOrElse c.abort(c.enclosingPosition, "no selection matches the current platform"))
  }

  private def evaluateBooleanExpr(c: blackbox.Context)(expr: c.Tree): Boolean = {
    import c.universe._

    val platform = symbolOf[loci.platform.type]

    def isStable(tree: Tree): Boolean = tree match {
      case Select(qualifier, _) if tree.symbol.isTerm && tree.symbol.asTerm.isStable =>
        isStable(qualifier)
      case Ident(_) if tree.symbol.isTerm && tree.symbol.asTerm.isStable =>
        true
      case Literal(_) =>
        true
      case _ =>
        false
    }

    expr match {
      case Apply(Select(qualifier, name), args) =>
        val operator = NameTransformer.decode(name.toString)
        val lhs = evaluateBooleanExpr(c)(qualifier)

        def rhs = {
          if (args.size != 1)
            c.abort(expr.pos, s"unexpected number of arguments for operator $operator: ${args.size}")
          evaluateBooleanExpr(c)(args.head)
        }

        operator match {
          case "==" => lhs == rhs
          case "!=" => lhs != rhs
          case "&" | "&&" => lhs & rhs
          case "|" | "||" => lhs | rhs
          case "^" => lhs ^ rhs
          case _ => c.abort(expr.pos, s"unknown operator: $operator")
        }

      case Select(qualifier, name) if expr.symbol.isTerm && !expr.symbol.asTerm.isStable =>
        val operator = NameTransformer.decode(name.toString)
        val value = evaluateBooleanExpr(c)(qualifier)

        if (operator != "unary_!")
          c.abort(expr.pos, s"unknown operator: $operator")

        !value

      case _
        if expr.symbol != null &&
           expr.symbol.isMethod &&
           expr.symbol.owner == platform &&
           expr.tpe != null &&
           expr.tpe <:< typeOf[Boolean] &&
           isStable(expr) =>
        try
          loci.platform.getClass.getMethod(expr.symbol.name.toString).invoke(loci.platform).asInstanceOf[Boolean]
        catch {
          case _: NoSuchMethodException | _: IllegalArgumentException | _: ClassCastException =>
            c.abort(expr.pos, s"failed to read value: $expr")
        }

      case _ if isStable(expr) =>
        if (!(expr.tpe <:< typeOf[Boolean]))
          c.abort(expr.pos, s"not a boolean expression: $expr")

        expr.tpe match {
          case ConstantType(Constant(value: Boolean)) =>
            value
          case _ =>
            c.abort(expr.pos, s"constant value not known at compile time: $expr")
        }

      case _ =>
        c.abort(expr.pos, s"not a constant expression: $expr")
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy