Skip to content

Commit

Permalink
consolidate .children() APIs into iterators
Browse files Browse the repository at this point in the history
Consolidate redundant `Node` and `Cursor` utilities like `.children()` vs `edges()`, or `Cursor` vs `CursorWithEdges`, and expose them to WASM:

- add `node.descendants()` and `cursor.descendants()` APIs to allow iterating over all descendants of the current node in pre-order traversal.
- add `cursor.ancestors()` API to allow iterating over all ancestors of the current node, starting with the immediate parent, and moving upwards, ending with the root node.
- add `cursor.consume()` API to allow iterating over all the remaining nodes in the current tree, moving in pre-order traversal, until the cursor is completed.
  • Loading branch information
OmarTawfik committed Nov 22, 2024
1 parent 249a5b1 commit 9a24218
Show file tree
Hide file tree
Showing 52 changed files with 902 additions and 321 deletions.
5 changes: 5 additions & 0 deletions .changeset/curvy-fireants-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": minor
---

add `node.descendants()` and `cursor.descendants()` APIs to allow iterating over all descendants of the current node in pre-order traversal.
5 changes: 5 additions & 0 deletions .changeset/grumpy-cups-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": minor
---

add `cursor.ancestors()` API to allow iterating over all ancestors of the current node, starting with the immediate parent, and moving upwards, ending with the root node.
5 changes: 5 additions & 0 deletions .changeset/long-tips-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomicfoundation/slang": minor
---

add `cursor.consume()` API to allow iterating over all the remaining nodes in the current tree, moving in pre-order traversal, until the cursor is completed.
3 changes: 2 additions & 1 deletion crates/codegen/runtime/cargo/crate/src/runtime/cst/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ pub type TerminalNode = metaslang_cst::nodes::TerminalNode<KindTypes>;
pub type Edge = metaslang_cst::nodes::Edge<KindTypes>;

pub type Cursor = metaslang_cst::cursor::Cursor<KindTypes>;
pub type CursorWithEdges = metaslang_cst::cursor::CursorWithEdges<KindTypes>;
pub type CursorIterator = metaslang_cst::cursor::CursorIterator<KindTypes>;
pub type AncestorsIterator = metaslang_cst::cursor::AncestorsIterator<KindTypes>;

