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                ..
66            } => self.visit_function_call(name, args.as_ref(), modifiers, *span),
67            AstNode::JavaScript { code, span } => self.visit_javascript(code, *span),
68            AstNode::Escaped { content, span } => self.visit_escaped(content, *span),
69        }
70    }
71}
72
73/// Example visitor that collects all function names
74pub struct FunctionCollector {
75    pub functions: Vec<String>,
76}
77
78impl FunctionCollector {
79    pub fn new() -> Self {
80        Self {
81            functions: Vec::new(),
82        }
83    }
84}
85
86impl AstVisitor for FunctionCollector {
87    fn visit_function_call(
88        &mut self,
89        name: &str,
90        args: Option<&Vec<Argument>>,
91        modifiers: &Modifiers,
92        span: Span,
93    ) {
94        self.functions.push(name.to_string());
95
96        // Continue visiting arguments
97        let _ = (modifiers, span);
98        if let Some(args) = args {
99            for arg in args {
100                self.visit_argument(arg);
101            }
102        }
103    }
104}
105
106/// Example visitor that counts node types
107#[derive(Default)]
108pub struct NodeCounter {
109    pub text_nodes: usize,
110    pub function_nodes: usize,
111    pub javascript_nodes: usize,
112    pub escaped_nodes: usize,
113}
114
115impl AstVisitor for NodeCounter {
116    fn visit_text(&mut self, _content: &str, _span: Span) {
117        self.text_nodes += 1;
118    }
119
120    fn visit_function_call(
121        &mut self,
122        _name: &str,
123        args: Option<&Vec<Argument>>,
124        _modifiers: &Modifiers,
125        _span: Span,
126    ) {
127        self.function_nodes += 1;
128        if let Some(args) = args {
129            for arg in args {
130                self.visit_argument(arg);
131            }
132        }
133    }
134
135    fn visit_javascript(&mut self, _code: &str, _span: Span) {
136        self.javascript_nodes += 1;
137    }
138
139    fn visit_escaped(&mut self, _content: &str, _span: Span) {
140        self.escaped_nodes += 1;
141    }
142}
143
144/// Mutable visitor trait for transforming AST
145pub trait AstVisitorMut {
146    /// Visit and possibly transform a node
147    fn visit_mut(&mut self, node: &mut AstNode) {
148        match node {
149            AstNode::Program { body, span } => self.visit_program_mut(body, *span),
150            AstNode::Text { content, span } => self.visit_text_mut(content, *span),
151            AstNode::FunctionCall {
152                name,
153                args,
154                modifiers,
155                span,
156                ..
157            } => self.visit_function_call_mut(name, args, modifiers, *span),
158            AstNode::JavaScript { code, span } => self.visit_javascript_mut(code, *span),
159            AstNode::Escaped { content, span } => self.visit_escaped_mut(content, *span),
160        }
161    }
162
163    fn visit_program_mut(&mut self, body: &mut [AstNode], span: Span) {
164        let _ = span;
165        for node in body {
166            self.visit_mut(node);
167        }
168    }
169
170    fn visit_text_mut(&mut self, content: &mut String, span: Span) {
171        let _ = (content, span);
172    }
173
174    fn visit_function_call_mut(
175        &mut self,
176        name: &mut String,
177        args: &mut Option<Vec<Argument>>,
178        modifiers: &mut Modifiers,
179        span: Span,
180    ) {
181        let _ = (name, modifiers, span);
182        if let Some(args) = args {
183            for arg in args {
184                for part in &mut arg.parts {
185                    self.visit_mut(part);
186                }
187            }
188        }
189    }
190
191    fn visit_javascript_mut(&mut self, code: &mut String, span: Span) {
192        let _ = (code, span);
193    }
194
195    fn visit_escaped_mut(&mut self, content: &mut String, span: Span) {
196        let _ = (content, span);
197    }
198}