summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
diff options
context:
space:
mode:
authorDen Shabalin <den.shabalin@gmail.com>2013-07-08 20:48:17 +0200
committerEugene Burmako <xeno.by@gmail.com>2013-07-08 21:20:28 +0200
commit7184fe0d3740ac8558067c18bdf449a65a8a26b9 (patch)
tree34afa3886443f46121710eccde1be74c553dc386 /src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
parent32949c496e2703e05ff07fae8d19bf91fe733e71 (diff)
downloadscala-7184fe0d3740ac8558067c18bdf449a65a8a26b9.tar.gz
scala-7184fe0d3740ac8558067c18bdf449a65a8a26b9.tar.bz2
scala-7184fe0d3740ac8558067c18bdf449a65a8a26b9.zip
implements quasiquotes
- Additions to the reflection API: - The Quasiquotes implicit class that defines `q`, `tq`, `pq` and `cq` interpolators which now become a part of the `scala.reflect.api. Universe`. - Implementations of the interpolators are macro-based and are hardwired through `FastTrack`. - The `Liftable` class and the `StandardLiftables` slice of the cake that provide a type class and a bunch of its instances that allow to easily splice user-defined types into quasiquotes. - Additional methods in `BuildUtils` that are used by the quasiquote macro to generate trees, notably: - `SyntacticClassDef`. An extractor/constructor that allows to construct and deconstruct classes using arguments that mirror syntactic form of ClassDefs (e.g. constructor outside of the body). - `TupleN`, `TupleTypeN`. Extractor/constructor for easy construction of ast that represents a tuple term or type with given amount of elements. - Actual implementation of quasiquotes in the `scala.tools.reflect. quasiquotes` package which is organized into a cake called `Quasiquotes` with slices introducing core abstractions necessary to splice into Scala syntax, routines for interfacing with the parser, and customized reifiers for tree construction and deconstruction.
Diffstat (limited to 'src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala')
-rw-r--r--src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
new file mode 100644
index 0000000000..9a6ba56c18
--- /dev/null
+++ b/src/compiler/scala/tools/reflect/quasiquotes/Parsers.scala
@@ -0,0 +1,134 @@
+package scala.tools.reflect
+package quasiquotes
+
+import scala.tools.nsc.ast.parser.{Parsers => ScalaParser}
+import scala.tools.nsc.ast.parser.Tokens._
+import scala.compat.Platform.EOL
+import scala.reflect.internal.util.{BatchSourceFile, SourceFile}
+import scala.collection.mutable.ListBuffer
+
+/** Builds upon the vanilla Scala parser and teams up together with Placeholders.scala to emulate holes.
+ * A principled solution to splicing into Scala syntax would be a parser that natively supports holes.
+ * Unfortunately, that's outside of our reach in Scala 2.11, so we have to emulate.
+ */
+trait Parsers { self: Quasiquotes =>
+ import global._
+
+ abstract class Parser extends {
+ val global: self.global.type = self.global
+ } with ScalaParser {
+ /** Wraps given code to obtain a desired parser mode.
+ * This way we can just re-use standard parser entry point.
+ */
+ def wrapCode(code: String): String =
+ s"object wrapper { self => $EOL $code $EOL }"
+
+ def unwrapTree(wrappedTree: Tree): Tree = {
+ val PackageDef(_, List(ModuleDef(_, _, Template(_, _, _ :: parsed)))) = wrappedTree
+ parsed match {
+ case tree :: Nil => tree
+ case stats :+ tree => Block(stats, tree)
+ }
+ }
+
+ def parse(code: String): Tree = {
+ try {
+ val wrapped = wrapCode(code)
+ debug(s"wrapped code\n=${wrapped}\n")
+ val file = new BatchSourceFile(nme.QUASIQUOTE_FILE, wrapped)
+ val tree = new QuasiquoteParser(file).parse()
+ unwrapTree(tree)
+ } catch {
+ case mi: MalformedInput => c.abort(c.macroApplication.pos, s"syntax error: ${mi.msg}")
+ }
+ }
+
+ class QuasiquoteParser(source0: SourceFile) extends SourceFileParser(source0) {
+ override val treeBuilder = new ParserTreeBuilder {
+ // q"(..$xs)"
+ override def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree =
+ Apply(Ident(nme.QUASIQUOTE_TUPLE), trees)
+
+ // tq"(..$xs)"
+ override def makeTupleType(trees: List[Tree], flattenUnary: Boolean): Tree =
+ AppliedTypeTree(Ident(tpnme.QUASIQUOTE_TUPLE), trees)
+
+ // q"{ $x }"
+ override def makeBlock(stats: List[Tree]): Tree = stats match {
+ case (head @ Ident(name)) :: Nil if holeMap.contains(name) => Block(Nil, head)
+ case _ => super.makeBlock(stats)
+ }
+ }
+ import treeBuilder.{global => _, _}
+
+ // q"def foo($x)"
+ override def allowTypelessParams = true
+
+ // q"foo match { case $x }"
+ override def caseClause(): CaseDef =
+ if (isHole && lookingAhead { in.token == CASE || in.token == RBRACE || in.token == SEMI }) {
+ val c = makeCaseDef(Apply(Ident(nme.QUASIQUOTE_CASE), List(Ident(ident()))), EmptyTree, EmptyTree)
+ while (in.token == SEMI) in.nextToken()
+ c
+ } else
+ super.caseClause()
+
+ def isHole = isIdent && holeMap.contains(in.name)
+
+ override def isAnnotation: Boolean = super.isAnnotation || (isHole && lookingAhead { isAnnotation })
+
+ override def isModifier: Boolean = super.isModifier || (isHole && lookingAhead { isModifier })
+
+ override def isLocalModifier: Boolean = super.isLocalModifier || (isHole && lookingAhead { isLocalModifier })
+
+ override def isTemplateIntro: Boolean = super.isTemplateIntro || (isHole && lookingAhead { isTemplateIntro })
+
+ override def isDclIntro: Boolean = super.isDclIntro || (isHole && lookingAhead { isDclIntro })
+
+ // $mods def foo
+ // $mods T
+ override def readAnnots(annot: => Tree): List[Tree] = in.token match {
+ case AT =>
+ in.nextToken()
+ annot :: readAnnots(annot)
+ case _ if isHole && lookingAhead { in.token == AT || isModifier || isDefIntro || isIdent} =>
+ val ann = Apply(Select(New(Ident(tpnme.QUASIQUOTE_MODS)), nme.CONSTRUCTOR), List(Literal(Constant(in.name.toString))))
+ in.nextToken()
+ ann :: readAnnots(annot)
+ case _ =>
+ Nil
+ }
+ }
+ }
+
+ object TermParser extends Parser
+
+ object CaseParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " }")
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val Match(_, head :: tail) = super.unwrapTree(wrappedTree)
+ if (tail.nonEmpty)
+ c.abort(c.macroApplication.pos, "Can't parse more than one casedef, consider generating a match tree instead")
+ head
+ }
+ }
+
+ object PatternParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("something match { case " + code + " => }")
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val Match(_, List(CaseDef(pat, _, _))) = super.unwrapTree(wrappedTree)
+ pat
+ }
+ }
+
+ object TypeParser extends Parser {
+ override def wrapCode(code: String) = super.wrapCode("type T = " + code)
+
+ override def unwrapTree(wrappedTree: Tree): Tree = {
+ val TypeDef(_, _, _, rhs) = super.unwrapTree(wrappedTree)
+ rhs
+ }
+ }
+} \ No newline at end of file