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

org.nalloc.bitb.kcits.macros.OptionalMacros.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 Adam Rosenberger
 *
 * 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 org.nalloc.bitb.kcits.macros

import scala.language.existentials
import scala.reflect.macros.blackbox._

private[kcits] trait OptionalResolver[T] {
  type OptionalType
}

private[kcits] trait PrimitiveResolver[T] {
  type PrimitiveType
}

object OptionalMacros {

  def map_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(f: c.Expr[A => B])(x: c.Expr[OptionalResolver[B]]): c.Expr[x.value.OptionalType] = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinelTo = sentinelValue[B](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    val optionalType = x.tree.tpe.decls
        .find(x => x.isType && x.name == TypeName("OptionalType"))
        .map(_.typeSignature)
        .getOrElse(c.abort(c.enclosingPosition, "Couldn't determine optional type"))

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard)
      new $optionalType($f($underlying))
    else
      new $optionalType($sentinelTo)
    """)
  }

  def flatMap_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(f: c.Expr[A => B])(x: c.Expr[PrimitiveResolver[B]]): c.Expr[B] = {
    import c.universe._

    val underlying = underlyingValue[A](c)

    val primitiveType = x.tree.tpe.decls
        .find(x => x.isType && x.name == TypeName("PrimitiveType"))
        .map(_.typeSignature)
        .getOrElse(c.abort(c.enclosingPosition, "Couldn't determine optional type"))

    val sentinelTo = sentinelValueFor(c)(primitiveType)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)
    val optionalType = c.macroApplication.tpe

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard)
      $f($underlying)
    else
      new $optionalType($sentinelTo)
    """)
  }

  def foreach_impl[A: c.WeakTypeTag](c: Context)(f: c.Expr[A => Unit]): c.Expr[Unit] = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard)
      $f($underlying)
    """)
  }

  def exists_impl[A: c.WeakTypeTag](c: Context)(f: c.Expr[A => Boolean]): c.Expr[Boolean] = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    new Inliner[c.type](c).inlineAndReset(q"$sentinelGuard && $f($underlying)")
  }

  def filter_impl[A: c.WeakTypeTag](c: Context)(f: c.Expr[A => Boolean]) = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinel = sentinelValue[A](c)
    val optionalType = c.macroApplication.tpe
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard && $f($underlying))
      ${c.prefix.tree}
    else
      new $optionalType($sentinel)
    """)
  }

  def getOrElse_impl[A: c.WeakTypeTag](c: Context)(f: c.Expr[A]): c.Expr[A] = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard)
      $underlying
    else
      $f
    """)
  }

  def orElse_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(f: c.Expr[B]): c.Expr[B] = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard)
      ${c.prefix.tree}
    else
      $f
    """)
  }

  def fold_impl[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(ifEmpty: c.Expr[B])(f: c.Expr[A => B]): c.Expr[B] = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard)
      $f($underlying)
    else
      $ifEmpty
    """)
  }

  def forAll_impl[A: c.WeakTypeTag](c: Context)(f: c.Expr[A => Boolean]): c.Expr[Boolean] = {
    import c.universe._

    val underlying = underlyingValue[A](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)

    new Inliner[c.type](c).inlineAndReset(q"!$sentinelGuard || $f($underlying)")
  }

  def collect[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context)(pf: c.Expr[PartialFunction[A, B]])(x: c.Expr[OptionalResolver[B]]): c.Expr[x.value.OptionalType] = {
    import c.universe._

    val cases = pf.tree.collect {
      case q"$_ def applyOrElse[..$_](..$_): $_ = $_ match { case ..$cases case $_ => $_ }" => cases
    }.flatten

    val underlying = underlyingValue[A](c)
    val sentinelGuard = generateSentinelGuard[A](c)(underlying)
    val sentinelTo = sentinelValue[B](c)
    val optionalType = c.macroApplication.tpe

    val hasDefault = cases.exists {
      case cq"$_ => $_" => true
      case _            => false
    }

    val patternMatch =
      if (hasDefault) {
        q"""
				  new $optionalType($underlying match {
				    case ..$cases 
				  })
				"""
      }
      else {
        q"""
        new $optionalType($underlying match {
				    case ..$cases
					  case _ => $sentinelTo
				  })
    		"""
      }

    new Inliner[c.type](c).inlineAndReset( q"""
    if ($sentinelGuard) {
    	$patternMatch
    }
    else {
      new $optionalType($sentinelTo)
    }
    """)
  }

  private def underlyingValue[A: c.WeakTypeTag](c: Context) = {
    import c.universe._

    c.Expr[A](Select(c.prefix.tree, TermName("value")))
  }

  private def generateSentinelGuard[A: c.WeakTypeTag](c: Context)(underlying: c.Expr[A]) = {
    import c.universe._

    val sentinel = sentinelValue[A](c)

    if (isFloatingPointType[A](c)) q"$underlying == $underlying"
    else q"$sentinel != $underlying"
  }

  private def isFloatingPointType[A: c.WeakTypeTag](c: Context) = c.weakTypeTag[A].tpe match {
    case x if x =:= c.WeakTypeTag.Float.tpe || x =:= c.WeakTypeTag.Double.tpe => true
    case _                                                                    => false
  }

  private def sentinelValue[X: c.WeakTypeTag](c: Context): c.universe.Tree =
    sentinelValueFor(c)(c.weakTypeTag[X].tpe)

  private def sentinelValueFor(c: Context)(underlyingType: c.universe.Type): c.universe.Tree = {
    import c.universe._

    underlyingType match {
      case x if x =:= c.WeakTypeTag.Byte.tpe   => q"-128"
      case x if x =:= c.WeakTypeTag.Short.tpe  => q"-32768"
      case x if x =:= c.WeakTypeTag.Int.tpe    => q"-2147483648"
      case x if x =:= c.WeakTypeTag.Long.tpe   => q"0x8000000000000000L"
      case x if x =:= c.WeakTypeTag.Float.tpe  => q"java.lang.Float.NaN"
      case x if x =:= c.WeakTypeTag.Double.tpe => q"java.lang.Double.NaN"
      case x if x <:< c.WeakTypeTag.AnyRef.tpe => q"null"
      case x                                   => c.abort(c.enclosingPosition, s"Type $x does not support sentinel value checks")
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy