Skip to content

Commit 74ab77b

Browse files
committed
refactor: Adapt for_next_counter_match_linter and print_linter to the Visitor pattern
1 parent 29aacea commit 74ab77b

5 files changed

Lines changed: 258 additions & 51 deletions

File tree

rusty_linter/src/core/visitor.rs

Lines changed: 199 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use rusty_common::{Position, Positioned};
22
use rusty_parser::*;
33

4+
use crate::delegate_visitor;
45
use crate::LintErrorPos;
56

67
/// The result of a visitor.
@@ -19,6 +20,20 @@ pub trait SetPosition {
1920
fn set_position(&mut self, pos: Position);
2021
}
2122

23+
// Blanket implementation for Option
24+
25+
impl<P, T> Visitor<Option<T>> for P
26+
where
27+
P: Visitor<T>,
28+
{
29+
fn visit(&mut self, element: &Option<T>) -> VisitResult {
30+
match element {
31+
Some(t) => self.visit(t),
32+
_ => Ok(()),
33+
}
34+
}
35+
}
36+
2237
// Blanket implementation for Vec
2338

2439
impl<P, T> Visitor<Vec<T>> for P
@@ -51,34 +66,14 @@ pub trait DelegateVisitor<T> {
5166
fn delegate(&mut self) -> impl Visitor<T>;
5267
}
5368

