Skip to content

Commit

Permalink
Don't slay the compiler (#345)
Browse files Browse the repository at this point in the history
I created too huge patterns to be matched against. Those crashed the
compiler.
This change optimizes the pattern (and the runtime const) of huge
constants (numbers and characters) and other terms, whose representation
is very large (int-lists and char-lists/strings).
  • Loading branch information
keddelzz committed Apr 18, 2017
1 parent 2fbe29b commit a60604b
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
37 changes: 35 additions & 2 deletions macros/src/main/scala/me/rexim/morganey/meta/QuotationMacro.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package me.rexim.morganey.meta

import me.rexim.morganey.ast._
import me.rexim.morganey.meta.QuotationMacro._

import StringContext.treatEscapes
import scala.reflect.macros.whitebox

private[meta] object QuotationMacro {

val bigNumberThreshold = 10

}

private[meta] class QuotationMacro(val c: whitebox.Context) {
import c.universe._

Expand Down Expand Up @@ -35,6 +42,9 @@ private[meta] class QuotationMacro(val c: whitebox.Context) {

private val LambdaTermTpe = typeOf[LambdaTerm]

private lazy val morganeyNumber = implicitly[Unlift[Int]]
private lazy val morganeyIntList = implicitly[Unlift[List[Int]]]

private lazy val liftList = implicitly[Lift[List[LambdaTerm]]]
private lazy val liftList_ = c.inferImplicitValue(typeOf[Lift[List[LambdaTerm]]], silent = true)
private lazy val unliftList = implicitly[Unlift[List[LambdaTerm]]]
Expand All @@ -50,8 +60,8 @@ private[meta] class QuotationMacro(val c: whitebox.Context) {
private def unliftableT(tpe: Type): Type = appliedType(UnliftableTpe, tpe)

private case class Lifted(exp: Tree, preamble: List[Tree] = Nil) {
def define(decl: Tree): Lifted =
Lifted(exp, preamble :+ decl)
def define(decls: Tree*): Lifted =
Lifted(exp, preamble ++ decls.toList)

def wrap(f: Tree => Tree): Lifted =
Lifted(f(exp), preamble)
Expand Down Expand Up @@ -95,17 +105,40 @@ private[meta] class QuotationMacro(val c: whitebox.Context) {
private def liftPrimitiveTerm(term: LambdaTerm): Lifted = term match {
case LambdaVar(DottedHole(_)) =>
c.abort(c.enclosingPosition, "Illegal usage of ..!")

case LambdaVar(Hole(hole)) =>
replaceHole(hole, dotted = false) match {
case Left(x) => x
case Right((x, _)) => x
}

case LambdaVar(name) =>
Lifted(q"_root_.me.rexim.morganey.ast.LambdaVar($name)")

// generate special code for detecting int lists (also strings)
case morganeyIntList(xs) if isUnapply =>
val const = TermName(c.freshName("const"))
val preamble =
q"""
val $const =
new _root_.me.rexim.morganey.meta.UnapplyConst[_root_.scala.collection.immutable.List[Int]]($xs)
"""
Lifted(pq"$const()").define(preamble)

// generate special code for detecting big constant numbers and characters
case morganeyNumber(n) if isUnapply && n > bigNumberThreshold =>
val const = TermName(c.freshName("const"))
val preamble =
q"""
val $const = new _root_.me.rexim.morganey.meta.UnapplyConst[Int]($n)
"""
Lifted(pq"$const()").define(preamble)

case LambdaFunc(param, body) =>
liftPrimitiveTerm(param).wrap2(liftComplexTerm(body)) {
case (paramTree, bodyTree) => q"_root_.me.rexim.morganey.ast.LambdaFunc($paramTree, $bodyTree)"
}

case LambdaApp(left, right) =>
liftComplexTerm(left).wrap2(liftComplexTerm(right)) {
case (leftTree, rightTree) => q"_root_.me.rexim.morganey.ast.LambdaApp($leftTree, $rightTree)"
Expand Down
15 changes: 15 additions & 0 deletions macros/src/main/scala/me/rexim/morganey/meta/UnapplyConst.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.rexim.morganey.meta

import me.rexim.morganey.ast.LambdaTerm

/**
* Helper class to match a constant in the quotation at runtime
*/
class UnapplyConst[T](n: T)(implicit morganeyValue: Unliftable[T]) {

def unapply(term: LambdaTerm): Boolean = term match {
case morganeyValue(m) => n == m
case _ => false
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import me.rexim.morganey.monad._
import scala.collection.generic.CanBuildFrom
import scala.language.higherKinds

/**
* Helper class to convert each LambdaTerm to T in a collection
*/
class UnapplyEach[T, CC[+X] <: TraversableOnce[X]](unliftT: Unliftable[T])
(implicit cbf: CanBuildFrom[CC[LambdaTerm], T, CC[T]],
cbfo: CanBuildFrom[CC[LambdaTerm], Option[T], CC[Option[T]]]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ class InterpolatorSpec extends FlatSpec with Matchers with TestTerms {
m"$numbers" should be (pair(zero, pair(one, pair(two, zero, "z"), "z"), "z"))
}

"Matching against big strings" should "be possible with the quotation mechanism" in {
val string = "The quick brown fox jumps over the lazy dog"
// `string` is represented by 4057 applications in morganey
val m""" "The quick brown fox jumps over the lazy dog" """ = m"$string"
}

"Lambda terms" should "be converted back to Scala values during unlifting" in {
val m"${nZero: Int}" = zero
nZero should be (0)
Expand Down

0 comments on commit a60604b

Please sign in to comment.