Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 282 lines (232 sloc) 10.896 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
*
* @author Paul Phillips
*/

package scala.tools.nsc
package ast

import PartialFunction._
import symtab.Flags
import scala.language.implicitConversions

/** A DSL for generating scala code. The goal is that the
* code generating code should look a lot like the code it
* generates.
*/

trait TreeDSL {
  val global: Global

  import global._
  import definitions._

  object CODE {
    // Add a null check to a Tree => Tree function
    def nullSafe[T](f: Tree => Tree, ifNull: Tree): Tree => Tree =
      tree => IF (tree MEMBER_== NULL) THEN ifNull ELSE f(tree)

    def returning[T](x: T)(f: T => Unit): T = util.returning(x)(f)

    object LIT extends (Any => Literal) {
      def typed(x: Any) = apply(x) setType ConstantType(Constant(x))
      def apply(x: Any) = Literal(Constant(x))
      def unapply(x: Any) = condOpt(x) { case Literal(Constant(value)) => value }
    }

    // Boring, predictable trees.
    def TRUE = LIT typed true
    def FALSE = LIT typed false
    def ZERO = LIT(0)
    def NULL = LIT(null)
    def UNIT = LIT(())

    object WILD {
      def empty = Ident(nme.WILDCARD)
      def apply(tpe: Type) = Ident(nme.WILDCARD) setType tpe
      def unapply(other: Any) = cond(other) { case Ident(nme.WILDCARD) => true }
    }

    def fn(lhs: Tree, op: Name, args: Tree*) = Apply(Select(lhs, op), args.toList)
    def fn(lhs: Tree, op: Symbol, args: Tree*) = Apply(Select(lhs, op), args.toList)

    class TreeMethods(target: Tree) {
      /** logical/comparison ops **/
      def OR(other: Tree) =
        if (target == EmptyTree) other
        else if (other == EmptyTree) target
        else gen.mkOr(target, other)

      def AND(other: Tree) =
        if (target == EmptyTree) other
        else if (other == EmptyTree) target
        else gen.mkAnd(target, other)

      /** Note - calling ANY_== in the matcher caused primitives to get boxed
* for the comparison, whereas looking up nme.EQ does not. See #3570 for
* an example of how target.tpe can be non-null, yet it claims not to have
* a member called nme.EQ. Not sure if that should happen, but we can be
* robust by dragging in Any regardless.
*/
      def MEMBER_== (other: Tree) = {
        val opSym = if (target.tpe == null) NoSymbol else target.tpe member nme.EQ
        if (opSym == NoSymbol) ANY_==(other)
        else fn(target, opSym, other)
      }
      def ANY_EQ (other: Tree) = OBJ_EQ(other AS ObjectClass.tpe)
      def ANY_== (other: Tree) = fn(target, Any_==, other)
      def ANY_!= (other: Tree) = fn(target, Any_!=, other)
      def OBJ_!= (other: Tree) = fn(target, Object_!=, other)
      def OBJ_EQ (other: Tree) = fn(target, Object_eq, other)
      def OBJ_NE (other: Tree) = fn(target, Object_ne, other)

      def INT_>= (other: Tree) = fn(target, getMember(IntClass, nme.GE), other)
      def INT_== (other: Tree) = fn(target, getMember(IntClass, nme.EQ), other)