54-
/// A visitor that can visit global statements
55-
/// but does not enter the implementations of functions or subs.
56-
/// Actual visiting logic is handled by the delegate.
57-
pub struct ShallowVisitor<P> {
58-
delegate: P,
59-
}
60-
61-
impl<P> ShallowVisitor<P> {
62-
pub fn new(delegate: P) -> Self {
63-
Self { delegate }
64-
}
69+
delegate_visitor!(
70+
/// A visitor that can visit global statements
71+
/// but does not enter the implementations of functions or subs.
72+
/// Actual visiting logic is handled by the delegate.
73+
GlobalVisitor
74+
);
6575

66-
/// Returns the delegate back.
67-
pub fn delegate(self) -> P {
68-
self.delegate
69-
}
70-
}
71-
72-
impl<P> SetPosition for ShallowVisitor<P>
73-
where
74-
P: SetPosition,
75-
{
76-
fn set_position(&mut self, pos: Position) {
77-
self.delegate.set_position(pos);
78-
}
79-
}
80-
81-
impl<P> Visitor<GlobalStatement> for ShallowVisitor<P>
76+
impl<P> Visitor<GlobalStatement> for GlobalVisitor<P>
8277
where
8378
P: Visitor<DefType>
8479
+ Visitor<FunctionDeclaration>
@@ -102,3 +97,180 @@ where
10297
}
10398
}
10499
}
100+
101+
delegate_visitor!(
102+
/// A visitor that can visit statements
103+
/// and also enters the implementations of functions or subs,
104+
/// as well as nested block statements (e.g. inside IF statements).
105+
/// Actual visiting logic is handled by the delegate.
106+
DeepStatementVisitor
107+
);
108+
109+
impl<P> Visitor<GlobalStatement> for DeepStatementVisitor<P>
110+
where
111+
P: Visitor<DefType>
112+
+ Visitor<FunctionDeclaration>
113+
+ Visitor<FunctionImplementation>
114+
+ Visitor<SubDeclaration>
115+
+ Visitor<SubImplementation>
116+
+ Visitor<UserDefinedType>
117+
+ Visitor<Statement>
118+
+ SetPosition,
119+
{
120+
fn visit(&mut self, element: &GlobalStatement) -> VisitResult {
121+
match element {
122+
GlobalStatement::DefType(def_type) => self.delegate.visit(def_type),
123+
GlobalStatement::FunctionDeclaration(f) => self.delegate.visit(f),
124+
GlobalStatement::FunctionImplementation(f) => {
125+
// notify first the delegate about the FUNCTION implementation
126+
self.delegate.visit(f)?;
127+
// then visit the body (this will go into the statements of the FUNCTION)
128+
self.visit(&f.body)
129+
}
130+
GlobalStatement::Statement(statement) => self.visit(statement),
131+
GlobalStatement::SubDeclaration(s) => self.delegate.visit(s),
132+
GlobalStatement::SubImplementation(s) => {
133+
// notify first the delegate about the SUB implementation
134+
self.delegate.visit(s)?;
135+
// then visit the body (this will go into the statements of the SUB)
136+
self.visit(&s.body)
137+
}
138+
GlobalStatement::UserDefinedType(user_defined_type) => {
139+
self.delegate.visit(user_defined_type)
140+
}
141+
}
142+
}
143+
}
144+
145+
impl<P> Visitor<Statement> for DeepStatementVisitor<P>
146+
where
147+
P: Visitor<Statement> + SetPosition,
148+
{
149+
fn visit(&mut self, element: &Statement) -> VisitResult {
150+
// first visit the delegate
151+
self.delegate.visit(element)?;
152+
153+
// then dive into the statement to for cases of blocks
154+
match element {
155+
Statement::IfBlock(if_block) => self.visit(if_block),
156+
Statement::SelectCase(select_case) => self.visit(select_case),
157+
Statement::ForLoop(for_loop) => self.visit(for_loop),
158+
Statement::While(conditional_block) => self.visit(conditional_block),
159+
Statement::DoLoop(do_loop) => self.visit(do_loop),
160+
_ => Ok(()),
161+
}
162+
}
163+
}
164+
165+
impl<P> Visitor<ForLoop> for DeepStatementVisitor<P>
166+
where
167+
P: Visitor<Statement> + SetPosition,
168+
{
169+
fn visit(&mut self, element: &ForLoop) -> VisitResult {
170+
self.visit(&element.statements)
171+
}
172+
}
173+
174+
impl<P> Visitor<SelectCase> for DeepStatementVisitor<P>
175+
where
176+
P: Visitor<Statement> + SetPosition,
177+
{
178+
fn visit(&mut self, element: &SelectCase) -> VisitResult {
179+
self.visit(&element.case_blocks)?;
180+
self.visit(&element.else_block)
181+
}
182+
}
183+
184+
impl<P> Visitor<CaseBlock> for DeepStatementVisitor<P>
185+
where
186+
P: Visitor<Statement> + SetPosition,
187+
{
188+
fn visit(&mut self, element: &CaseBlock) -> VisitResult {
189+
self.visit(&element.statements)
190+
}
191+
}
192+
193+
impl<P> Visitor<IfBlock> for DeepStatementVisitor<P>
194+
where
195+
P: Visitor<Statement> + SetPosition,
196+
{
197+
fn visit(&mut self, element: &IfBlock) -> VisitResult {
198+
self.visit(&element.if_block)?;
199+
self.visit(&element.else_if_blocks)?;
200+
self.visit(&element.else_block)
201+
}
202+
}
203+
204+
impl<P> Visitor<DoLoop> for DeepStatementVisitor<P>
205+
where
206+
P: Visitor<Statement> + SetPosition,
207+
{
208+
fn visit(&mut self, element: &DoLoop) -> VisitResult {
209+
self.visit(&element.statements)
210+
}
211+
}
212+
213+
impl<P> Visitor<ConditionalBlock> for DeepStatementVisitor<P>
214+
where
215+
P: Visitor<Statement> + SetPosition,
216+
{
217+
fn visit(&mut self, element: &ConditionalBlock) -> VisitResult {
218+
self.visit(&element.statements)
219+
}
220+
}
221+
222+
/// Creates a no-op visitor implementation
223+
/// for the given types.
224+
#[macro_export]
225+
macro_rules! no_op_visitor {
226+
($visitor_name: ident: $($types:tt),+) => {
227+
$(
228+
impl Visitor<$types> for $visitor_name {
229+
fn visit(&mut self, _element: &$types) -> VisitResult {
230+
Ok(())
231+
}
232+
}
233+
)+
234+
};
235+
}
236+
237+
#[macro_export]
238+
macro_rules! no_pos_visitor {
239+
($visitor_name: ident) => {
240+
impl SetPosition for $visitor_name {
241+
fn set_position(&mut self, _pos: Position) {}
242+
}
243+
};
244+
}
245+
246+
/// Creates a visitor that delegates to another.
247+
#[macro_export]
248+
macro_rules! delegate_visitor {
249+
($(#[$($attrss:tt)*])* $name: ident) => {
250+
$(#[$($attrss)*])*
251+
pub struct $name<P> {
252+
delegate: P
253+
}
254+
255+
impl<P> $name<P> {
256+
pub fn new(delegate: P) -> Self {
257+
Self { delegate }
258+
}
259+
260+
/// Returns the delegate back.
261+
#[allow(unused)]
262+
pub fn delegate(self) -> P {
263+
self.delegate
264+
}
265+
}
266+
267+
impl<P> SetPosition for $name<P>
268+
where
269+
P: SetPosition,
270+
{
271+
fn set_position(&mut self, pos: Position) {
272+
self.delegate.set_position(pos);
273+
}
274+
}
275+
};
276+
}

rusty_linter/src/post_linter/for_next_counter_match_linter.rs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
1-
use crate::core::{LintError, LintErrorPos};
1+
use crate::core::*;
2+
use crate::{no_op_visitor, no_pos_visitor};
23
use rusty_common::*;
3-
use rusty_parser::{Expression, ExpressionType, ForLoop, TypeQualifier, VariableInfo};
4-
5-
use super::post_conversion_linter::*;
4+
use rusty_parser::*;
65

76
pub struct ForNextCounterMatch;
87

8+
no_op_visitor!(ForNextCounterMatch: DefType, FunctionDeclaration, FunctionImplementation, SubDeclaration, SubImplementation, UserDefinedType);
9+
no_pos_visitor!(ForNextCounterMatch);
10+
11+
impl ForNextCounterMatch {
12+
pub fn visitor() -> impl Visitor<Program> + SetPosition {
13+
DeepStatementVisitor::new(Self)
14+
}
15+
}
16+
17+
impl Visitor<Statement> for ForNextCounterMatch {
18+
fn visit(&mut self, element: &Statement) -> VisitResult {
19+
match element {
20+
Statement::ForLoop(f) => self.visit(f),
21+
_ => Ok(()),
22+
}
23+
}
24+
}
25+
26+
impl Visitor<ForLoop> for ForNextCounterMatch {
27+
fn visit(&mut self, f: &ForLoop) -> crate::core::VisitResult {
28+
self.ensure_numeric_variable(f)?;
29+
self.ensure_for_next_counter_match(f)
30+
}
31+
}
32+
933
impl ForNextCounterMatch {
1034
fn ensure_numeric_variable(&self, f: &ForLoop) -> Result<(), LintErrorPos> {
1135
let Positioned {
@@ -26,7 +50,7 @@ impl ForNextCounterMatch {
2650
ExpressionType::BuiltIn(_) => Ok(()),
2751
_ => Err(LintError::TypeMismatch.at_pos(*pos)),
2852
},
29-
_ => unimplemented!(),
53+
_ => panic!("It should not be possible for the FOR variable to be something othe than a variable"),
3054
}
3155
}
3256

@@ -58,11 +82,3 @@ impl ForNextCounterMatch {
5882
}
5983
}
6084
}
61-
62-
impl PostConversionLinter for ForNextCounterMatch {
63-
fn visit_for_loop(&mut self, f: &ForLoop) -> Result<(), LintErrorPos> {
64-
self.visit_statements(&f.statements)?;
65-
self.ensure_numeric_variable(f)?;
66-
self.ensure_for_next_counter_match(f)
67-
}
68-
}

rusty_linter/src/post_linter/post_linter.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::core::HasSubprograms;
22
use crate::core::LintErrorPos;
3+
use crate::core::Visitor;
34
use crate::post_linter::expression_reducer::ExpressionReducer;
45
use crate::post_linter::post_conversion_linter::PostConversionLinter;
56
use crate::post_linter::{
@@ -24,17 +25,17 @@ fn apply_linters(
2425
result: &Program,
2526
linter_context: &impl HasSubprograms,
2627
) -> Result<(), LintErrorPos> {
27-
let mut linter = for_next_counter_match_linter::ForNextCounterMatch {};
28-
linter.visit_program(result)?;
28+
let mut linter = for_next_counter_match_linter::ForNextCounterMatch::visitor();
29+
linter.visit(result)?;
2930

3031
let mut linter = dots_linter::DotsLinter::default();
3132
linter.visit_program(result)?;
3233

3334
let mut linter = built_in_linter::BuiltInLinter::new();
3435
linter.visit_program(result)?;
3536

36-
let mut linter = print_linter::PrintLinter {};
37-
linter.visit_program(result)?;
37+
let mut linter = print_linter::PrintLinter::visitor();
38+
linter.visit(result)?;
3839

3940
let mut linter = user_defined_function_linter::UserDefinedFunctionLinter { linter_context };
4041
linter.visit_program(result)?;

rusty_linter/src/post_linter/print_linter.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
1-
use super::post_conversion_linter::PostConversionLinter;
2-
use crate::core::{LintError, LintErrorPos};
3-
use rusty_common::AtPos;
4-
use rusty_parser::{ExpressionType, HasExpressionType, Print, PrintArg, TypeQualifier};
1+
use crate::core::*;
2+
use crate::{no_op_visitor, no_pos_visitor};
3+
use rusty_common::*;
4+
use rusty_parser::*;
55

66
pub struct PrintLinter;
77

8-
impl PostConversionLinter for PrintLinter {
9-
fn visit_print(&mut self, print: &Print) -> Result<(), LintErrorPos> {
8+
no_op_visitor!(PrintLinter: DefType, FunctionDeclaration, FunctionImplementation, SubDeclaration, SubImplementation, UserDefinedType);
9+
no_pos_visitor!(PrintLinter);
10+
11+
impl PrintLinter {
12+
pub fn visitor() -> impl Visitor<Program> + SetPosition {
13+
DeepStatementVisitor::new(Self)
14+
}
15+
}
16+
17+
impl Visitor<Statement> for PrintLinter {
18+
fn visit(&mut self, element: &Statement) -> VisitResult {
19+
match element {
20+
Statement::Print(p) => self.visit(p),
21+
_ => Ok(()),
22+
}
23+
}
24+
}
25+
26+
impl Visitor<Print> for PrintLinter {
27+
fn visit(&mut self, print: &Print) -> VisitResult {
1028
if let Some(f) = &print.format_string {
1129
if f.expression_type() != ExpressionType::BuiltIn(TypeQualifier::DollarString) {
1230
return Err(LintError::TypeMismatch.at(f));

rusty_linter/src/pre_linter/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ struct MainContext {
2020
}
2121

2222
pub fn pre_lint_program(program: &Program) -> Result<PreLinterResult, LintErrorPos> {
23-
let mut visitor = ShallowVisitor::new(MainContext::default());
23+
let mut visitor = GlobalVisitor::new(MainContext::default());
2424
visitor.visit(program)?;
2525
let ctx = visitor.delegate();
2626
ctx.post_visit_functions()?;

0 commit comments

Comments
 (0)