Skip to content

Commit

Permalink
consolidate .children() APIs into iterators
Browse files Browse the repository at this point in the history
Consolidate both `Node` and `Cursor` utilities like `.children()`, `descendents()`, `edges()`, `CursorWithEdges` etc... and expose them to TS
  • Loading branch information
OmarTawfik committed Nov 19, 2024
1 parent d9e7b78 commit cc0e567
Show file tree
Hide file tree
Showing 32 changed files with 394 additions and 203 deletions.
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 iterators for both nodes and cursors to return `.children()`, `.ancestors()`, and `.descendents()`.
1 change: 0 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,6 @@ 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 Query = metaslang_cst::query::Query<KindTypes>;
pub use metaslang_cst::query::QueryError;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::mem;
use std::ops::ControlFlow;

use crate::cst::{Node, TerminalKindExtensions, TextIndex};
use crate::cst::{Node, TerminalKindExtensions};
use crate::parser::parser_support::context::{Marker, ParserContext};
use crate::parser::parser_support::ParserResult;
use crate::parser::ParseError;
Expand Down Expand Up @@ -135,8 +135,8 @@ 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(|child| child.descendants())
.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 @@ -138,12 +138,10 @@ where
// Sanity check: Make sure that succesful parse is equivalent to not having any invalid nodes
debug_assert_eq!(
errors.is_empty(),
parse_tree
.cursor_with_offset(TextIndex::ZERO)
.all(|node| node
.as_terminal()
.filter(|tok| !tok.kind.is_valid())
.is_none())
parse_tree.descendants().all(|edge| edge
.as_terminal()
.filter(|tok| !tok.kind.is_valid())
.is_none())
);

ParseOutput { parse_tree, errors }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::ops::ControlFlow;

use crate::cst::{
Edge, EdgeLabel, Node, NonterminalKind, TerminalKind, TerminalKindExtensions, TextIndex,
};
use crate::cst::{Edge, EdgeLabel, Node, NonterminalKind, TerminalKind, TerminalKindExtensions};

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum ParserResult {
Expand Down Expand Up @@ -129,9 +127,9 @@ 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(|node| node.descendants())
.all(|edge| {
edge.as_terminal()
.filter(|tok| !tok.kind.is_valid())
.is_none()
})
Expand Down Expand Up @@ -205,9 +203,9 @@ 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(|node| node.descendants())
.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
10 changes: 10 additions & 0 deletions crates/codegen/runtime/cargo/wasm/src/runtime/config.json.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"custom_inspect": true
}
},
"nomic-foundation:slang:cst:nonterminal-node-iterator": {
"Resource": {
"as_iterator": true
}
},
"nomic-foundation:slang:cst:nonterminal-node.id()": {
"Function": {
"as_getter": true
Expand Down Expand Up @@ -70,6 +75,11 @@
"as_getter": true
}
},
"nomic-foundation:slang:cst:edge-iterator": {
"Resource": {
"as_iterator": true
}
},
"nomic-foundation:slang:cst:cursor.node()": {
"Function": {
"as_getter": 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.
children: func() -> list<edge>;
/// Returns an iterator over the immediate children of this node.
children: func() -> edge-iterator;
/// Returns an iterator over all descendents of this node in pre-order traversal.
descendents: func() -> edge-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 @@ -99,6 +102,12 @@ interface cst {
create-cursor: func(text-offset: text-index) -> cursor;
}

/// Iterator over nonterminal nodes in the syntax tree.
resource nonterminal-node-iterator {
/// Returns the next nonterminal node in the iteration, or None if there are no more nodes.
next: func() -> option<nonterminal-node>;
}

/// Represents a terminal node in the syntax tree.
/// These are leaf nodes that represent actual tokens from the source code.
resource terminal-node {
Expand All @@ -111,8 +120,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.
children: func() -> list<edge>;
/// Returns an iterator over the immediate children of this node.
children: func() -> edge-iterator;
/// Returns an iterator over all descendents of this node in pre-order traversal.
descendents: func() -> edge-iterator;

/// Converts the node back to source code text.
unparse: func() -> string;
/// Converts the node to a JSON representation for debugging.
Expand All @@ -127,6 +139,12 @@ interface cst {
node: node,
}

/// Iterator over edges in the syntax tree.
resource edge-iterator {
/// Returns the next edge in the iteration, or None if there are no more edges.
next: func() -> option<edge>;
}

/// Provides navigation and traversal capabilities over the syntax tree.
resource cursor {
/// Resets the cursor to its initial position.
Expand Down Expand Up @@ -157,8 +175,12 @@ 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 an iterator over the immediate children of the current node.
children: func() -> edge-iterator;
/// Returns an iterator over all descendents of the current node in pre-order traversal.
descendents: func() -> edge-iterator;
/// Returns an iterator over all ancestor nodes, starting from the immediate parent and moving up to the root.
ancestors: func() -> nonterminal-node-iterator;

/// Moves to the next node in pre-order traversal.
go-to-next: func() -> bool;
Expand Down Expand Up @@ -235,12 +257,21 @@ interface cst {
/// 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.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ mod ffi {
QueryMatch, QueryMatchIterator, QueryMatchIteratorBorrow, TerminalKind, TerminalNode,
TerminalNodeBorrow, TextIndex, TextRange,
};

pub type NonterminalNodeIterator<T: Iterator<Item = NonterminalNode>> = T;
pub type EdgeIterator<T: Iterator<Item = Edge>> = T;
}

mod rust {
Expand All @@ -23,7 +26,9 @@ impl ffi::Guest for crate::wasm_crate::World {
type TerminalKindExtensions = TerminalKindExtensionsWrapper;

type NonterminalNode = NonterminalNodeWrapper;
type NonterminalNodeIterator = NonterminalNodeIteratorWrapper;
type TerminalNode = TerminalNodeWrapper;
type EdgeIterator = EdgeIteratorWrapper;

type Cursor = CursorWrapper;

Expand Down Expand Up @@ -125,6 +130,18 @@ define_rc_wrapper! { NonterminalNode {
}
} }

//================================================
//
// resource nonterminal-node-iterator
//
//================================================

define_refcell_wrapper! { NonterminalNodeIterator {
fn next(&self) -> Option<ffi::NonterminalNode> {
self._borrow_mut_ffi().next().map(IntoFFI::_into_ffi)
}
} }

//================================================
//
// resource terminal-node
Expand Down Expand Up @@ -173,6 +190,18 @@ impl IntoFFI<ffi::Edge> for rust::Edge {
}
}

//================================================
//
// resource edge-iterator
//
//================================================

define_refcell_wrapper! { EdgeIterator {
fn next(&self) -> Option<ffi::Edge> {
self._borrow_mut_ffi().next().map(IntoFFI::_into_ffi)
}
} }

//================================================
//
// resource cursor
Expand Down Expand Up @@ -220,7 +249,7 @@ define_refcell_wrapper! { Cursor {
self._borrow_ffi().depth().try_into().unwrap()
}

fn ancestors(&self) -> Vec<ffi::NonterminalNode> {
fn ancestors(&self) -> ffi::NonterminalNodeIterator<_> {
self._borrow_ffi().ancestors().map(|x|x._into_ffi()).collect()
}

Expand Down
Loading

0 comments on commit cc0e567

Please sign in to comment.