-
Notifications
You must be signed in to change notification settings - Fork 14
/
TreeRelation.scala
174 lines (143 loc) · 5.3 KB
/
TreeRelation.scala
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
/*
* This file is part of Kiama.
*
* Copyright (C) 2014-2021 Anthony M Sloane, Macquarie University.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.bitbucket.inkytonik.kiama
package relation
import org.bitbucket.inkytonik.kiama.relation.Relation.emptyImage
import org.bitbucket.inkytonik.kiama.util.Memoiser
import org.bitbucket.inkytonik.kiama.util.Memoiser.makeIdMemoiser
/**
* A tree relation is a binary relation on tree nodes with an extra property
* that the `image` operation throws a `NodeNotInTreeException` exception if
* it is applied to a node that is not in this tree. `T` is the type of the
* tree nodes.
*/
class TreeRelation[T <: AnyRef with Product](
tree : Tree[T, _ <: T],
override val graph : Memoiser[T, Vector[T]] = makeIdMemoiser[T, Vector[T]](),
override val inverseGraph : Memoiser[T, Vector[T]] = makeIdMemoiser[T, Vector[T]]()
) extends Relation[T, T](graph, inverseGraph) {
rel =>
// Make sure root is present
graph.putIfAbsent(tree.root, emptyImage)
inverseGraph.putIfAbsent(tree.root, emptyImage)
/**
* Set the image of the relation for `t` to `us`. Takes care to make
* sure that all mentioned nodes are present in the graph and the
* inverse graph.
*/
def set(t : T, us : Vector[T]) : Unit = {
graph.updateAt(t, _ ++ us, us)
for (u <- us)
graph.putIfAbsent(u, emptyImage)
inverseGraph.putIfAbsent(t, emptyImage)
for (u <- us)
inverseGraph.updateAt(u, _ :+ t, Vector(t))
}
override def apply(t : T) : Vector[T] = {
val v = super.getGraph(t)
if (v == null)
throw new NodeNotInTreeException(t)
else
v
}
override lazy val inverse : TreeRelation[T] =
new TreeRelation[T](tree, inverseGraph, graph) {
override lazy val inverse : TreeRelation[T] =
rel
}
}
/**
* Support for tree relations.
*/
object TreeRelation {
import scala.annotation.tailrec
import scala.collection.immutable.Queue
/**
* Return whether this node is a leaf node or not.
*/
def isLeaf[T <: Product](t : T) : Boolean = {
def isOkLeafChild(a : Any) : Boolean =
a match {
case _ : Option[_] | _ : Either[_, _] | _ : Tuple1[_] |
_ : Tuple2[_, _] | _ : Tuple3[_, _, _] | _ : Tuple4[_, _, _, _] =>
true
case _ : Product =>
false
case _ =>
true
}
t.productIterator.forall(isOkLeafChild)
}
/**
* Return a vector of the children of `t`, skipping values that do not
* contribute directly to the tree structure. See the documentation of the
* `Tree` class for a detailed explanation of values that are skipped by
* this method.
*/
def treeChildren[T <: Product](t : T) : Vector[T] = {
@tailrec
def loop(pending : Queue[Any], children : Vector[T]) : Vector[T] =
if (pending.isEmpty)
children
else {
val candidate = pending.front
val rest = pending.tail
candidate match {
case _ : Bridge[_] =>
// ignore
loop(rest, children)
case Some(n) =>
loop(n +: rest, children)
case None =>
// ignore
loop(rest, children)
case Left(l) =>
loop(l +: rest, children)
case Right(r) =>
loop(r +: rest, children)
case Tuple1(a) =>
loop(a +: rest, children)
case (a, b) =>
loop(List(a, b) ++: rest, children)
case (a, b, c) =>
loop(List(a, b, c) ++: rest, children)
case (a, b, c, d) =>
loop(List(a, b, c, d) ++: rest, children)
case s : TraversableOnce[_] =>
loop(s ++: rest, children)
case p : Product =>
loop(rest, children :+ (p.asInstanceOf[T]))
case _ =>
// ignore
loop(rest, children)
}
}
loop(Queue(t.productIterator), emptyImage)
}
/**
* Make a child tree relation for the given tree. Populate the relation
* using `treeChildren` to traverse the structure from the tree's root.
*/
def childFromTree[T <: AnyRef with Product, R <: T](tree : Tree[T, R]) : TreeRelation[T] = {
val relation = new TreeRelation(tree)
@tailrec
def loop(pending : Queue[T]) : TreeRelation[T] =
if (pending.isEmpty)
relation
else {
val l = pending.front
val next = treeChildren(l)
if (!next.isEmpty)
relation.set(l, next)
loop(pending.tail ++ next)
}
loop(Queue(tree.root))
}
}