1use rowan::WalkEvent;
25
26use crate::AstToken as _;
27use crate::Comment;
28use crate::Document;
29use crate::SupportedVersion;
30use crate::SyntaxKind;
31use crate::SyntaxNode;
32use crate::VersionStatement;
33use crate::VisitReason;
34use crate::Whitespace;
35use crate::v1::BoundDecl;
36use crate::v1::CallStatement;
37use crate::v1::CommandSection;
38use crate::v1::CommandText;
39use crate::v1::ConditionalStatement;
40use crate::v1::Expr;
41use crate::v1::ImportStatement;
42use crate::v1::InputSection;
43use crate::v1::MetadataArray;
44use crate::v1::MetadataObject;
45use crate::v1::MetadataObjectItem;
46use crate::v1::MetadataSection;
47use crate::v1::OutputSection;
48use crate::v1::ParameterMetadataSection;
49use crate::v1::Placeholder;
50use crate::v1::RequirementsSection;
51use crate::v1::RuntimeItem;
52use crate::v1::RuntimeSection;
53use crate::v1::ScatterStatement;
54use crate::v1::StringText;
55use crate::v1::StructDefinition;
56use crate::v1::TaskDefinition;
57use crate::v1::TaskHintsSection;
58use crate::v1::UnboundDecl;
59use crate::v1::WorkflowDefinition;
60use crate::v1::WorkflowHintsSection;
61
62#[allow(unused_variables)]
68pub trait Visitor {
69 type State;
71
72 fn document(
78 &mut self,
79 state: &mut Self::State,
80 reason: VisitReason,
81 doc: &Document,
82 version: SupportedVersion,
83 );
84
85 fn whitespace(&mut self, state: &mut Self::State, whitespace: &Whitespace) {}
87
88 fn comment(&mut self, state: &mut Self::State, comment: &Comment) {}
90
91 fn version_statement(
93 &mut self,
94 state: &mut Self::State,
95 reason: VisitReason,
96 stmt: &VersionStatement,
97 ) {
98 }
99
100 fn import_statement(
102 &mut self,
103 state: &mut Self::State,
104 reason: VisitReason,
105 stmt: &ImportStatement,
106 ) {
107 }
108
109 fn struct_definition(
111 &mut self,
112 state: &mut Self::State,
113 reason: VisitReason,
114 def: &StructDefinition,
115 ) {
116 }
117
118 fn task_definition(
120 &mut self,
121 state: &mut Self::State,
122 reason: VisitReason,
123 task: &TaskDefinition,
124 ) {
125 }
126
127 fn workflow_definition(
129 &mut self,
130 state: &mut Self::State,
131 reason: VisitReason,
132 workflow: &WorkflowDefinition,
133 ) {
134 }
135
136 fn input_section(
138 &mut self,
139 state: &mut Self::State,
140 reason: VisitReason,
141 section: &InputSection,
142 ) {
143 }
144
145 fn output_section(
147 &mut self,
148 state: &mut Self::State,
149 reason: VisitReason,
150 section: &OutputSection,
151 ) {
152 }
153
154 fn command_section(
156 &mut self,
157 state: &mut Self::State,
158 reason: VisitReason,
159 section: &CommandSection,
160 ) {
161 }
162
163 fn command_text(&mut self, state: &mut Self::State, text: &CommandText) {}
165
166 fn requirements_section(
168 &mut self,
169 state: &mut Self::State,
170 reason: VisitReason,
171 section: &RequirementsSection,
172 ) {
173 }
174
175 fn task_hints_section(
177 &mut self,
178 state: &mut Self::State,
179 reason: VisitReason,
180 section: &TaskHintsSection,
181 ) {
182 }
183
184 fn workflow_hints_section(
186 &mut self,
187 state: &mut Self::State,
188 reason: VisitReason,
189 section: &WorkflowHintsSection,
190 ) {
191 }
192
193 fn runtime_section(
195 &mut self,
196 state: &mut Self::State,
197 reason: VisitReason,
198 section: &RuntimeSection,
199 ) {
200 }
201
202 fn runtime_item(&mut self, state: &mut Self::State, reason: VisitReason, item: &RuntimeItem) {}
204
205 fn metadata_section(
207 &mut self,
208 state: &mut Self::State,
209 reason: VisitReason,
210 section: &MetadataSection,
211 ) {
212 }
213
214 fn parameter_metadata_section(
216 &mut self,
217 state: &mut Self::State,
218 reason: VisitReason,
219 section: &ParameterMetadataSection,
220 ) {
221 }
222
223 fn metadata_object(
225 &mut self,
226 state: &mut Self::State,
227 reason: VisitReason,
228 object: &MetadataObject,
229 ) {
230 }
231
232 fn metadata_object_item(
234 &mut self,
235 state: &mut Self::State,
236 reason: VisitReason,
237 item: &MetadataObjectItem,
238 ) {
239 }
240
241 fn metadata_array(
244 &mut self,
245 state: &mut Self::State,
246 reason: VisitReason,
247 item: &MetadataArray,
248 ) {
249 }
250
251 fn unbound_decl(&mut self, state: &mut Self::State, reason: VisitReason, decl: &UnboundDecl) {}
253
254 fn bound_decl(&mut self, state: &mut Self::State, reason: VisitReason, decl: &BoundDecl) {}
256
257 fn expr(&mut self, state: &mut Self::State, reason: VisitReason, expr: &Expr) {}
259
260 fn string_text(&mut self, state: &mut Self::State, text: &StringText) {}
262
263 fn placeholder(
265 &mut self,
266 state: &mut Self::State,
267 reason: VisitReason,
268 placeholder: &Placeholder,
269 ) {
270 }
271
272 fn conditional_statement(
274 &mut self,
275 state: &mut Self::State,
276 reason: VisitReason,
277 stmt: &ConditionalStatement,
278 ) {
279 }
280
281 fn scatter_statement(
283 &mut self,
284 state: &mut Self::State,
285 reason: VisitReason,
286 stmt: &ScatterStatement,
287 ) {
288 }
289
290 fn call_statement(
292 &mut self,
293 state: &mut Self::State,
294 reason: VisitReason,
295 stmt: &CallStatement,
296 ) {
297 }
298}
299
300pub(crate) fn visit<V: Visitor>(root: &SyntaxNode, state: &mut V::State, visitor: &mut V) {
303 for event in root.preorder_with_tokens() {
304 let (reason, element) = match event {
305 WalkEvent::Enter(node) => (VisitReason::Enter, node),
306 WalkEvent::Leave(node) => (VisitReason::Exit, node),
307 };
308
309 match element.kind() {
310 SyntaxKind::RootNode => {
311 let document = Document(element.into_node().unwrap());
312
313 let version = document
314 .version_statement()
315 .and_then(|s| s.version().as_str().parse::<SupportedVersion>().ok())
316 .expect("only WDL documents with supported versions can be visited");
317
318 visitor.document(state, reason, &document, version)
319 }
320 SyntaxKind::VersionStatementNode => visitor.version_statement(
321 state,
322 reason,
323 &VersionStatement(element.into_node().unwrap()),
324 ),
325 SyntaxKind::ImportStatementNode => visitor.import_statement(
326 state,
327 reason,
328 &ImportStatement(element.into_node().unwrap()),
329 ),
330 SyntaxKind::ImportAliasNode => {
331 }
333 SyntaxKind::StructDefinitionNode => visitor.struct_definition(
334 state,
335 reason,
336 &StructDefinition(element.into_node().unwrap()),
337 ),
338 SyntaxKind::TaskDefinitionNode => visitor.task_definition(
339 state,
340 reason,
341 &TaskDefinition(element.into_node().unwrap()),
342 ),
343 SyntaxKind::WorkflowDefinitionNode => visitor.workflow_definition(
344 state,
345 reason,
346 &WorkflowDefinition(element.into_node().unwrap()),
347 ),
348 SyntaxKind::UnboundDeclNode => {
349 visitor.unbound_decl(state, reason, &UnboundDecl(element.into_node().unwrap()))
350 }
351 SyntaxKind::BoundDeclNode => {
352 visitor.bound_decl(state, reason, &BoundDecl(element.into_node().unwrap()))
353 }
354 SyntaxKind::PrimitiveTypeNode
355 | SyntaxKind::MapTypeNode
356 | SyntaxKind::ArrayTypeNode
357 | SyntaxKind::PairTypeNode
358 | SyntaxKind::ObjectTypeNode
359 | SyntaxKind::TypeRefNode => {
360 }
362 SyntaxKind::InputSectionNode => {
363 visitor.input_section(state, reason, &InputSection(element.into_node().unwrap()))
364 }
365 SyntaxKind::OutputSectionNode => {
366 visitor.output_section(state, reason, &OutputSection(element.into_node().unwrap()))
367 }
368 SyntaxKind::CommandSectionNode => visitor.command_section(
369 state,
370 reason,
371 &CommandSection(element.into_node().unwrap()),
372 ),
373 SyntaxKind::RequirementsSectionNode => visitor.requirements_section(
374 state,
375 reason,
376 &RequirementsSection(element.into_node().unwrap()),
377 ),
378 SyntaxKind::TaskHintsSectionNode => visitor.task_hints_section(
379 state,
380 reason,
381 &TaskHintsSection(element.into_node().unwrap()),
382 ),
383 SyntaxKind::WorkflowHintsSectionNode => visitor.workflow_hints_section(
384 state,
385 reason,
386 &WorkflowHintsSection(element.into_node().unwrap()),
387 ),
388 SyntaxKind::TaskHintsItemNode | SyntaxKind::WorkflowHintsItemNode => {
389 }
391 SyntaxKind::RequirementsItemNode => {
392 }
394 SyntaxKind::RuntimeSectionNode => visitor.runtime_section(
395 state,
396 reason,
397 &RuntimeSection(element.into_node().unwrap()),
398 ),
399 SyntaxKind::RuntimeItemNode => {
400 visitor.runtime_item(state, reason, &RuntimeItem(element.into_node().unwrap()))
401 }
402 SyntaxKind::MetadataSectionNode => visitor.metadata_section(
403 state,
404 reason,
405 &MetadataSection(element.into_node().unwrap()),
406 ),
407 SyntaxKind::ParameterMetadataSectionNode => visitor.parameter_metadata_section(
408 state,
409 reason,
410 &ParameterMetadataSection(element.into_node().unwrap()),
411 ),
412 SyntaxKind::MetadataObjectNode => visitor.metadata_object(
413 state,
414 reason,
415 &MetadataObject(element.into_node().unwrap()),
416 ),
417 SyntaxKind::MetadataObjectItemNode => visitor.metadata_object_item(
418 state,
419 reason,
420 &MetadataObjectItem(element.into_node().unwrap()),
421 ),
422 SyntaxKind::MetadataArrayNode => {
423 visitor.metadata_array(state, reason, &MetadataArray(element.into_node().unwrap()))
424 }
425 SyntaxKind::LiteralNullNode => {
426 }
428 k if Expr::can_cast(k) => {
429 visitor.expr(
430 state,
431 reason,
432 &Expr::cast(element.into_node().expect(
433 "any element that is able to be turned into an expr should be a node",
434 ))
435 .expect("expr should be built"),
436 )
437 }
438 SyntaxKind::LiteralMapItemNode
439 | SyntaxKind::LiteralObjectItemNode
440 | SyntaxKind::LiteralStructItemNode
441 | SyntaxKind::LiteralHintsItemNode
442 | SyntaxKind::LiteralInputItemNode
443 | SyntaxKind::LiteralOutputItemNode => {
444 }
446 k @ (SyntaxKind::LiteralIntegerNode
447 | SyntaxKind::LiteralFloatNode
448 | SyntaxKind::LiteralBooleanNode
449 | SyntaxKind::LiteralNoneNode
450 | SyntaxKind::LiteralStringNode
451 | SyntaxKind::LiteralPairNode
452 | SyntaxKind::LiteralArrayNode
453 | SyntaxKind::LiteralMapNode
454 | SyntaxKind::LiteralObjectNode
455 | SyntaxKind::LiteralStructNode
456 | SyntaxKind::LiteralHintsNode
457 | SyntaxKind::LiteralInputNode
458 | SyntaxKind::LiteralOutputNode
459 | SyntaxKind::ParenthesizedExprNode
460 | SyntaxKind::NameRefNode
461 | SyntaxKind::IfExprNode
462 | SyntaxKind::LogicalNotExprNode
463 | SyntaxKind::NegationExprNode
464 | SyntaxKind::LogicalOrExprNode
465 | SyntaxKind::LogicalAndExprNode
466 | SyntaxKind::EqualityExprNode
467 | SyntaxKind::InequalityExprNode
468 | SyntaxKind::LessExprNode
469 | SyntaxKind::LessEqualExprNode
470 | SyntaxKind::GreaterExprNode
471 | SyntaxKind::GreaterEqualExprNode
472 | SyntaxKind::AdditionExprNode
473 | SyntaxKind::SubtractionExprNode
474 | SyntaxKind::MultiplicationExprNode
475 | SyntaxKind::DivisionExprNode
476 | SyntaxKind::ModuloExprNode
477 | SyntaxKind::CallExprNode
478 | SyntaxKind::IndexExprNode
479 | SyntaxKind::AccessExprNode) => {
480 unreachable!("`{k:?}` should be handled by `Expr::can_cast`")
481 }
482 SyntaxKind::PlaceholderNode => {
483 visitor.placeholder(state, reason, &Placeholder(element.into_node().unwrap()))
484 }
485 SyntaxKind::PlaceholderSepOptionNode
486 | SyntaxKind::PlaceholderDefaultOptionNode
487 | SyntaxKind::PlaceholderTrueFalseOptionNode => {
488 }
490 SyntaxKind::ConditionalStatementNode => visitor.conditional_statement(
491 state,
492 reason,
493 &ConditionalStatement(element.into_node().unwrap()),
494 ),
495 SyntaxKind::ScatterStatementNode => visitor.scatter_statement(
496 state,
497 reason,
498 &ScatterStatement(element.into_node().unwrap()),
499 ),
500 SyntaxKind::CallStatementNode => {
501 visitor.call_statement(state, reason, &CallStatement(element.into_node().unwrap()))
502 }
503 SyntaxKind::CallTargetNode
504 | SyntaxKind::CallAliasNode
505 | SyntaxKind::CallAfterNode
506 | SyntaxKind::CallInputItemNode => {
507 }
509 SyntaxKind::Abandoned | SyntaxKind::MAX => {
510 unreachable!("node should not exist in the tree")
511 }
512 SyntaxKind::Whitespace if reason == VisitReason::Enter => {
513 visitor.whitespace(state, &Whitespace(element.into_token().unwrap()))
514 }
515 SyntaxKind::Comment if reason == VisitReason::Enter => {
516 visitor.comment(state, &Comment(element.into_token().unwrap()))
517 }
518 SyntaxKind::LiteralStringText if reason == VisitReason::Enter => {
519 visitor.string_text(state, &StringText(element.into_token().unwrap()))
520 }
521 SyntaxKind::LiteralCommandText if reason == VisitReason::Enter => {
522 visitor.command_text(state, &CommandText(element.into_token().unwrap()))
523 }
524 _ => {
525 }
527 }
528 }
529}