aboutsummaryrefslogtreecommitdiff
path: root/src/algo/walk.rs
blob: 86dd82cc9fcb70c11595bf8901c7e02aeb188ee8 (plain)
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
use SyntaxNodeRef;

pub fn preorder<'a>(root: SyntaxNodeRef<'a>) -> impl Iterator<Item=SyntaxNodeRef<'a>> {
    walk(root).filter_map(|event| match event {
        WalkEvent::Enter(node) => Some(node),
        WalkEvent::Exit(_) => None,
    })
}

#[derive(Debug, Copy, Clone)]
enum WalkEvent<'a> {
    Enter(SyntaxNodeRef<'a>),
    Exit(SyntaxNodeRef<'a>),
}

fn walk<'a>(root: SyntaxNodeRef<'a>) -> impl Iterator<Item=WalkEvent<'a>> {
    let mut done = false;
    ::itertools::unfold(WalkEvent::Enter(root), move |pos| {
        if done {
            return None;
        }
        let res = *pos;
        *pos = match *pos {
            WalkEvent::Enter(node) => match node.first_child() {
                Some(child) => WalkEvent::Enter(child),
                None => WalkEvent::Exit(node),
            },
            WalkEvent::Exit(node) => {
                if node == root {
                    done = true;
                    WalkEvent::Exit(node)
                } else {
                    match node.next_sibling() {
                        Some(sibling) => WalkEvent::Enter(sibling),
                        None => match node.parent() {
                            Some(node) => WalkEvent::Exit(node),
                            None => WalkEvent::Exit(node),
                        }
                    }
                }
            }
        };
        Some(res)
    })
}