-
Notifications
You must be signed in to change notification settings - Fork 5
Collection Like UI
Béguet Eric edited this page Jan 25, 2015
·
14 revisions
TQL provides a way to query data structures in a way similar to Scala collections via expansion methods.
This page assume complete knowledge of the [TQL combinator library](https://github.com/begeric/TQL-scalameta/wiki/Combinators).
Those function allow to chose the traversal strategy, if none is specified _topDown_ is used by default.
__Definition__
```scala
def collect[C[_]] = new {
def apply[A](f: PartialFunction[T, A]): C[A] //or List[A] if C[_] is infered to Nothing
}
```
__Example__
```scala
val r1 = tree.collect[Set]{case Lit.Int(a) => a}
r1: Set[Int] = Set(5, 3, 17, 22)
val r2 = tree.collect{case Lit.Int(a) => a}
r2: List[Int] = List(5, 3, 5, 3, 17, 22, 5)
```
__Definition__
```scala
def focus(f: PartialFunction[T, Boolean]):EvaluatorAndThen[V, A]
```
__Definition__
```scala
def transform(f: PartialFunction[T, (T, A)]): MatcherResult[(T, A)]
```
__Definition__
```scala
def combine[B](x: Matcher[B]): EvaluatorAndThen[V, B]
```
The libray is implemented through two different evaluation stategies:
- Methods which return an Evaluator, which mean that they only define a traversal strategy and so can be queried afterward.
- Methods which return a result.
The API can be used on data of type T (the data structure), Option[T] and List[T]. In the case of Option[T] and List[T] every result is then boxed in an Option or a List.
For example, if (tree : T).transform{..} returns a result of type T, then _(optTree: Option[T]).transform{..} retuns an Option[T].
note: All examples refer to the following code spinnet which we name tree:
val a = 5
val c = 3
c = 5
if (3 == 17) {
val c = {
val d = "hey"
22
}
}
else
"2"
5
Definition
def topDown: EvaluatorMeta
def topDownBreak: EvaluatorMeta
def bottomUp: EvaluatorMeta
def bottomUpBreak: EvaluatorMeta
Example
val r1 = tree.collect{
case Defn.Val(_, List(name: Term.Name), _, _) => name
}
r1:List[Term.Name] = List(a, c, c, d)
val r2 = tree.topDown.collect{
case Defn.Val(_, List(name: Term.Name), _, _) => name
} //equiv to the above
r2:List[Term.Name] = List(a, c, c, d)
val r3 = tree.topDownBreak.collect{
case Defn.Val(_, List(name: Term.Name), _, _) => name
}
r3:List[Term.Name] = List(a, c, c)
val r4 = tree.bottomUp.collect{
case Defn.Val(_, List(name: Term.Name), _, _) => name
}
r4:List[Term.Name] = List(a, c, d, c)
val r5 = tree.bottomUpBreak.collect{
case Defn.Val(_, List(name: Term.Name), _, _) => name
}
r5:List[Term.Name] = List(a, c, d)
Example
val l: EvaluatorAndThen[Term.If, Term.If] = tree.focus{case Term.If(a, b, c) => true}
Collect all Integer literals inside a Term.If.
val l = tree.focus{case Term.If(a, b, c) => true}
.topDown
.collect{case Lit.Int(a) => a}
l: List[Int] = List(3, 17, 22)
Expands to:
def guard[U <: T : ClassTag](f: PartialFunction[U, Boolean]):EvaluatorAndThen[U]
As with the combinators a transformation can go with a result.
Example
val (t, a) = tree.transform{
case Defn.Val(a, b, c, d) =>
Defn.Var(a,b,c,Some(d)) andCollect b.toString)
}
t: scala.meta.Tree = {
var a = 5
var c = 3
c = 5
if (3 == 17) {
var c = {
var d = "hey"
22
}
} else "2"
5
}
a: List[String] = List(a, c, c, d)
val t = tree.transform{
case Lit.Int(a) => Lit.Int(a * 2)
}
t: scala.meta.Tree = {
val a = 10
val c = 6
c = 10
if (6 == 34) {
val c = {
val d = "hey"
44
}
} else "2"
10
}
Expands to:
def transformWithResult[I <: T : ClassTag, O <: T, A : Monoid]
(f: PartialFunction[I, (O, A)]): MatcherResult[(T, A)]
Provides a way to combine the collection-like UI with normal combinators.
Example
val r1: EvaluatorAndThen[scala.meta.Tree, List[Int]] =
x.focus({case Term.If(_,_,_) => true})
.combine(topDown(collect{case Lit.Int(a) => a}))
r2 = r1.result //The result is lazily computed, so we need to force the result to have it.
r2: List[Int] = List(3, 17, 1, 78, 14, 85, 2)