From aea86d154ec5adde6adb05088a50f01380ffb8bf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 30 Jul 2018 23:45:10 +0300 Subject: stackless traversal --- src/algo/mod.rs | 1 + src/algo/walk.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/ast.rs | 13 +------------ src/lib.rs | 2 ++ src/utils.rs | 2 +- src/yellow/red.rs | 3 +++ src/yellow/syntax.rs | 28 +++++++++++++++++++++++++++- 7 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 src/algo/mod.rs create mode 100644 src/algo/walk.rs (limited to 'src') diff --git a/src/algo/mod.rs b/src/algo/mod.rs new file mode 100644 index 000000000..c1644d16d --- /dev/null +++ b/src/algo/mod.rs @@ -0,0 +1 @@ +pub mod walk; diff --git a/src/algo/walk.rs b/src/algo/walk.rs new file mode 100644 index 000000000..86dd82cc9 --- /dev/null +++ b/src/algo/walk.rs @@ -0,0 +1,46 @@ +use SyntaxNodeRef; + +pub fn preorder<'a>(root: SyntaxNodeRef<'a>) -> impl Iterator> { + 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> { + 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) + }) +} + diff --git a/src/ast.rs b/src/ast.rs index 3a9287466..b513eb13e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,5 +1,5 @@ use std::sync::Arc; -use {SyntaxNode, TreeRoot, SyntaxRoot, SyntaxNodeRef}; +use {SyntaxNode, TreeRoot, SyntaxRoot}; #[derive(Debug)] pub struct File> { @@ -16,15 +16,4 @@ impl File { pub fn syntax(&self) -> SyntaxNode { self.syntax.clone() } - - pub fn for_each_node(&self, mut f: impl FnMut(SyntaxNodeRef)) { - let syntax = self.syntax(); - let syntax = syntax.borrow(); - go(syntax, &mut f); - - fn go(syntax: SyntaxNodeRef, f: &mut FnMut(SyntaxNodeRef)) { - f(syntax); - syntax.children().into_iter().for_each(f) - } - } } diff --git a/src/lib.rs b/src/lib.rs index 3a8e6fa64..9049beb29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ extern crate text_unit; extern crate unicode_xid; +extern crate itertools; mod lexer; mod parser; @@ -30,6 +31,7 @@ mod yellow; /// Utilities for simple uses of the parser. pub mod utils; pub mod ast; +pub mod algo; pub use { lexer::{tokenize, Token}, diff --git a/src/utils.rs b/src/utils.rs index 20991659a..826a7d60b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,7 +3,7 @@ use {SyntaxError, SyntaxNode, SyntaxNodeRef}; /// Parse a file and create a string representation of the resulting parse tree. pub fn dump_tree(syntax: &SyntaxNode) -> String { - let syntax = syntax.borrow(); + let syntax = syntax.as_ref(); let mut errors: BTreeSet<_> = syntax.root.errors.iter().cloned().collect(); let mut result = String::new(); go(syntax, &mut result, 0, &mut errors); diff --git a/src/yellow/red.rs b/src/yellow/red.rs index bffb72510..8329ec5b2 100644 --- a/src/yellow/red.rs +++ b/src/yellow/red.rs @@ -84,4 +84,7 @@ impl RedNode { pub(crate) fn parent(&self) -> Option> { Some(self.parent.as_ref()?.parent) } + pub(crate) fn index_in_parent(&self) -> Option { + Some(self.parent.as_ref()?.index_in_parent) + } } diff --git a/src/yellow/syntax.rs b/src/yellow/syntax.rs index 5c31a3f35..65ce647c7 100644 --- a/src/yellow/syntax.rs +++ b/src/yellow/syntax.rs @@ -18,6 +18,15 @@ pub struct SyntaxNode> { red: ptr::NonNull, } +impl PartialEq> for SyntaxNode { + fn eq(&self, other: &SyntaxNode) -> bool { + self.red == other.red + } +} + +impl Eq for SyntaxNode { +} + pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>; #[derive(Debug)] @@ -53,7 +62,7 @@ impl SyntaxNode> { } impl SyntaxNode { - pub fn borrow<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> { + pub fn as_ref<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> { SyntaxNode { root: &*self.root, red: ptr::NonNull::clone(&self.red), @@ -92,6 +101,23 @@ impl SyntaxNode { }) } + pub fn first_child(&self) -> Option> { + self.children().next() + } + + pub fn next_sibling(&self) -> Option> { + let red = self.red(); + let parent = self.parent()?; + let next_sibling_idx = red.index_in_parent()? + 1; + if next_sibling_idx == red.n_children() { + return None; + } + Some(SyntaxNode { + root: self.root.clone(), + red: parent.red().nth_child(next_sibling_idx), + }) + } + fn red(&self) -> &RedNode { unsafe { self.red.as_ref() } } -- cgit v1.2.3