Skip to main content

forge_kit/
visitor.rs

1//! Visitor pattern for traversing the ForgeScript AST
2//!
3//! This module provides a clean way to traverse and analyze the AST
4//! without modifying the core parser code.
5
6use crate::parser::{Argument, AstNode, Modifiers, Span};
7
8/// Trait for visiting AST nodes
9pub trait AstVisitor {
10    /// Visit a program node
11    fn visit_program(&mut self, body: &[AstNode], _span: Span) {
12        for node in body {
13            self.visit(node);
14        }
15    }
16
17    /// Visit a text node
18    fn visit_text(&mut self, content: &str, span: Span) {
19        let _ = (content, span);
20    }
21
22    /// Visit a function call node
23    fn visit_function_call(
24        &mut self,
25        name: &str,
26        args: Option<&Vec<Argument>>,
27        modifiers: &Modifiers,
28        span: Span,
29    ) {
30        let _ = (name, modifiers, span);
31        if let Some(args) = args {
32            for arg in args {
33                self.visit_argument(arg);
34            }
35        }
36    }
37
38    /// Visit an argument
39    fn visit_argument(&mut self, arg: &Argument) {
40        for part in &arg.parts {
41            self.visit(part);
42        }
43    }
44
45    /// Visit a JavaScript expression node
46    fn visit_javascript(&mut self, code: &str, span: Span) {
47        let _ = (code, span);
48    }
49
50    /// Visit an escaped content node
51    fn visit_escaped(&mut self, content: &str, span: Span) {
52        let _ = (content, span);
53    }
54
55    /// Dispatch to the appropriate visit method
56    fn visit(&mut self, node: &AstNode) {
57        match node {
58            AstNode::Program { body, span } => self.visit_program(body, *span),
59            AstNode::Text { content, span } => self.visit_text(content, *span),
60            AstNode::FunctionCall {
61                name,
62                args,
63                modifiers,
64                span,
65            } => self.visit_function_call(name, args.as_ref(), modifiers, *span),
66            AstNode::JavaScript { code, span } => self.visit_javascript(code, *span),
67            AstNode::Escaped { content, span } => self.visit_escaped(content, *span),
68        }
69    }
70}
71
72/// Example visitor that collects all function names
73pub struct FunctionCollector {
74    pub functions: Vec<String>,
75}
76
77impl FunctionCollector {
78    pub fn new() -> Self {
79        Self {
80            functions: Vec::new(),
81        }
82    }
83}
84
85impl AstVisitor for FunctionCollector {
86    fn visit_function_call(
87        &mut self,
88        name: &str,
89        args: Option<&Vec<Argument>>,
90        modifiers: &Modifiers,
91        span: Span,
92    ) {
93        self.functions.push(name.to_string());
94
95        // Continue visiting arguments
96        let _ = (modifiers, span);
97        if let Some(args) = args {
98            for arg in args {
99                self.visit_argument(arg);
100            }
101        }
102    }
103}
104
105/// Example visitor that counts node types
106#[derive(Default)]
107pub struct NodeCounter {
108    pub text_nodes: usize,
109    pub function_nodes: usize,
110    pub javascript_nodes: usize,
111    pub escaped_nodes: usize,
112}
113
114impl AstVisitor for NodeCounter {
115    fn visit_text(&mut self, _content: &str, _span: Span) {
116        self.text_nodes += 1;
117    }
118
119    fn visit_function_call(
120        &mut self,
121        _name: &str,
122        args: Option<&Vec<Argument>>,
123        _modifiers: &Modifiers,
124        _span: Span,
125    ) {
126        self.function_nodes += 1;
127        if let Some(args) = args {
128            for arg in args {
129                self.visit_argument(arg);
130            }
131        }
132    }
133
134    fn visit_javascript(&mut self, _code: &str, _span: Span) {
135        self.javascript_nodes += 1;
136    }
137
138    fn visit_escaped(&mut self, _content: &str, _span: Span) {
139        self.escaped_nodes += 1;
140    }
141}
142
143/// Mutable visitor trait for transforming AST
144pub trait AstVisitorMut {
145    /// Visit and possibly transform a node
146    fn visit_mut(&mut self, node: &mut AstNode) {
147        match node {
148            AstNode::Program { body, span } => self.visit_program_mut(body, *span),
149            AstNode::Text { content, span } => self.visit_text_mut(content, *span),
150            AstNode::FunctionCall {
151                name,
152                args,
153                modifiers,
154                span,
155            } => self.visit_function_call_mut(name, args, modifiers, *span),
156            AstNode::JavaScript { code, span } => self.visit_javascript_mut(code, *span),
157            AstNode::Escaped { content, span } => self.visit_escaped_mut(content, *span),
158        }
159    }
160
161    fn visit_program_mut(&mut self, body: &mut [AstNode], span: Span) {
162        let _ = span;
163        for node in body {
164            self.visit_mut(node);
165        }
166    }
167
168    fn visit_text_mut(&mut self, content: &mut String, span: Span) {
169        let _ = (content, span);
170    }
171
172    fn visit_function_call_mut(
173        &mut self,
174        name: &mut String,
175        args: &mut Option<Vec<Argument>>,
176        modifiers: &mut Modifiers,
177        span: Span,
178    ) {
179        let _ = (name, modifiers, span);
180        if let Some(args) = args {
181            for arg in args {
182                for part in &mut arg.parts {
183                    self.visit_mut(part);
184                }
185            }
186        }
187    }
188
189    fn visit_javascript_mut(&mut self, code: &mut String, span: Span) {
190        let _ = (code, span);
191    }
192
193    fn visit_escaped_mut(&mut self, content: &mut String, span: Span) {
194        let _ = (content, span);
195    }
196}