Classes

Class definitions

Class definitions are written using CLASSDEF(...):

import treehugger.forest._, definitions._, treehuggerDSL._

val tree = (CLASSDEF("C"): Tree)
// tree: Tree = ClassDef(
//   Modifiers(0L, TypeName(""), List()),
//   Modifiers(0L, TypeName(""), List()),
//   TypeName("C"),
//   List(),
//   List(),
//   Template(
//     List(),
//     ValDef(Modifiers(4L, TypeName(""), List()), Ident(TermName("_")), EmptyTree),
//     List()
//   )
// )

treeToString(tree)
// res0: String = "class C"

val tree2 = CLASSDEF("C") := BLOCK(
  VAL("x") := LIT(0)
)
// tree2: ClassDef = ClassDef(
//   Modifiers(0L, TypeName(""), List()),
//   Modifiers(0L, TypeName(""), List()),
//   TypeName("C"),
//   List(),
//   List(),
//   Template(
//     List(),
//     ValDef(Modifiers(4L, TypeName(""), List()), Ident(TermName("_")), EmptyTree),
//     List(
//       ValDef(
//         Modifiers(0L, TypeName(""), List()),
//         Ident(TermName("x")),
//         Literal(Constant(0))
//       )
//     )
//   )
// )

treeToString(tree2)
// res1: String = """class C {
//   val x = 0
// }"""

The general form of the first example is:

CLASSDEF(sym|"C").empty

As with value and function names, CLASSDEF can accept either a symbol or a String.

Constructor parameters

Primary constructor parameters are written using withParams(...) similar to function definitions. Except, it could use VAL(...) and VAR(...) in addition to PARAM(...):

val tree3 = (CLASSDEF("C")
  withParams(PARAM("x", IntClass),
    VAL("y", StringClass),
    VAR("z", TYPE_LIST(StringClass))) := BLOCK(
  DEF("hi") := LIT("hi") 
))
// tree3: ClassDef = ClassDef(
//   Modifiers(0L, TypeName(""), List()),
//   Modifiers(0L, TypeName(""), List()),
//   TypeName("C"),
//   List(),
//   List(
//     ValDef(
//       Modifiers(8192L, TypeName(""), List()),
//       Typed(Ident(TermName("x")), TypeTree()),
//       EmptyTree
//     ),
//     ValDef(
//       Modifiers(0L, TypeName(""), List()),
//       Typed(Ident(TermName("y")), TypeTree()),
//       EmptyTree
//     ),
//     ValDef(
//       Modifiers(4096L, TypeName(""), List()),
//       Typed(Ident(TermName("z")), TypeTree()),
//       EmptyTree
//     )
//   ),
//   Template(
//     List(),
//     ValDef(Modifiers(4L, TypeName(""), List()), Ident(TermName("_")), EmptyTree),
//     List(
//       DefDef(
//         Modifiers(0L, TypeName(""), List()),
//         TermName("hi"),
//         List(),
//         List(),
//         TypeTree(),
//         Literal(Constant("hi"))
//       )
//     )
//   )
// )

treeToString(tree3)
// res2: String = """class C(x: Int, val y: String, var z: List[String]) {
//   def hi = "hi"
// }"""

Constructor definitions

Auxiliary constructors are defined using DEFTHIS := BLOCK(stat, ...). Optionally, DEFTHIS may take withParams(...):

val tree4 = (CLASSDEF("C")
  withParams(PARAM("s", StringClass)) := BLOCK(
  DEFTHIS withParams(PARAM("x", IntClass)) := BLOCK(
    THIS APPLY(REF("x") TOSTRING)
  )
))
// tree4: ClassDef = ClassDef(
//   Modifiers(0L, TypeName(""), List()),
//   Modifiers(0L, TypeName(""), List()),
//   TypeName("C"),
//   List(),
//   List(
//     ValDef(
//       Modifiers(8192L, TypeName(""), List()),
//       Typed(Ident(TermName("s")), TypeTree()),
//       EmptyTree
//     )
//   ),
//   Template(
//     List(),
//     ValDef(Modifiers(4L, TypeName(""), List()), Ident(TermName("_")), EmptyTree),
//     List(
//       DefDef(
//         Modifiers(0L, TypeName(""), List()),
//         TermName("_$this"),
//         List(),
//         List(
//           List(
//             ValDef(
//               Modifiers(8192L, TypeName(""), List()),
//               Typed(Ident(TermName("x")), TypeTree()),
//               EmptyTree
//             )
//           )
//         ),
//         TypeTree(),
//         Block(
//           List(),
//           Apply(
//             This(TypeName("")),
//             List(Select(Ident(TermName("x")), TermName("toString")))
//           )
//         )
//       )
//     )
//   )
// )

treeToString(tree4)
// res3: String = """class C(s: String) {
//   def this(x: Int) = {
//     this(x.toString)
//   }
// }"""

Extending classes

To define classes by extending super classes use withParents(tree|typ|"T"):

scala> val tree5 = CLASSDEF("C") withParents("B") := BLOCK(
  DEF("x") := LIT(0)
)
scala> treeToString(tree5)

Self type annotations

To define self type annotations use withSelf(sym|"self", [typ1, ...]):

val tree6 = CLASSDEF("C") withSelf("self", "T1", "T2") := BLOCK(
  VAL("x") := REF("self")
)
// tree6: ClassDef = ClassDef(
//   Modifiers(0L, TypeName(""), List()),
//   Modifiers(0L, TypeName(""), List()),
//   TypeName("C"),
//   List(),
//   List(),
//   Template(
//     List(),
//     ValDef(
//       Modifiers(0L, TypeName(""), List()),
//       Typed(Ident(TermName("self")), TypeTree()),
//       EmptyTree
//     ),
//     List(
//       ValDef(
//         Modifiers(0L, TypeName(""), List()),
//         Ident(TermName("x")),
//         Ident(TermName("self"))
//       )
//     )
//   )
// )

treeToString(tree6)
// res4: String = """class C { self: T1 with T2 => 
//   val x = self
// }"""

Early denifitions

To define field values before supertype constructor is called add early definitions using withEarlyDefs(tree, ...):

val tree7 = CLASSDEF("C") withEarlyDefs(
  VAL("name") := LIT("Bob")
) withParents("B") := BLOCK(
  LIT(0)
)
// tree7: ClassDef = ClassDef(
//   Modifiers(0L, TypeName(""), List()),
//   Modifiers(0L, TypeName(""), List()),
//   TypeName("C"),
//   List(),
//   List(),
//   Template(
//     List(
//       Block(
//         List(),
//         ValDef(
//           Modifiers(0L, TypeName(""), List()),
//           Ident(TermName("name")),
//           Literal(Constant("Bob"))
//         )
//       ),
//       TypeTree()
//     ),
//     ValDef(Modifiers(4L, TypeName(""), List()), Ident(TermName("_")), EmptyTree),
//     List(Literal(Constant(0)))
//   )
// )

treeToString(tree7)
// res5: String = """class C extends {
//   val name = "Bob"
// } with B {
//   0
// }"""