ironplc_dsl/
visitor.rs

1//! A set of traits and functions for visiting all nodes in a library.
2//!
3//! To use the visitor, define a struct and implement the Visitor trait
4//! for the struct.
5//!
6//! Visitor trait functions call functions that implement walking through
7//! the library. Selectively call this functions to selectively descend
8//! into the library.
9//!
10//! # Example
11//!
12//! ```
13//! use ironplc_dsl::dsl::FunctionDeclaration;
14//! use ironplc_dsl::visitor::{ Visitor, visit_function_declaration };
15//!
16//! struct Dummy {}
17//! impl Dummy {
18//!   fn do_work() {}
19//! }
20//!
21//! impl Visitor<String> for Dummy {
22//!     type Value = ();
23//!
24//!     fn visit_function_declaration(&mut self, node: &FunctionDeclaration) -> Result<Self::Value, String> {
25//!         // Do something custom before visiting the FunctionDeclaration node
26//!         Dummy::do_work();
27//!
28//!         // Continue the recursion
29//!         visit_function_declaration(self, node)
30//!     }
31//! }
32//! ```
33
34use crate::ast::*;
35use crate::dsl::*;
36use crate::sfc::Network;
37
38/// Defines a way to recurse into an object in the AST or DSL.
39pub trait Acceptor {
40    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E>;
41}
42
43/// Recurses into a vec of objects.
44impl<X> Acceptor for Vec<X>
45where
46    X: Acceptor,
47{
48    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
49        match self
50            .into_iter()
51            .map(|x| x.accept(visitor))
52            .find(|r| r.is_err())
53        {
54            Some(err) => {
55                // At least one of the items returned an error, so
56                // return the first error.
57                err
58            }
59            None => {
60                // There were no errors, so return the default value
61                Ok(V::Value::default())
62            }
63        }
64    }
65}
66
67/// Recurses into an optional object. Does nothing if the option is none.
68impl<X> Acceptor for Option<X>
69where
70    X: Acceptor,
71{
72    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
73        match self.as_ref() {
74            Some(x) => x.accept(visitor),
75            None => Ok(V::Value::default()),
76        }
77    }
78}
79
80/// Defines a visitor for the object tree. The default visitor recursively
81/// walks to visit items in the tree.
82pub trait Visitor<E> {
83    /// Value produced by this visitor when the result is not an error.
84    ///
85    /// The returned value is usually not meaningful because no guarantee
86    /// is provided when returning from vectors of objects.
87    type Value: Default;
88
89    fn walk(&mut self, node: &Library) -> Result<Self::Value, E> {
90        Acceptor::accept(&node.elems, self)
91    }
92
93    fn visit_configuration_declaration(
94        &mut self,
95        node: &ConfigurationDeclaration,
96    ) -> Result<Self::Value, E> {
97        visit_configuration_declaration(self, node)
98    }
99
100    fn visit_resource_declaration(&mut self, node: &ResourceDeclaration) -> Result<Self::Value, E> {
101        visit_resource_declaration(self, node)
102    }
103
104    fn visit_declaration(&mut self, node: &Declaration) -> Result<Self::Value, E> {
105        visit_declaration(self, node)
106    }
107
108    fn visit_enum_declaration(&mut self, node: &EnumerationDeclaration) -> Result<Self::Value, E> {
109        visit_enum_declaration(self, node)
110    }
111
112    fn visit_function_block_declaration(
113        &mut self,
114        node: &FunctionBlockDeclaration,
115    ) -> Result<Self::Value, E> {
116        visit_function_block_declaration(self, node)
117    }
118
119    fn visit_function_declaration(&mut self, node: &FunctionDeclaration) -> Result<Self::Value, E> {
120        visit_function_declaration(self, node)
121    }
122
123    fn visit_program_declaration(&mut self, node: &ProgramDeclaration) -> Result<Self::Value, E> {
124        visit_program_declaration(self, node)
125    }
126
127    fn visit_located_var_init(&mut self, node: &LocatedVarInit) -> Result<Self::Value, E> {
128        todo!()
129    }
130
131    fn visit_var_init_decl(&mut self, node: &VarInitDecl) -> Result<Self::Value, E> {
132        visit_var_init_decl(self, &node)
133    }
134
135    fn visit_sfc(&mut self, node: &Sfc) -> Result<Self::Value, E> {
136        Acceptor::accept(&node.networks, self)
137    }
138
139    fn visit_statements(&mut self, node: &Statements) -> Result<Self::Value, E> {
140        Acceptor::accept(&node.body, self)
141    }
142
143    fn visit_assignment(&mut self, node: &Assignment) -> Result<Self::Value, E> {
144        Acceptor::accept(&node.target, self)
145    }
146
147    fn visit_if(&mut self, node: &If) -> Result<Self::Value, E> {
148        visit_if(self, &node)
149    }
150
151    fn visit_compare(&mut self, op: &CompareOp, terms: &Vec<ExprKind>) -> Result<Self::Value, E> {
152        visit_compare(self, op, terms)
153    }
154
155    fn visit_binary_op(
156        &mut self,
157        op: &Vec<Operator>,
158        terms: &Vec<ExprKind>,
159    ) -> Result<Self::Value, E> {
160        // TODO this doesn't really go through binary operators - maybe this should be split
161        // into smaller pieces.
162        visit_binary_op(self, op, terms)
163    }
164
165    fn visit_unary_op(&mut self, op: &UnaryOp, term: &ExprKind) -> Result<Self::Value, E> {
166        visit_unary_op(self, op, term)
167    }
168
169    fn visit_direct_variable(&mut self, node: &DirectVariable) -> Result<Self::Value, E> {
170        todo!()
171    }
172
173    fn visit_symbolic_variable(&mut self, node: &SymbolicVariable) -> Result<Self::Value, E> {
174        // leaf node - no children
175        Ok(Self::Value::default())
176    }
177
178    fn visit_fb_call(&mut self, fb_call: &FbCall) -> Result<Self::Value, E> {
179        // TODO
180        Ok(Self::Value::default())
181    }
182
183    fn visit_enumerated_type_initializer(
184        &mut self,
185        init: &EnumeratedTypeInitializer,
186    ) -> Result<Self::Value, E> {
187        // leaf node - no children
188        Ok(Self::Value::default())
189    }
190
191    fn visit_enumerated_values_initializer(
192        &mut self,
193        init: &EnumeratedValuesInitializer,
194    ) -> Result<Self::Value, E> {
195        // leaf node - no children
196        Ok(Self::Value::default())
197    }
198
199    fn visit_function_block_type_initializer(
200        &mut self,
201        init: &FunctionBlockTypeInitializer,
202    ) -> Result<Self::Value, E> {
203        // leaf node - no children
204        Ok(Self::Value::default())
205    }
206}
207
208pub fn visit_configuration_declaration<V: Visitor<E> + ?Sized, E>(
209    v: &mut V,
210    node: &ConfigurationDeclaration,
211) -> Result<V::Value, E> {
212    Acceptor::accept(&node.global_var, v)?;
213    Acceptor::accept(&node.resource_decl, v)
214}
215
216pub fn visit_resource_declaration<V: Visitor<E> + ?Sized, E>(
217    v: &mut V,
218    node: &ResourceDeclaration,
219) -> Result<V::Value, E> {
220    Acceptor::accept(&node.global_vars, v)
221    // TODO there are more child elements here
222}
223
224pub fn visit_declaration<V: Visitor<E> + ?Sized, E>(
225    v: &mut V,
226    node: &Declaration,
227) -> Result<V::Value, E> {
228    Acceptor::accept(&node.initializer, v)
229}
230
231pub fn visit_enum_declaration<V: Visitor<E> + ?Sized, E>(
232    v: &mut V,
233    node: &EnumerationDeclaration,
234) -> Result<V::Value, E> {
235    Acceptor::accept(&node.spec, v)
236}
237
238pub fn visit_function_block_declaration<V: Visitor<E> + ?Sized, E>(
239    v: &mut V,
240    node: &FunctionBlockDeclaration,
241) -> Result<V::Value, E> {
242    Acceptor::accept(&node.inputs, v)?;
243    Acceptor::accept(&node.outputs, v)?;
244    Acceptor::accept(&node.inouts, v)?;
245    Acceptor::accept(&node.vars, v)?;
246    Acceptor::accept(&node.externals, v)?;
247    Acceptor::accept(&node.body, v)
248}
249
250pub fn visit_program_declaration<V: Visitor<E> + ?Sized, E>(
251    v: &mut V,
252    node: &ProgramDeclaration,
253) -> Result<V::Value, E> {
254    Acceptor::accept(&node.inputs, v)?;
255    Acceptor::accept(&node.outputs, v)?;
256    Acceptor::accept(&node.inouts, v)?;
257    Acceptor::accept(&node.vars, v)?;
258    Acceptor::accept(&node.body, v)
259}
260
261pub fn visit_function_declaration<V: Visitor<E> + ?Sized, E>(
262    v: &mut V,
263    node: &FunctionDeclaration,
264) -> Result<V::Value, E> {
265    Acceptor::accept(&node.inputs, v)?;
266    Acceptor::accept(&node.outputs, v)?;
267    Acceptor::accept(&node.inouts, v)?;
268    Acceptor::accept(&node.vars, v)?;
269    Acceptor::accept(&node.externals, v)?;
270    Acceptor::accept(&node.body, v)
271}
272
273pub fn visit_var_init_decl<V: Visitor<E> + ?Sized, E>(
274    v: &mut V,
275    node: &VarInitDecl,
276) -> Result<V::Value, E> {
277    Acceptor::accept(&node.initializer, v)
278}
279
280pub fn visit_if<V: Visitor<E> + ?Sized, E>(v: &mut V, node: &If) -> Result<V::Value, E> {
281    Acceptor::accept(&node.expr, v)?;
282    Acceptor::accept(&node.body, v)?;
283    Acceptor::accept(&node.else_body, v)
284}
285
286pub fn visit_compare<V: Visitor<E> + ?Sized, E>(
287    v: &mut V,
288    op: &CompareOp,
289    terms: &Vec<ExprKind>,
290) -> Result<V::Value, E> {
291    Acceptor::accept(terms, v)
292}
293
294pub fn visit_binary_op<V: Visitor<E> + ?Sized, E>(
295    v: &mut V,
296    op: &Vec<Operator>,
297    terms: &Vec<ExprKind>,
298) -> Result<V::Value, E> {
299    // TODO maybe something with the operator?
300    Acceptor::accept(terms, v)
301}
302
303pub fn visit_unary_op<V: Visitor<E> + ?Sized, E>(
304    v: &mut V,
305    op: &UnaryOp,
306    term: &ExprKind,
307) -> Result<V::Value, E> {
308    // TODO maybe something with the operator?
309    Acceptor::accept(term, v)
310}
311
312impl Acceptor for LibraryElement {
313    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
314        match self {
315            LibraryElement::ConfigurationDeclaration(config) => {
316                visitor.visit_configuration_declaration(config)
317            }
318            LibraryElement::DataTypeDeclaration(data_type_decl) => {
319                Acceptor::accept(data_type_decl, visitor)
320            }
321            LibraryElement::FunctionBlockDeclaration(func_block_decl) => {
322                visitor.visit_function_block_declaration(func_block_decl)
323            }
324            LibraryElement::FunctionDeclaration(func_decl) => {
325                visitor.visit_function_declaration(func_decl)
326            }
327            LibraryElement::ProgramDeclaration(prog_decl) => {
328                visitor.visit_program_declaration(prog_decl)
329            }
330        }
331    }
332}
333
334impl Acceptor for ResourceDeclaration {
335    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
336        visitor.visit_resource_declaration(self)
337    }
338}
339
340impl Acceptor for Declaration {
341    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
342        visitor.visit_declaration(self)
343    }
344}
345
346impl Acceptor for EnumerationDeclaration {
347    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
348        visitor.visit_enum_declaration(self)
349    }
350}
351
352impl Acceptor for EnumeratedSpecificationKind {
353    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
354        // TODO I don't know if we need to visit these items
355        Ok(V::Value::default())
356    }
357}
358
359impl Acceptor for TypeInitializer {
360    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
361        match self {
362            TypeInitializer::Simple {
363                type_name,
364                initial_value,
365            } => Ok(V::Value::default()),
366            TypeInitializer::EnumeratedValues(ev) => {
367                visitor.visit_enumerated_values_initializer(ev)
368            }
369            TypeInitializer::EnumeratedType(et) => visitor.visit_enumerated_type_initializer(et),
370            TypeInitializer::FunctionBlock(fbi) => {
371                visitor.visit_function_block_type_initializer(fbi)
372            }
373            TypeInitializer::Structure { type_name } => Ok(V::Value::default()),
374            TypeInitializer::LateResolvedType(_) => Ok(V::Value::default()),
375        }
376        // TODO don't yet know how to visit these
377    }
378}
379
380impl Acceptor for VarInitKind {
381    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
382        match self {
383            VarInitKind::VarInit(init) => visitor.visit_var_init_decl(init),
384            VarInitKind::LocatedVarInit(located_var) => visitor.visit_located_var_init(located_var),
385        }
386    }
387}
388
389impl Acceptor for ExprKind {
390    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
391        match self {
392            ExprKind::Compare { op, terms } => visitor.visit_compare(op, terms),
393            ExprKind::BinaryOp { ops, terms } => visitor.visit_binary_op(ops, terms),
394            ExprKind::UnaryOp { op, term } => visitor.visit_unary_op(op, term.as_ref()),
395            ExprKind::Const(_) => {
396                todo!()
397            }
398            ExprKind::Variable(variable) => Acceptor::accept(variable, visitor),
399            ExprKind::Function {
400                name,
401                param_assignment,
402            } => {
403                todo!()
404            }
405        }
406    }
407}
408
409impl Acceptor for VarInitDecl {
410    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
411        visitor.visit_var_init_decl(self)
412    }
413}
414
415impl Acceptor for FunctionBlockBody {
416    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
417        match self {
418            FunctionBlockBody::Sfc(network) => visitor.visit_sfc(network),
419            FunctionBlockBody::Statements(stmts) => visitor.visit_statements(stmts),
420            // TODO it isn't clear if visiting this is necessary
421            FunctionBlockBody::Empty() => Ok(V::Value::default()),
422        }
423    }
424}
425
426impl Acceptor for StmtKind {
427    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
428        match self {
429            StmtKind::Assignment(node) => visitor.visit_assignment(node),
430            StmtKind::If(node) => visitor.visit_if(node),
431            StmtKind::FbCall(node) => visitor.visit_fb_call(node),
432        }
433    }
434}
435
436impl Acceptor for Network {
437    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
438        // TODO
439        Ok(V::Value::default())
440    }
441}
442
443impl Acceptor for Variable {
444    fn accept<V: Visitor<E> + ?Sized, E>(&self, visitor: &mut V) -> Result<V::Value, E> {
445        match self {
446            Variable::DirectVariable(var) => visitor.visit_direct_variable(var),
447            Variable::SymbolicVariable(var) => visitor.visit_symbolic_variable(var),
448            Variable::MultiElementVariable(_) => {
449                todo!()
450            }
451        }
452    }
453}
454
455mod test {
456    use super::*;
457    use crate::ast::*;
458    use crate::core::{Id, SourceLoc};
459    use crate::dsl::*;
460    use std::collections::LinkedList;
461    use std::fmt::Error;
462
463    struct Descender {
464        names: LinkedList<String>,
465    }
466    impl Descender {
467        fn new() -> Descender {
468            Descender {
469                names: LinkedList::new(),
470            }
471        }
472    }
473
474    impl Visitor<Error> for Descender {
475        type Value = ();
476
477        fn visit_direct_variable(&mut self, variable: &DirectVariable) -> Result<(), Error> {
478            let mut dst = &mut self.names;
479            dst.push_back(variable.to_string());
480            Ok(())
481        }
482
483        fn visit_symbolic_variable(&mut self, var: &SymbolicVariable) -> Result<(), Error> {
484            let mut dst = &mut self.names;
485            dst.push_back(var.name.to_string());
486            Ok(())
487        }
488
489        fn visit_fb_call(&mut self, fb_call: &FbCall) -> Result<(), Error> {
490            let mut dst = &mut self.names;
491            dst.push_back(fb_call.var_name.to_string());
492            Ok(())
493        }
494    }
495
496    #[test]
497    fn walk_when_has_symbolic_variable_then_visits_variable() {
498        let library = Library {
499            elems: vec![LibraryElement::ProgramDeclaration(ProgramDeclaration {
500                type_name: Id::from("plc_prg"),
501                inputs: vec![VarInitDecl::simple_input(
502                    "Reset",
503                    "BOOL",
504                    SourceLoc::new(0),
505                )],
506                outputs: vec![],
507                inouts: vec![],
508                vars: vec![],
509                body: FunctionBlockBody::stmts(vec![StmtKind::fb_assign(
510                    "AverageVal",
511                    vec!["Cnt1", "Cnt2"],
512                    "_TMP_AverageVal17_OUT",
513                )]),
514            })],
515        };
516
517        let mut descender = Descender::new();
518
519        descender.walk(&library);
520
521        assert_eq!(1, descender.names.len())
522    }
523}