1 //          Copyright Brian Schott 2015.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dfmt.ast_info;
7 
8 import dparse.lexer;
9 import dparse.ast;
10 
11 /// AST information that is needed by the formatter.
12 struct ASTInformation
13 {
14     /// Sorts the arrays so that binary search will work on them
15     void cleanup()
16     {
17         import std.algorithm : sort;
18 
19         sort(doubleNewlineLocations);
20         sort(spaceAfterLocations);
21         sort(unaryLocations);
22         sort(attributeDeclarationLines);
23         sort(caseEndLocations);
24         sort(structInitStartLocations);
25         sort(structInitEndLocations);
26         sort(funLitStartLocations);
27         sort(funLitEndLocations);
28         sort(conditionalWithElseLocations);
29         sort(conditionalStatementLocations);
30         sort(arrayStartLocations);
31         sort(contractLocations);
32         sort(constraintLocations);
33     }
34 
35     /// Locations of end braces for struct bodies
36     size_t[] doubleNewlineLocations;
37 
38     /// Locations of tokens where a space is needed (such as the '*' in a type)
39     size_t[] spaceAfterLocations;
40 
41     /// Locations of unary operators
42     size_t[] unaryLocations;
43 
44     /// Lines containing attribute declarations
45     size_t[] attributeDeclarationLines;
46 
47     /// Case statement colon locations
48     size_t[] caseEndLocations;
49 
50     /// Opening braces of struct initializers
51     size_t[] structInitStartLocations;
52 
53     /// Closing braces of struct initializers
54     size_t[] structInitEndLocations;
55 
56     /// Opening braces of function literals
57     size_t[] funLitStartLocations;
58 
59     /// Closing braces of function literals
60     size_t[] funLitEndLocations;
61 
62     /// Conditional statements that have matching "else" statements
63     size_t[] conditionalWithElseLocations;
64 
65     /// Conditional statement locations
66     size_t[] conditionalStatementLocations;
67 
68     /// Locations of start locations of array initializers
69     size_t[] arrayStartLocations;
70 
71     /// Locations of "in" and "out" tokens that begin contracts
72     size_t[] contractLocations;
73 
74     /// Locations of template constraint "if" tokens
75     size_t[] constraintLocations;
76 }
77 
78 /// Collects information from the AST that is useful for the formatter
79 final class FormatVisitor : ASTVisitor
80 {
81     /**
82      * Params:
83      *     astInformation = the AST information that will be filled in
84      */
85     this(ASTInformation* astInformation)
86     {
87         this.astInformation = astInformation;
88     }
89 
90     override void visit(const ArrayInitializer arrayInitializer)
91     {
92         astInformation.arrayStartLocations ~= arrayInitializer.startLocation;
93         arrayInitializer.accept(this);
94     }
95 
96     override void visit(const ConditionalDeclaration dec)
97     {
98         if (dec.hasElse)
99         {
100             auto condition = dec.compileCondition;
101             if (condition.versionCondition !is null)
102             {
103                 astInformation.conditionalWithElseLocations
104                     ~= condition.versionCondition.versionIndex;
105             }
106             else if (condition.debugCondition !is null)
107             {
108                 astInformation.conditionalWithElseLocations ~= condition.debugCondition.debugIndex;
109             }
110             // Skip "static if" because the formatting for normal "if" handles
111             // it properly
112         }
113         dec.accept(this);
114     }
115 
116     override void visit(const Constraint constraint)
117     {
118         astInformation.constraintLocations ~= constraint.location;
119         constraint.accept(this);
120     }
121 
122     override void visit(const ConditionalStatement statement)
123     {
124         auto condition = statement.compileCondition;
125         if (condition.versionCondition !is null)
126         {
127             astInformation.conditionalStatementLocations ~= condition.versionCondition.versionIndex;
128         }
129         else if (condition.debugCondition !is null)
130         {
131             astInformation.conditionalStatementLocations ~= condition.debugCondition.debugIndex;
132         }
133         statement.accept(this);
134     }
135 
136     override void visit(const FunctionLiteralExpression funcLit)
137     {
138         if (funcLit.functionBody !is null)
139         {
140             astInformation.funLitStartLocations ~= funcLit.functionBody.blockStatement.startLocation;
141             astInformation.funLitEndLocations ~= funcLit.functionBody.blockStatement.endLocation;
142         }
143         funcLit.accept(this);
144     }
145 
146     override void visit(const DefaultStatement defaultStatement)
147     {
148         astInformation.caseEndLocations ~= defaultStatement.colonLocation;
149         defaultStatement.accept(this);
150     }
151 
152     override void visit(const CaseStatement caseStatement)
153     {
154         astInformation.caseEndLocations ~= caseStatement.colonLocation;
155         caseStatement.accept(this);
156     }
157 
158     override void visit(const CaseRangeStatement caseRangeStatement)
159     {
160         astInformation.caseEndLocations ~= caseRangeStatement.colonLocation;
161         caseRangeStatement.accept(this);
162     }
163 
164     override void visit(const FunctionBody functionBody)
165     {
166         if (functionBody.blockStatement !is null)
167             astInformation.doubleNewlineLocations ~= functionBody.blockStatement.endLocation;
168         if (functionBody.bodyStatement !is null && functionBody.bodyStatement
169                 .blockStatement !is null)
170             astInformation.doubleNewlineLocations
171                 ~= functionBody.bodyStatement.blockStatement.endLocation;
172         functionBody.accept(this);
173     }
174 
175     override void visit(const StructInitializer structInitializer)
176     {
177         astInformation.structInitStartLocations ~= structInitializer.startLocation;
178         astInformation.structInitEndLocations ~= structInitializer.endLocation;
179         structInitializer.accept(this);
180     }
181 
182     override void visit(const EnumBody enumBody)
183     {
184         astInformation.doubleNewlineLocations ~= enumBody.endLocation;
185         enumBody.accept(this);
186     }
187 
188     override void visit(const Unittest unittest_)
189     {
190         astInformation.doubleNewlineLocations ~= unittest_.blockStatement.endLocation;
191         unittest_.accept(this);
192     }
193 
194     override void visit(const Invariant invariant_)
195     {
196         astInformation.doubleNewlineLocations ~= invariant_.blockStatement.endLocation;
197         invariant_.accept(this);
198     }
199 
200     override void visit(const StructBody structBody)
201     {
202         astInformation.doubleNewlineLocations ~= structBody.endLocation;
203         structBody.accept(this);
204     }
205 
206     override void visit(const TemplateDeclaration templateDeclaration)
207     {
208         astInformation.doubleNewlineLocations ~= templateDeclaration.endLocation;
209         templateDeclaration.accept(this);
210     }
211 
212     override void visit(const TypeSuffix typeSuffix)
213     {
214         if (typeSuffix.star.type != tok!"")
215             astInformation.spaceAfterLocations ~= typeSuffix.star.index;
216         typeSuffix.accept(this);
217     }
218 
219     override void visit(const UnaryExpression unary)
220     {
221         if (unary.prefix.type == tok!"~" || unary.prefix.type == tok!"&"
222                 || unary.prefix.type == tok!"*"
223                 || unary.prefix.type == tok!"+" || unary.prefix.type == tok!"-")
224         {
225             astInformation.unaryLocations ~= unary.prefix.index;
226         }
227         unary.accept(this);
228     }
229 
230     override void visit(const AttributeDeclaration attributeDeclaration)
231     {
232         astInformation.attributeDeclarationLines ~= attributeDeclaration.line;
233         attributeDeclaration.accept(this);
234     }
235 
236     override void visit(const InStatement inStatement)
237     {
238         astInformation.contractLocations ~= inStatement.inTokenLocation;
239         inStatement.accept(this);
240     }
241 
242     override void visit(const OutStatement outStatement)
243     {
244         astInformation.contractLocations ~= outStatement.outTokenLocation;
245         outStatement.accept(this);
246     }
247 
248 private:
249     ASTInformation* astInformation;
250     alias visit = ASTVisitor.visit;
251 }