Skip to content

Commit ce7bc31

Browse files
committed
WIP: move Visitor into wdl-analysis
1 parent 14837c1 commit ce7bc31

File tree

9 files changed

+126
-1712
lines changed

9 files changed

+126
-1712
lines changed

wdl-analysis/src/lib.rs

+67
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,74 @@ mod rules;
2020
pub mod stdlib;
2121
pub mod types;
2222
mod validation;
23+
mod visitor;
2324

2425
pub use analyzer::*;
2526
pub use rules::*;
2627
pub use validation::*;
28+
pub use visitor::*;
29+
30+
use wdl_ast::SyntaxToken;
31+
use wdl_ast::SyntaxKind;
32+
use wdl_ast::SyntaxNode;
33+
use wdl_ast::Direction;
34+
use std::collections::HashSet;
35+
36+
/// The prefix of `except` comments.
37+
pub const EXCEPT_COMMENT_PREFIX: &str = "#@ except:";
38+
39+
/// An extension trait for syntax nodes.
40+
pub trait SyntaxNodeExt {
41+
/// Gets an iterator over the `@except` comments for a syntax node.
42+
fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_;
43+
44+
/// Gets the AST node's rule exceptions set.
45+
///
46+
/// The set is the comma-delimited list of rule identifiers that follows a
47+
/// `#@ except:` comment.
48+
fn rule_exceptions(&self) -> HashSet<String>;
49+
50+
/// Determines if a given rule id is excepted for the syntax node.
51+
fn is_rule_excepted(&self, id: &str) -> bool;
52+
}
53+
54+
impl SyntaxNodeExt for SyntaxNode {
55+
fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_ {
56+
self.siblings_with_tokens(Direction::Prev)
57+
.skip(1)
58+
.map_while(|s| {
59+
if s.kind() == SyntaxKind::Whitespace || s.kind() == SyntaxKind::Comment {
60+
s.into_token()
61+
} else {
62+
None
63+
}
64+
})
65+
.filter(|t| t.kind() == SyntaxKind::Comment)
66+
}
67+
68+
fn rule_exceptions(&self) -> HashSet<String> {
69+
let mut set = HashSet::default();
70+
for comment in self.except_comments() {
71+
if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX) {
72+
for id in ids.split(',') {
73+
let id = id.trim();
74+
set.insert(id.to_string());
75+
}
76+
}
77+
}
78+
79+
set
80+
}
81+
82+
fn is_rule_excepted(&self, id: &str) -> bool {
83+
for comment in self.except_comments() {
84+
if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX) {
85+
if ids.split(',').any(|i| i.trim() == id) {
86+
return true;
87+
}
88+
}
89+
}
90+
91+
false
92+
}
93+
}

wdl-ast/src/visitor.rs renamed to wdl-analysis/src/visitor.rs

+57-38
Original file line numberDiff line numberDiff line change
@@ -24,45 +24,56 @@
2424
use std::sync::Arc;
2525

2626
use rowan::WalkEvent;
27-
use wdl_grammar::SyntaxElement;
28-
29-
use crate::AstToken;
30-
use crate::Comment;
31-
use crate::Diagnostic;
32-
use crate::Document;
33-
use crate::SupportedVersion;
34-
use crate::SyntaxKind;
35-
use crate::SyntaxNode;
27+
use wdl_ast::SyntaxElement;
28+
29+
use wdl_ast::AstToken;
30+
use wdl_ast::Comment;
31+
use wdl_ast::Diagnostic;
32+
use wdl_ast::Document;
33+
use wdl_ast::SupportedVersion;
34+
use wdl_ast::SyntaxKind;
35+
use wdl_ast::SyntaxNode;
3636
use crate::SyntaxNodeExt;
37-
use crate::VersionStatement;
38-
use crate::VisitReason;
39-
use crate::Whitespace;
40-
use crate::v1::BoundDecl;
41-
use crate::v1::CallStatement;
42-
use crate::v1::CommandSection;
43-
use crate::v1::CommandText;
44-
use crate::v1::ConditionalStatement;
45-
use crate::v1::Expr;
46-
use crate::v1::ImportStatement;
47-
use crate::v1::InputSection;
48-
use crate::v1::MetadataArray;
49-
use crate::v1::MetadataObject;
50-
use crate::v1::MetadataObjectItem;
51-
use crate::v1::MetadataSection;
52-
use crate::v1::OutputSection;
53-
use crate::v1::ParameterMetadataSection;
54-
use crate::v1::Placeholder;
55-
use crate::v1::RequirementsSection;
56-
use crate::v1::RuntimeItem;
57-
use crate::v1::RuntimeSection;
58-
use crate::v1::ScatterStatement;
59-
use crate::v1::StringText;
60-
use crate::v1::StructDefinition;
61-
use crate::v1::TaskDefinition;
62-
use crate::v1::TaskHintsSection;
63-
use crate::v1::UnboundDecl;
64-
use crate::v1::WorkflowDefinition;
65-
use crate::v1::WorkflowHintsSection;
37+
use wdl_ast::VersionStatement;
38+
use wdl_ast::Whitespace;
39+
use wdl_ast::v1::BoundDecl;
40+
use wdl_ast::v1::CallStatement;
41+
use wdl_ast::v1::CommandSection;
42+
use wdl_ast::v1::CommandText;
43+
use wdl_ast::v1::ConditionalStatement;
44+
use wdl_ast::v1::Expr;
45+
use wdl_ast::v1::ImportStatement;
46+
use wdl_ast::v1::InputSection;
47+
use wdl_ast::v1::MetadataArray;
48+
use wdl_ast::v1::MetadataObject;
49+
use wdl_ast::v1::MetadataObjectItem;
50+
use wdl_ast::v1::MetadataSection;
51+
use wdl_ast::v1::OutputSection;
52+
use wdl_ast::v1::ParameterMetadataSection;
53+
use wdl_ast::v1::Placeholder;
54+
use wdl_ast::v1::RequirementsSection;
55+
use wdl_ast::v1::RuntimeItem;
56+
use wdl_ast::v1::RuntimeSection;
57+
use wdl_ast::v1::ScatterStatement;
58+
use wdl_ast::v1::StringText;
59+
use wdl_ast::v1::StructDefinition;
60+
use wdl_ast::v1::TaskDefinition;
61+
use wdl_ast::v1::TaskHintsSection;
62+
use wdl_ast::v1::UnboundDecl;
63+
use wdl_ast::v1::WorkflowDefinition;
64+
use wdl_ast::v1::WorkflowHintsSection;
65+
66+
/// Represents the reason an AST node has been visited.
67+
///
68+
/// Each node is visited exactly once, but the visitor will receive
69+
/// a call for entering the node and a call for exiting the node.
70+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
71+
pub enum VisitReason {
72+
/// The visit has entered the node.
73+
Enter,
74+
/// The visit has exited the node.
75+
Exit,
76+
}
6677

