Skip to content

Instantly share code, notes, and snippets.

@jedws
Last active December 29, 2015 05:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jedws/7622747 to your computer and use it in GitHub Desktop.
Save jedws/7622747 to your computer and use it in GitHub Desktop.
the two macro implementations are almost identical, but all attempts to share implementation are frustrated by obscure runtime problems
import reflect.macros.Context
import scalaz.{ -\/ , \/-, syntax }
import syntax.id._
import util.control.NonFatal
object Encoders {
trait Base {
def instance: String => Result[BigInt]
}
object Base16Macro extends Base {
val instance: String => Result[BigInt] = Encoding.B16.toBigInt
def encode(c: Context)(): c.Expr[BigInt] = MacroCommon.encodeImpl(c)(Base16Macro)
}
object Base32Macro extends Base {
val instance: String => Result[BigInt] = Encoding.B32.toBigInt
def encode(c: Context)(): c.Expr[BigInt] = MacroCommon.encodeImpl(c)(Base32Macro)
}
private[Encoders] object MacroCommon {
def encodeImpl(c: Context)(instance: Base with Singleton)(implicit itag: c.TypeTag[instance.type]): c.Expr[BigInt] = {
import c.universe._
val iSym = itag.tpe match {
case SingleType(_, sym) if !sym.isFreeTerm && sym.isStatic => sym
case x => sys.error("Instance must be static (was " + x + ").")
}
val i = c.Expr[Base](Ident(iSym))
c.Expr[BigInt] {
c.prefix.tree match {
case Apply(_, Apply(_, Literal(Constant(repr: String)) :: Nil) :: Nil) =>
instance.instance(repr) match {
case \/-(_) =>
val toTree = implicitly[Liftable[String]]
val s = c.Expr[String](toTree(c.universe, repr))
reify { i.splice.instance(s.splice).fold(_ => throw new RuntimeException, identity) }.tree
case -\/(e) =>
c.abort(c.enclosingPosition, "Invalid Base16 literal, parsing failed: " + e)
}
case _ => c.abort(c.enclosingPosition, "Invalid Base16 literal.")
}
}
}
}
}
object Encoding {
object B16 extends Base(('0' to '9') ++ ('A' to 'F'), 16)
object B32 extends Base(('A' to 'Z') ++ ('2' to '7'), 32)
class Base(chars: IndexedSeq[Char], base: Int) extends (Int => Char) {
def apply(i: Int) =
chars(i)
val contains: Char => Boolean =
c => chars.contains(c.toUpper)
val indexOf: Char => Int =
chars.indexOf(_)
def toBigIntUnsafe(s: String): BigInt =
if (s.forall { contains })
s.map { c => indexOf(c.toUpper) }.foldLeft(0: BigInt) { (a, b) => a * base + b }
else
throw new NumberFormatException(s"'$s' contains characters not in $chars")
def toBigInt(s: String): Exception \/ BigInt =
try toBigIntUnsafe(s).right
catch { case NonFatal(e) => e.left }
}
implicit class Base16EncodingMacro(sc: StringContext) {
def b16(): BigInt = macro Base16Macro.encode
}
implicit class Base32EncodingMacro(sc: StringContext) {
def b32(): BigInt = macro Base32Macro.encode
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment