diff --git a/crates/codegen/parser/runtime/src/lib.rs b/crates/codegen/parser/runtime/src/lib.rs index eb5b7b0741..8ed2f8ed55 100644 --- a/crates/codegen/parser/runtime/src/lib.rs +++ b/crates/codegen/parser/runtime/src/lib.rs @@ -10,7 +10,6 @@ pub(crate) mod lexer; pub mod parse_error; pub mod parse_output; pub mod text_index; -pub mod visitor; #[cfg(feature = "slang_napi_interfaces")] pub mod napi; diff --git a/crates/codegen/parser/runtime/src/visitor.rs b/crates/codegen/parser/runtime/src/visitor.rs index 1562bc0e59..8b13789179 100644 --- a/crates/codegen/parser/runtime/src/visitor.rs +++ b/crates/codegen/parser/runtime/src/visitor.rs @@ -1,76 +1 @@ -use std::ops::ControlFlow; -use std::rc::Rc; -use super::{cst::*, cursor::Cursor}; - -/// A Visitor pattern for traversing the CST. -/// -/// The trait supports fallible iteration, i.e. the visitor can early return an error from the visit. -pub trait Visitor { - /// Called when the [`Visitor`] enters a [`RuleNode`]. - fn rule_enter( - &mut self, - _node: &Rc, - _cursor: &Cursor, - ) -> Result, E> { - Ok(ControlFlow::Continue(Step::In)) - } - - /// Called when the [`Visitor`] exits a [`RuleNode`]. - fn rule_exit(&mut self, _node: &Rc, _cursor: &Cursor) -> Result, E> { - Ok(ControlFlow::Continue(())) - } - - /// Called when the [`Visitor`] enters a [`TokenNode`]. - fn token(&mut self, _node: &Rc, _cursor: &Cursor) -> Result, E> { - Ok(ControlFlow::Continue(())) - } -} - -/// Whether the [`Visitor`] should should enter the children of a [`RuleNode`] or not. -pub enum Step { - In, - Over, -} - -impl Cursor { - pub fn drive_visitor>( - &mut self, - visitor: &mut V, - ) -> Result, E> { - if self.is_completed() { - return Ok(ControlFlow::Continue(())); - } - - loop { - // Node clone is cheap because it's just an enum around an Rc - match self.node() { - Node::Rule(rule_node) => { - match visitor.rule_enter(&rule_node, self)? { - ControlFlow::Break(()) => return Ok(ControlFlow::Break(())), - ControlFlow::Continue(Step::In) => { - if self.go_to_first_child() { - self.drive_visitor(visitor)?; - self.go_to_parent(); - } - } - ControlFlow::Continue(Step::Over) => {} - } - if visitor.rule_exit(&rule_node, self)? == ControlFlow::Break(()) { - return Ok(ControlFlow::Break(())); - } - } - - Node::Token(token_node) => { - if visitor.token(&token_node, self)? == ControlFlow::Break(()) { - return Ok(ControlFlow::Break(())); - } - } - } - - if !self.go_to_next_sibling() { - return Ok(ControlFlow::Continue(())); - } - } - } -} diff --git a/crates/solidity/outputs/cargo/crate/src/generated/visitor.rs b/crates/solidity/outputs/cargo/crate/src/generated/visitor.rs index 2a8585df5d..b8a2159452 100644 --- a/crates/solidity/outputs/cargo/crate/src/generated/visitor.rs +++ b/crates/solidity/outputs/cargo/crate/src/generated/visitor.rs @@ -1,78 +1 @@ // This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::ops::ControlFlow; -use std::rc::Rc; - -use super::{cst::*, cursor::Cursor}; - -/// A Visitor pattern for traversing the CST. -/// -/// The trait supports fallible iteration, i.e. the visitor can early return an error from the visit. -pub trait Visitor { - /// Called when the [`Visitor`] enters a [`RuleNode`]. - fn rule_enter( - &mut self, - _node: &Rc, - _cursor: &Cursor, - ) -> Result, E> { - Ok(ControlFlow::Continue(Step::In)) - } - - /// Called when the [`Visitor`] exits a [`RuleNode`]. - fn rule_exit(&mut self, _node: &Rc, _cursor: &Cursor) -> Result, E> { - Ok(ControlFlow::Continue(())) - } - - /// Called when the [`Visitor`] enters a [`TokenNode`]. - fn token(&mut self, _node: &Rc, _cursor: &Cursor) -> Result, E> { - Ok(ControlFlow::Continue(())) - } -} - -/// Whether the [`Visitor`] should should enter the children of a [`RuleNode`] or not. -pub enum Step { - In, - Over, -} - -impl Cursor { - pub fn drive_visitor>( - &mut self, - visitor: &mut V, - ) -> Result, E> { - if self.is_completed() { - return Ok(ControlFlow::Continue(())); - } - - loop { - // Node clone is cheap because it's just an enum around an Rc - match self.node() { - Node::Rule(rule_node) => { - match visitor.rule_enter(&rule_node, self)? { - ControlFlow::Break(()) => return Ok(ControlFlow::Break(())), - ControlFlow::Continue(Step::In) => { - if self.go_to_first_child() { - self.drive_visitor(visitor)?; - self.go_to_parent(); - } - } - ControlFlow::Continue(Step::Over) => {} - } - if visitor.rule_exit(&rule_node, self)? == ControlFlow::Break(()) { - return Ok(ControlFlow::Break(())); - } - } - - Node::Token(token_node) => { - if visitor.token(&token_node, self)? == ControlFlow::Break(()) { - return Ok(ControlFlow::Break(())); - } - } - } - - if !self.go_to_next_sibling() { - return Ok(ControlFlow::Continue(())); - } - } - } -} diff --git a/crates/solidity/outputs/cargo/tests/src/doc_examples/mod.rs b/crates/solidity/outputs/cargo/tests/src/doc_examples/mod.rs index b68ceef00e..670bdee50b 100644 --- a/crates/solidity/outputs/cargo/tests/src/doc_examples/mod.rs +++ b/crates/solidity/outputs/cargo/tests/src/doc_examples/mod.rs @@ -1,3 +1,2 @@ mod cursor_api; mod simple_contract; -mod visitor_api; diff --git a/crates/solidity/outputs/cargo/tests/src/doc_examples/visitor_api.rs b/crates/solidity/outputs/cargo/tests/src/doc_examples/visitor_api.rs deleted file mode 100644 index 1c7b5c8524..0000000000 --- a/crates/solidity/outputs/cargo/tests/src/doc_examples/visitor_api.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::ops::ControlFlow; -use std::rc::Rc; - -use anyhow::{bail, ensure, Error, Result}; -use semver::Version; - -use slang_solidity::{ - cst::{Node, RuleNode}, - cursor::Cursor, - kinds::{ProductionKind, RuleKind, TokenKind}, - language::Language, - visitor::{Step, Visitor}, -}; - -struct ContractCollector { - contract_names: Vec, -} - -impl Visitor for ContractCollector { - fn rule_enter( - &mut self, - node: &Rc, - _cursor: &Cursor, - ) -> Result> { - if node.kind == RuleKind::ContractDefinition { - if let Node::Token(token) = &node.children[2] { - ensure!(token.kind == TokenKind::Identifier); - self.contract_names.push(token.text.to_owned()); - } else { - bail!("Expected contract identifier: {node:?}"); - }; - - return Ok(ControlFlow::Continue(Step::Over)); - } - - Ok(ControlFlow::Continue(Step::In)) - } -} - -#[test] -fn visitor_api() -> Result<()> { - let language = Language::new(Version::parse("0.8.0")?)?; - let parse_output = language.parse(ProductionKind::ContractDefinition, "contract Foo {}"); - - let mut collector = ContractCollector { - contract_names: Vec::new(), - }; - - parse_output - .create_tree_cursor() - .drive_visitor(&mut collector)?; - - assert!(matches!(&collector.contract_names[..], [single] if single == "Foo")); - - return Ok(()); -} diff --git a/crates/solidity/outputs/npm/crate/src/generated/visitor.rs b/crates/solidity/outputs/npm/crate/src/generated/visitor.rs index 2a8585df5d..b8a2159452 100644 --- a/crates/solidity/outputs/npm/crate/src/generated/visitor.rs +++ b/crates/solidity/outputs/npm/crate/src/generated/visitor.rs @@ -1,78 +1 @@ // This file is generated automatically by infrastructure scripts. Please don't edit by hand. - -use std::ops::ControlFlow; -use std::rc::Rc; - -use super::{cst::*, cursor::Cursor}; - -/// A Visitor pattern for traversing the CST. -/// -/// The trait supports fallible iteration, i.e. the visitor can early return an error from the visit. -pub trait Visitor { - /// Called when the [`Visitor`] enters a [`RuleNode`]. - fn rule_enter( - &mut self, - _node: &Rc, - _cursor: &Cursor, - ) -> Result, E> { - Ok(ControlFlow::Continue(Step::In)) - } - - /// Called when the [`Visitor`] exits a [`RuleNode`]. - fn rule_exit(&mut self, _node: &Rc, _cursor: &Cursor) -> Result, E> { - Ok(ControlFlow::Continue(())) - } - - /// Called when the [`Visitor`] enters a [`TokenNode`]. - fn token(&mut self, _node: &Rc, _cursor: &Cursor) -> Result, E> { - Ok(ControlFlow::Continue(())) - } -} - -/// Whether the [`Visitor`] should should enter the children of a [`RuleNode`] or not. -pub enum Step { - In, - Over, -} - -impl Cursor { - pub fn drive_visitor>( - &mut self, - visitor: &mut V, - ) -> Result, E> { - if self.is_completed() { - return Ok(ControlFlow::Continue(())); - } - - loop { - // Node clone is cheap because it's just an enum around an Rc - match self.node() { - Node::Rule(rule_node) => { - match visitor.rule_enter(&rule_node, self)? { - ControlFlow::Break(()) => return Ok(ControlFlow::Break(())), - ControlFlow::Continue(Step::In) => { - if self.go_to_first_child() { - self.drive_visitor(visitor)?; - self.go_to_parent(); - } - } - ControlFlow::Continue(Step::Over) => {} - } - if visitor.rule_exit(&rule_node, self)? == ControlFlow::Break(()) { - return Ok(ControlFlow::Break(())); - } - } - - Node::Token(token_node) => { - if visitor.token(&token_node, self)? == ControlFlow::Break(()) { - return Ok(ControlFlow::Break(())); - } - } - } - - if !self.go_to_next_sibling() { - return Ok(ControlFlow::Continue(())); - } - } - } -} diff --git a/documentation/public/user-guide/cargo-crate/index.md b/documentation/public/user-guide/cargo-crate/index.md index a9a2138bff..04e2086fa1 100644 --- a/documentation/public/user-guide/cargo-crate/index.md +++ b/documentation/public/user-guide/cargo-crate/index.md @@ -46,24 +46,8 @@ You can then iterate over the resulting children, and assert that they match the For many code analysis tasks, it is useful to traverse the parse tree and visit each node. The `Cursor` object allows callers to traverse the parse tree in a pre-order depth-first manner. -This is an internal iterator. The `Cursor` can drive an external iterator i.e. the `Visitor` trait, which is described below. - The below example uses a cursor to collect the names of all contracts in a source file, and returns them as a `Vec`: ```{ .rust } --8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/cursor_api.rs" ``` - -## Visitor API - -The `Visitor` trait allows callers to implement a visitor that will be called for each node in the tree. -The `std::ops::ControlFlow` enum coupled with the `Step` enum allows callers to control the traversal behavior. - -For example, if the visitor is only interested in the top-level nodes, it can return `ControlFlow::Continue(Step::Over)` to skip the children of the current node. -If the visitor is interested in the children of the current node, it can return `ControlFlow::Continue(Step::In)` to visit them. - -The below example defines a visitor that collects the names of all contracts in a source file, and returns them as a `Vec`: - -```{ .rust } ---8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/visitor_api.rs" -```