6778
/// Represents a collection of validation diagnostics.
6879
///
@@ -652,3 +663,11 @@ pub(crate) fn visit<V: Visitor>(root: &SyntaxNode, diagnostics: &mut Diagnostics
652663
}
653664
}
654665
}
666+
667+
impl Document<SyntaxNode> {
668+
/// Visits the document with a pre-order traversal using the provided
669+
/// visitor to visit each element in the document.
670+
pub fn visit<V: Visitor>(&self, diagnostics: &mut Diagnostics, visitor: &mut V) {
671+
visit(&self.0, diagnostics, visitor)
672+
}
673+
}

wdl-ast/src/lib.rs

+2-92
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
//! ```rust
1717
//! # let source = "version 1.1\nworkflow test {}";
1818
//! use wdl_ast::Document;
19-
//! use wdl_ast::Validator;
2019
//!
2120
//! let (document, diagnostics) = Document::parse(source);
2221
//! if !diagnostics.is_empty() {
@@ -31,7 +30,6 @@
3130
#![warn(clippy::missing_docs_in_private_items)]
3231
#![warn(rustdoc::broken_intra_doc_links)]
3332

34-
use std::collections::HashSet;
3533
use std::fmt;
3634

3735
pub use rowan::Direction;
@@ -59,13 +57,8 @@ pub use wdl_grammar::version;
5957
pub mod v1;
6058

6159
mod element;
62-
mod visitor;
6360

6461
pub use element::*;
65-
pub use visitor::*;
66-
67-
/// The prefix of `except` comments.
68-
pub const EXCEPT_COMMENT_PREFIX: &str = "#@ except:";
6962

7063
/// A trait that abstracts the underlying representation of a syntax tree node.
7164
///
@@ -104,9 +97,6 @@ pub trait TreeNode: Clone + fmt::Debug + PartialEq + Eq + std::hash::Hash {
10497

10598
/// Gets the ancestors of the node.
10699
fn ancestors(&self) -> impl Iterator<Item = Self>;
107-
108-
/// Determines if a given rule id is excepted for the node.
109-
fn is_rule_excepted(&self, id: &str) -> bool;
110100
}
111101

112102
/// A trait that abstracts the underlying representation of a syntax token.
@@ -361,10 +351,6 @@ impl TreeNode for SyntaxNode {
361351
fn ancestors(&self) -> impl Iterator<Item = Self> {
362352
self.ancestors()
363353
}
364-
365-
fn is_rule_excepted(&self, id: &str) -> bool {
366-
<Self as SyntaxNodeExt>::is_rule_excepted(self, id)
367-
}
368354
}
369355

370356
impl TreeToken for SyntaxToken {
@@ -389,74 +375,6 @@ impl TreeToken for SyntaxToken {
389375
}
390376
}
391377

