Skip to content

Commit e3c3e88

Browse files
authored
Merge pull request #493 from viperproject/optimize-iterator
Fix performance regression of AST traversal
2 parents 54314a6 + 928cd15 commit e3c3e88

2 files changed

Lines changed: 61 additions & 19 deletions

File tree

src/main/scala/viper/silver/ast/Ast.scala

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import viper.silver.ast.utility.rewriter.Traverse.Traverse
1313
import viper.silver.ast.utility.rewriter.{Rewritable, StrategyBuilder, Traverse}
1414
import viper.silver.verifier.errors.ErrorNode
1515
import viper.silver.verifier.{AbstractVerificationError, ConsistencyError, ErrorReason}
16-
import scala.language.reflectiveCalls
1716

1817
/*
1918
@@ -49,7 +48,7 @@ Some design choices:
4948
* Note that all but Program are transitive subtypes of `Node` via `Hashable`. The reason is
5049
* that AST node hashes may depend on the entire program, not just their sub-AST.
5150
*/
52-
trait Node extends Iterable[Node] with Rewritable {
51+
trait Node extends Traversable[Node] with Rewritable {
5352

5453
/** @see [[Nodes.subnodes()]] */
5554
def subnodes = Nodes.subnodes(this)
@@ -62,23 +61,7 @@ trait Node extends Iterable[Node] with Rewritable {
6261
Visitor.reduceWithContext(this, Nodes.subnodes)(context, enter, combine)
6362
}
6463

65-
override def iterator: Iterator[Node] = {
66-
val iterator = new Iterator[Node] {
67-
var remaining = Seq.empty[Node]
68-
69-
override def hasNext: Boolean = remaining.nonEmpty
70-
71-
override def next(): Node = {
72-
val result = remaining.head
73-
remaining = remaining.tail
74-
result
75-
}
76-
}
77-
78-
Visitor.visit(this, Nodes.subnodes) { case n: Node => iterator.remaining ++= Seq(n) }
79-
80-
iterator
81-
}
64+
override def foreach[A](f: Node => A) = Visitor.visit(this, Nodes.subnodes) { case a: Node => f(a) }
8265

8366
/** @see [[Visitor.visit()]] */
8467
def visit[A](f: PartialFunction[Node, A]): Unit = {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
//
5+
// Copyright (c) 2011-2020 ETH Zurich.
6+
7+
package viper.silver.ast.utility
8+
9+
import scala.collection.mutable
10+
11+
/** A trait for traversable collections. */
12+
trait Traversable[+A] {
13+
self =>
14+
15+
/** Applies a function to all element of the collection. */
16+
def foreach[B](f: A => B): Unit
17+
18+
/** Creates a filter of this traversable collection. */
19+
def withFilter(p: A => Boolean): Traversable[A] = new WithFilter(p)
20+
21+
class WithFilter(p: A => Boolean) extends Traversable[A] {
22+
/** Applies a function to all filtered elements of the outer collection. */
23+
def foreach[U](f: A => U): Unit = {
24+
for (x <- self) {
25+
if (p(x)) f(x)
26+
}
27+
}
28+
29+
/** Further refines the filter of this collection. */
30+
override def withFilter(q: A => Boolean): WithFilter = {
31+
new WithFilter(x => p(x) && q(x))
32+
}
33+
}
34+
35+
/** Finds the first element of this collection for which the given partial
36+
* function is defined, and applies the partial function to it.
37+
*/
38+
def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = {
39+
for (x <- self) {
40+
if (pf.isDefinedAt(x)) {
41+
return Some(pf(x))
42+
}
43+
}
44+
None
45+
}
46+
47+
/** Builds a new collection by applying a partial function to all elements
48+
* of this collection on which the function is defined.
49+
*/
50+
def collect[B](pf: PartialFunction[A, B]): Iterable[B] = {
51+
val elements = mutable.Queue.empty[B]
52+
for (x <- self) {
53+
if (pf.isDefinedAt(x)) {
54+
elements.append(pf(x))
55+
}
56+
}
57+
elements
58+
}
59+
}

0 commit comments

Comments
 (0)