      // generic operations on ByteClass, IntClass, LongClass
      def GEN_| (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.OR), other)
      def GEN_& (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.AND), other)
      def GEN_== (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.EQ), other)
      def GEN_!= (other: Tree, kind: ClassSymbol) = fn(target, getMember(kind, nme.NE), other)

      /** Apply, Select, Match **/
      def APPLY(params: Tree*) = Apply(target, params.toList)
      def APPLY(params: List[Tree]) = Apply(target, params)
      def MATCH(cases: CaseDef*) = Match(target, cases.toList)

      def DOT(member: Name) = SelectStart(Select(target, member))
      def DOT(sym: Symbol) = SelectStart(Select(target, sym))

      /** Assignment */
      // !!! This method is responsible for some tree sharing, but a diligent
      // reviewer pointed out that we shouldn't blindly duplicate these trees
      // as there might be DefTrees nested beneath them. It's not entirely
      // clear how to proceed, so for now it retains the non-duplicating behavior.
      def ===(rhs: Tree) = Assign(target, rhs)

      /** Methods for sequences **/
      def DROP(count: Int): Tree =
        if (count == 0) target
        else (target DOT nme.drop)(LIT(count))

      /** Casting & type tests -- working our way toward understanding exactly
* what differs between the different forms of IS and AS.
*
* See ticket #2168 for one illustration of AS vs. AS_ANY.
*/
      def AS(tpe: Type) = gen.mkAsInstanceOf(target, tpe, any = true, wrapInApply = false)
      def IS(tpe: Type) = gen.mkIsInstanceOf(target, tpe, true)
      def IS_OBJ(tpe: Type) = gen.mkIsInstanceOf(target, tpe, false)

      def TOSTRING() = fn(target, nme.toString_)
      def GETCLASS() = fn(target, Object_getClass)
    }

    case class SelectStart(tree: Select) {
      def apply(args: Tree*) = Apply(tree, args.toList)
    }

    class CaseStart(pat: Tree, guard: Tree) {
      def IF(g: Tree): CaseStart = new CaseStart(pat, g)
      def ==>(body: Tree): CaseDef = CaseDef(pat, guard, body)
    }

    /** VODD, if it's not obvious, means ValOrDefDef. This is the
* common code between a tree based on a pre-existing symbol and
* one being built from scratch.
*/
    trait VODDStart {
      def name: Name
      def defaultMods: Modifiers
      def defaultTpt: Tree
      def defaultPos: Position

      type ResultTreeType <: ValOrDefDef
      def mkTree(rhs: Tree): ResultTreeType
      def ===(rhs: Tree): ResultTreeType

      private var _tpt: Tree = null
      private var _pos: Position = null

      def withType(tp: Type): this.type = {
        _tpt = TypeTree(tp)
        this
      }
      def withPos(pos: Position): this.type = {
        _pos = pos
        this
      }

      final def mods = defaultMods
      final def tpt = if (_tpt == null) defaultTpt else _tpt
      final def pos = if (_pos == null) defaultPos else _pos
    }
    trait SymVODDStart extends VODDStart {
      def sym: Symbol
      def symType: Type

      def name = sym.name
      def defaultMods = Modifiers(sym.flags)
      def defaultTpt = TypeTree(symType) setPos sym.pos.focus
      def defaultPos = sym.pos

      final def ===(rhs: Tree): ResultTreeType =
        atPos(pos)(mkTree(rhs) setSymbol sym)
    }
    trait ValCreator {
      self: VODDStart =>

      type ResultTreeType = ValDef
      def mkTree(rhs: Tree): ValDef = ValDef(mods, name.toTermName, tpt, rhs)
    }
    trait DefCreator {
      self: VODDStart =>

      def tparams: List[TypeDef]
      def vparamss: List[List[ValDef]]

      type ResultTreeType = DefDef
      def mkTree(rhs: Tree): DefDef = DefDef(mods, name, tparams, vparamss, tpt, rhs)
    }

    class DefSymStart(val sym: Symbol) extends SymVODDStart with DefCreator {
      def symType = sym.tpe.finalResultType
      def tparams = sym.typeParams map TypeDef
      def vparamss = mapParamss(sym)(ValDef)
    }
    class ValSymStart(val sym: Symbol) extends SymVODDStart with ValCreator {
      def symType = sym.tpe
    }

    trait TreeVODDStart extends VODDStart {
      def defaultMods = NoMods
      def defaultTpt = TypeTree()
      def defaultPos = NoPosition

      final def ===(rhs: Tree): ResultTreeType =
        if (pos == NoPosition) mkTree(rhs)
        else atPos(pos)(mkTree(rhs))
    }

    class ValTreeStart(val name: Name) extends TreeVODDStart with ValCreator {
    }
    class DefTreeStart(val name: Name) extends TreeVODDStart with DefCreator {
      def tparams: List[TypeDef] = Nil
      def vparamss: List[List[ValDef]] = ListOfNil
    }

    class IfStart(cond: Tree, thenp: Tree) {
      def THEN(x: Tree) = new IfStart(cond, x)
      def ELSE(elsep: Tree) = If(cond, thenp, elsep)
      def ENDIF = If(cond, thenp, EmptyTree)
    }
    class TryStart(body: Tree, catches: List[CaseDef], fin: Tree) {
      def CATCH(xs: CaseDef*) = new TryStart(body, xs.toList, fin)
      def ENDTRY = Try(body, catches, fin)
    }

    def CASE(pat: Tree): CaseStart = new CaseStart(pat, EmptyTree)
    def DEFAULT: CaseStart = new CaseStart(WILD.empty, EmptyTree)

    class SymbolMethods(target: Symbol) {
      def IS_NULL() = REF(target) OBJ_EQ NULL
      def GET() = fn(REF(target), nme.get)
      def ARGS = target.paramss.head
    }

    /** Top level accessible. */
    def MATCHERROR(arg: Tree) = Throw(MatchErrorClass.tpe, arg)
    def THROW(sym: Symbol, msg: Tree): Throw = Throw(sym.tpe, msg.TOSTRING())

    def NEW(tpt: Tree, args: Tree*): Tree = New(tpt, List(args.toList))
    def DEF(sym: Symbol): DefSymStart = new DefSymStart(sym)
    def VAL(sym: Symbol): ValSymStart = new ValSymStart(sym)

    def AND(guards: Tree*) =
      if (guards.isEmpty) EmptyTree
      else guards reduceLeft gen.mkAnd

    def IF(tree: Tree) = new IfStart(tree, EmptyTree)
    def TRY(tree: Tree) = new TryStart(tree, Nil, EmptyTree)
    def BLOCK(xs: Tree*) = Block(xs.init.toList, xs.last)
    def NOT(tree: Tree) = Select(tree, Boolean_not)
    def SOME(xs: Tree*) = Apply(SomeClass.companionSymbol, makeTupleTerm(xs.toList, true))

    /** Typed trees from symbols. */
    def THIS(sym: Symbol) = gen.mkAttributedThis(sym)
    def ID(sym: Symbol) = gen.mkAttributedIdent(sym)
    def REF(sym: Symbol) = gen.mkAttributedRef(sym)
    def REF(pre: Type, sym: Symbol) = gen.mkAttributedRef(pre, sym)

    def makeTupleTerm(trees: List[Tree], flattenUnary: Boolean): Tree = trees match {
      case Nil => UNIT
      case List(tree) if flattenUnary => tree
      case _ => Apply(TupleClass(trees.length).companionModule, trees: _*)
    }

    /** Implicits - some of these should probably disappear **/
    implicit def mkTreeMethods(target: Tree): TreeMethods = new TreeMethods(target)
    implicit def mkTreeMethodsFromSymbol(target: Symbol): TreeMethods = new TreeMethods(Ident(target))
    implicit def mkSymbolMethodsFromSymbol(target: Symbol): SymbolMethods = new SymbolMethods(target)

    /** (foo DOT bar) might be simply a Select, but more likely it is to be immediately
* followed by an Apply. We don't want to add an actual apply method to arbitrary
* trees, so SelectStart is created with an apply - and if apply is not the next
* thing called, the implicit from SelectStart -> Tree will provide the tree.
*/
    implicit def mkTreeFromSelectStart(ss: SelectStart): Select = ss.tree
    implicit def mkTreeMethodsFromSelectStart(ss: SelectStart): TreeMethods = mkTreeMethods(ss.tree)
  }
}
Something went wrong with that request. Please try again.