392-
/// Represents the reason an AST node has been visited.
393-
///
394-
/// Each node is visited exactly once, but the visitor will receive
395-
/// a call for entering the node and a call for exiting the node.
396-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
397-
pub enum VisitReason {
398-
/// The visit has entered the node.
399-
Enter,
400-
/// The visit has exited the node.
401-
Exit,
402-
}
403-
404-
/// An extension trait for syntax nodes.
405-
pub trait SyntaxNodeExt {
406-
/// Gets an iterator over the `@except` comments for a syntax node.
407-
fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_;
408-
409-
/// Gets the AST node's rule exceptions set.
410-
///
411-
/// The set is the comma-delimited list of rule identifiers that follows a
412-
/// `#@ except:` comment.
413-
fn rule_exceptions(&self) -> HashSet<String>;
414-
415-
/// Determines if a given rule id is excepted for the syntax node.
416-
fn is_rule_excepted(&self, id: &str) -> bool;
417-
}
418-
419-
impl SyntaxNodeExt for SyntaxNode {
420-
fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_ {
421-
self.siblings_with_tokens(Direction::Prev)
422-
.skip(1)
423-
.map_while(|s| {
424-
if s.kind() == SyntaxKind::Whitespace || s.kind() == SyntaxKind::Comment {
425-
s.into_token()
426-
} else {
427-
None
428-
}
429-
})
430-
.filter(|t| t.kind() == SyntaxKind::Comment)
431-
}
432-
433-
fn rule_exceptions(&self) -> HashSet<String> {
434-
let mut set = HashSet::default();
435-
for comment in self.except_comments() {
436-
if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX) {
437-
for id in ids.split(',') {
438-
let id = id.trim();
439-
set.insert(id.to_string());
440-
}
441-
}
442-
}
443-
444-
set
445-
}
446-
447-
fn is_rule_excepted(&self, id: &str) -> bool {
448-
for comment in self.except_comments() {
449-
if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX) {
450-
if ids.split(',').any(|i| i.trim() == id) {
451-
return true;
452-
}
453-
}
454-
}
455-
456-
false
457-
}
458-
}
459-
460378
/// Represents the AST of a [Document].
461379
///
462380
/// See [Document::ast].
@@ -550,11 +468,11 @@ impl Document {
550468
/// Ast::Unsupported => panic!("should be a V1 AST"),
551469
/// }
552470
/// ```
553-
pub fn parse(source: &str) -> (Self, Diagnostics) {
471+
pub fn parse(source: &str) -> (Self, Vec<Diagnostic>) {
554472
let (tree, diagnostics) = SyntaxTree::parse(source);
555473
(
556474
Document::cast(tree.into_syntax()).expect("document should cast"),
557-
visitor::Diagnostics(diagnostics),
475+
diagnostics,
558476
)
559477
}
560478
}
@@ -589,14 +507,6 @@ impl<N: TreeNode> Document<N> {
589507
}
590508
}
591509

592-
impl Document<SyntaxNode> {
593-
/// Visits the document with a pre-order traversal using the provided
594-
/// visitor to visit each element in the document.
595-
pub fn visit<V: Visitor>(&self, diagnostics: &mut Diagnostics, visitor: &mut V) {
596-
visit(&self.0, diagnostics, visitor)
597-
}
598-
}
599-
600510
impl fmt::Debug for Document {
601511
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
602512
self.0.fmt(f)

wdl-ast/src/v1/decls.rs

-40
Original file line numberDiff line numberDiff line change
@@ -964,11 +964,7 @@ impl<N: TreeNode> Decl<N> {
964964
#[cfg(test)]
965965
mod test {
966966
use super::*;
967-
use crate::Diagnostics;
968967
use crate::Document;
969-
use crate::SupportedVersion;
970-
use crate::VisitReason;
971-
use crate::Visitor;
972968

973969
#[test]
974970
fn decls() {
@@ -1099,41 +1095,5 @@ task test {
10991095
.text(),
11001096
"foo"
11011097
);
1102-
1103-
// Use a visitor to count the number of declarations
1104-
#[derive(Default)]
1105-
struct MyVisitor {
1106-
bound: usize,
1107-
unbound: usize,
1108-
}
1109-
1110-
impl Visitor for MyVisitor {
1111-
fn document(
1112-
&mut self,
1113-
_: &mut Diagnostics,
1114-
_: VisitReason,
1115-
_: &Document,
1116-
_: SupportedVersion,
1117-
) {
1118-
}
1119-
1120-
fn bound_decl(&mut self, _: &mut Diagnostics, reason: VisitReason, _: &BoundDecl) {
1121-
if reason == VisitReason::Enter {
1122-
self.bound += 1;
1123-
}
1124-
}
1125-
1126-
fn unbound_decl(&mut self, _: &mut Diagnostics, reason: VisitReason, _: &UnboundDecl) {
1127-
if reason == VisitReason::Enter {
1128-
self.unbound += 1;
1129-
}
1130-
}
1131-
}
1132-
1133-
let mut visitor = MyVisitor::default();
1134-
let mut diagnostics = Diagnostics::default();
1135-
document.visit(&mut diagnostics, &mut visitor);
1136-
assert_eq!(visitor.bound, 6);
1137-
assert_eq!(visitor.unbound, 5);
11381098
}
11391099
}

0 commit comments

Comments
 (0)