stackless traversal
This commit is contained in:
@@ -10,6 +10,7 @@ members = [ "tools", "cli" ]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
unicode-xid = "0.1.0"
|
unicode-xid = "0.1.0"
|
||||||
text_unit = "0.1.1"
|
text_unit = "0.1.1"
|
||||||
|
itertools = "0.7.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
testutils = { path = "./tests/testutils" }
|
testutils = { path = "./tests/testutils" }
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use libsyntax2::{
|
|||||||
File,
|
File,
|
||||||
utils::dump_tree,
|
utils::dump_tree,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
|
algo,
|
||||||
};
|
};
|
||||||
use neon::prelude::*;
|
use neon::prelude::*;
|
||||||
|
|
||||||
@@ -17,11 +18,12 @@ pub struct Wrapper {
|
|||||||
impl Wrapper {
|
impl Wrapper {
|
||||||
fn highlight(&self) -> Vec<(TextRange, &'static str)> {
|
fn highlight(&self) -> Vec<(TextRange, &'static str)> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
self.inner.for_each_node(|node| {
|
let syntax = self.inner.syntax();
|
||||||
|
for node in algo::walk::preorder(syntax.as_ref()) {
|
||||||
if node.kind() == ERROR {
|
if node.kind() == ERROR {
|
||||||
res.push((node.range(), "error"))
|
res.push((node.range(), "error"))
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/algo/mod.rs
Normal file
1
src/algo/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod walk;
|
||||||
46
src/algo/walk.rs
Normal file
46
src/algo/walk.rs
Normal file
@@ -0,0 +1,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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
13
src/ast.rs
13
src/ast.rs
@@ -1,5 +1,5 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use {SyntaxNode, TreeRoot, SyntaxRoot, SyntaxNodeRef};
|
use {SyntaxNode, TreeRoot, SyntaxRoot};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
|
pub struct File<R: TreeRoot = Arc<SyntaxRoot>> {
|
||||||
@@ -16,15 +16,4 @@ impl<R: TreeRoot> File<R> {
|
|||||||
pub fn syntax(&self) -> SyntaxNode<R> {
|
pub fn syntax(&self) -> SyntaxNode<R> {
|
||||||
self.syntax.clone()
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
extern crate text_unit;
|
extern crate text_unit;
|
||||||
extern crate unicode_xid;
|
extern crate unicode_xid;
|
||||||
|
extern crate itertools;
|
||||||
|
|
||||||
mod lexer;
|
mod lexer;
|
||||||
mod parser;
|
mod parser;
|
||||||
@@ -30,6 +31,7 @@ mod yellow;
|
|||||||
/// Utilities for simple uses of the parser.
|
/// Utilities for simple uses of the parser.
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
|
pub mod algo;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
lexer::{tokenize, Token},
|
lexer::{tokenize, Token},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use {SyntaxError, SyntaxNode, SyntaxNodeRef};
|
|||||||
|
|
||||||
/// Parse a file and create a string representation of the resulting parse tree.
|
/// Parse a file and create a string representation of the resulting parse tree.
|
||||||
pub fn dump_tree(syntax: &SyntaxNode) -> String {
|
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 errors: BTreeSet<_> = syntax.root.errors.iter().cloned().collect();
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
go(syntax, &mut result, 0, &mut errors);
|
go(syntax, &mut result, 0, &mut errors);
|
||||||
|
|||||||
@@ -84,4 +84,7 @@ impl RedNode {
|
|||||||
pub(crate) fn parent(&self) -> Option<ptr::NonNull<RedNode>> {
|
pub(crate) fn parent(&self) -> Option<ptr::NonNull<RedNode>> {
|
||||||
Some(self.parent.as_ref()?.parent)
|
Some(self.parent.as_ref()?.parent)
|
||||||
}
|
}
|
||||||
|
pub(crate) fn index_in_parent(&self) -> Option<usize> {
|
||||||
|
Some(self.parent.as_ref()?.index_in_parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ pub struct SyntaxNode<R: TreeRoot = Arc<SyntaxRoot>> {
|
|||||||
red: ptr::NonNull<RedNode>,
|
red: ptr::NonNull<RedNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl <R1: TreeRoot, R2: TreeRoot> PartialEq<SyntaxNode<R1>> for SyntaxNode<R2> {
|
||||||
|
fn eq(&self, other: &SyntaxNode<R1>) -> bool {
|
||||||
|
self.red == other.red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <R: TreeRoot> Eq for SyntaxNode<R> {
|
||||||
|
}
|
||||||
|
|
||||||
pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>;
|
pub type SyntaxNodeRef<'a> = SyntaxNode<&'a SyntaxRoot>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -53,7 +62,7 @@ impl SyntaxNode<Arc<SyntaxRoot>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<R: TreeRoot> SyntaxNode<R> {
|
impl<R: TreeRoot> SyntaxNode<R> {
|
||||||
pub fn borrow<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> {
|
pub fn as_ref<'a>(&'a self) -> SyntaxNode<&'a SyntaxRoot> {
|
||||||
SyntaxNode {
|
SyntaxNode {
|
||||||
root: &*self.root,
|
root: &*self.root,
|
||||||
red: ptr::NonNull::clone(&self.red),
|
red: ptr::NonNull::clone(&self.red),
|
||||||
@@ -92,6 +101,23 @@ impl<R: TreeRoot> SyntaxNode<R> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn first_child(&self) -> Option<SyntaxNode<R>> {
|
||||||
|
self.children().next()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_sibling(&self) -> Option<SyntaxNode<R>> {
|
||||||
|
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 {
|
fn red(&self) -> &RedNode {
|
||||||
unsafe { self.red.as_ref() }
|
unsafe { self.red.as_ref() }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user