pub type Query = metaslang_cst::query::Query<KindTypes>;
pub use metaslang_cst::query::QueryError;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ impl ParseOutput {

/// Creates a cursor that starts at the root of the parse tree.
pub fn create_tree_cursor(&self) -> Cursor {
self.parse_tree.cursor_with_offset(TextIndex::ZERO)
self.parse_tree.clone().cursor_with_offset(TextIndex::ZERO)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,13 @@ pub fn total_not_skipped_span(result: &ParserResult) -> usize {

nodes
.iter()
.flat_map(|child| child.cursor_with_offset(TextIndex::ZERO))
.filter_map(|node| match node {
.flat_map(|edge| {
edge.node
.clone()
.cursor_with_offset(TextIndex::ZERO)
.consume()
})
.filter_map(|edge| match edge.node {
Node::Terminal(terminal) if terminal.kind.is_valid() => Some(terminal.text.len()),
_ => None,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ where
debug_assert_eq!(
errors.is_empty(),
parse_tree
.clone()
.cursor_with_offset(TextIndex::ZERO)
.all(|node| node
.consume()
.all(|edge| edge
.as_terminal()
.filter(|tok| !tok.kind.is_valid())
.is_none())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,14 @@ impl Match {
pub fn is_full_recursive(&self) -> bool {
self.nodes
.iter()
.flat_map(|node| node.cursor_with_offset(TextIndex::ZERO))
.all(|node| {
node.as_terminal()
.flat_map(|edge| {
edge.node
.clone()
.cursor_with_offset(TextIndex::ZERO)
.consume()
})
.all(|edge| {
edge.as_terminal()
.filter(|tok| !tok.kind.is_valid())
.is_none()
})
Expand Down Expand Up @@ -205,9 +210,14 @@ impl IncompleteMatch {
let result = self
.nodes
.iter()
.flat_map(|node| node.cursor_with_offset(TextIndex::ZERO))
.try_fold(0u8, |mut acc, node| {
match node {
.flat_map(|edge| {
edge.node
.clone()
.cursor_with_offset(TextIndex::ZERO)
.consume()
})
.try_fold(0u8, |mut acc, edge| {
match edge.node {
Node::Terminal(tok) if tok.kind.is_valid() && !tok.kind.is_trivia() => {
acc += 1;
}
Expand Down
12 changes: 11 additions & 1 deletion crates/codegen/runtime/cargo/wasm/src/runtime/config.json.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,21 @@
"as_getter": true
}
},
"nomic-foundation:slang:cst:cursor.ancestors()": {
"nomic-foundation:slang:cst:cursor.children()": {
"Function": {
"as_getter": true
}
},
"nomic-foundation:slang:cst:cursor-iterator": {
"Resource": {
"as_iterator": true
}
},
"nomic-foundation:slang:cst:ancestors-iterator": {
"Resource": {
"as_iterator": true
}
},
"nomic-foundation:slang:cst:query-match.captures": {
"ListOfTuple": {
"as_dictionary": true
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ interface cst {
/// Returns the length of the text span this node covers.
text-length: func() -> text-index;

/// Returns the list of child edges connected to this node.
/// Returns the list of child edges directly connected to this node.
children: func() -> list<edge>;
/// Returns an iterator over all descendants of the current node in pre-order traversal.
descendants: func() -> cursor-iterator;

/// Converts the node and its children back to source code text.
unparse: func() -> string;
/// Converts the node to a JSON representation for debugging.
Expand All @@ -111,8 +114,11 @@ interface cst {
/// Returns the length of the text span this node covers.
text-length: func() -> text-index;

/// Returns the list of child edges connected to this node.
/// Returns the list of child edges directly connected to this node.
children: func() -> list<edge>;
/// Returns an iterator over all descendants of this node in pre-order traversal.
descendants: func() -> cursor-iterator;

/// Converts the node back to source code text.
unparse: func() -> string;
/// Converts the node to a JSON representation for debugging.
Expand Down Expand Up @@ -157,13 +163,19 @@ interface cst {
/// Returns the current depth in the tree (i.e. number of ancestors).
depth: func() -> u32;

/// Returns the list of ancestor nodes up to the root.
ancestors: func() -> list<nonterminal-node>;
/// Returns the list of child edges directly connected to this node.
children: func() -> list<edge>;
/// Returns an iterator over all descendants of the current node in pre-order traversal.
descendants: func() -> cursor-iterator;
/// Returns an iterator over all the remaining nodes in the current tree, moving in pre-order traversal, until the cursor is completed.
consume: func() -> cursor-iterator;
/// Returns an iterator over all ancestors of the current node, starting with the immediate parent, and moving upwards, ending with the root node.
ancestors: func() -> ancestors-iterator;

/// Moves to the next node in pre-order traversal.
go-to-next: func() -> bool;
/// Moves to the next node that isn't a descendant of the current node.
go-to-next-non-descendent: func() -> bool;
go-to-next-non-descendant: func() -> bool;
/// Moves to the previous node in pre-order traversal.
go-to-previous: func() -> bool;

Expand Down Expand Up @@ -201,6 +213,18 @@ interface cst {
query: func(queries: list<borrow<query>>) -> query-match-iterator;
}

/// Iterator over all the remaining nodes in the current tree, moving in pre-order traversal, until the cursor is completed.
resource cursor-iterator {
/// Returns the next edge in the iteration, or `undefined` if there are no more edges.
next: func() -> option<edge>;
}

/// Iterator over all ancestors of the current node, starting with the immediate parent, and moving upwards, ending with the root node.
resource ancestors-iterator {
/// Returns the next nonterminal node in the iteration, or `undefined` if there are no more nodes.
next: func() -> option<nonterminal-node>;
}

/// Represents a tree query for pattern matching in the syntax tree.
resource query {
/// Parses a query string into a query object.
Expand Down Expand Up @@ -228,19 +252,28 @@ interface cst {

/// Iterator over query matches in the syntax tree.
resource query-match-iterator {
/// Returns the next match or None if there are no more matches.
/// Returns the next match or `undefined` if there are no more matches.
next: func() -> option<query-match>;
}

/// Represents a position in the source text, with indices for different unicode encodings of the source.
record text-index {
/// Byte offset in UTF-8 encoding.
/// This is useful when working with languages like Rust that use UTF-8.
utf8: u32,
/// Character offset in UTF-16 encoding.
/// Byte offset in UTF-8 encoding.
/// This is useful when working with languages like JavaScript that use UTF-16.
utf16: u32,
/// Line number (0-based).
/// Lines are separated by:
///
/// - carriage return `\r`.
/// - newline `\n`.
/// - line separator `\u2028`.
/// - paragraph separator `\u2029`.
line: u32,
/// Column number (0-based).
/// Columns are counted in [unicode scalar values](https://www.unicode.org/glossary/#unicode_scalar_value).
column: u32,
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9a24218

Please sign in to comment.