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 enum BraceIndentInfoFlags
12 {
13     tempIndent = 1 << 0,
14 }
15 
16 struct BraceIndentInfo
17 {
18     size_t startLocation;
19     size_t endLocation;
20 
21     uint flags;
22 
23     uint beginIndentLevel;
24 }
25 
26 /// AST information that is needed by the formatter.
27 struct ASTInformation
28 {
29     /// Sorts the arrays so that binary search will work on them
30     void cleanup()
31     {
32         import std.algorithm : sort;
33 
34         sort(doubleNewlineLocations);
35         sort(spaceAfterLocations);
36         sort(unaryLocations);
37         sort(attributeDeclarationLines);
38         sort(caseEndLocations);
39         sort(structInitStartLocations);
40         sort(structInitEndLocations);
41         sort(funLitStartLocations);
42         sort(funLitEndLocations);
43         sort(conditionalWithElseLocations);
44         sort(conditionalStatementLocations);
45         sort(arrayStartLocations);
46         sort(contractLocations);
47         sort(constraintLocations);
48         sort(constructorDestructorLocations);
49         sort(staticConstructorDestructorLocations);
50         sort(sharedStaticConstructorDestructorLocations);
51 
52         sort!((a,b) => a.endLocation < b.endLocation)
53             (indentInfoSortedByEndLocation);
54     }
55 
56     /// Locations of end braces for struct bodies
57     size_t[] doubleNewlineLocations;
58 
59     /// Locations of tokens where a space is needed (such as the '*' in a type)
60     size_t[] spaceAfterLocations;
61 
62     /// Locations of unary operators
63     size_t[] unaryLocations;
64 
65     /// Lines containing attribute declarations
66     size_t[] attributeDeclarationLines;
67 
68     /// Case statement colon locations
69     size_t[] caseEndLocations;
70 
71     /// Opening braces of struct initializers
72     size_t[] structInitStartLocations;
73 
74     /// Closing braces of struct initializers
75     size_t[] structInitEndLocations;
76 
77     /// Opening braces of function literals
78     size_t[] funLitStartLocations;
79 
80     /// Closing braces of function literals
81     size_t[] funLitEndLocations;
82 
83     /// Conditional statements that have matching "else" statements
84     size_t[] conditionalWithElseLocations;
85 
86     /// Conditional statement locations
87     size_t[] conditionalStatementLocations;
88 
89     /// Locations of start locations of array initializers
90     size_t[] arrayStartLocations;
91 
92     /// Locations of "in" and "out" tokens that begin contracts
93     size_t[] contractLocations;
94 
95     /// Locations of template constraint "if" tokens
96     size_t[] constraintLocations;
97 
98     /// Locations of constructor/destructor "shared" tokens ?
99     size_t[] sharedStaticConstructorDestructorLocations;
100 
101     /// Locations of constructor/destructor "static" tokens ?
102     size_t[] staticConstructorDestructorLocations;
103 
104     /// Locations of constructor/destructor "this" tokens ?
105     size_t[] constructorDestructorLocations;
106 
107     BraceIndentInfo[] indentInfoSortedByEndLocation;
108 }
109 
110 /// Collects information from the AST that is useful for the formatter
111 final class FormatVisitor : ASTVisitor
112 {
113     alias visit = ASTVisitor.visit;
114 
115     /**
116      * Params:
117      *     astInformation = the AST information that will be filled in
118      */
119     this(ASTInformation* astInformation)
120     {
121         this.astInformation = astInformation;
122     }
123 
124     override void visit(const ArrayInitializer arrayInitializer)
125     {
126         astInformation.arrayStartLocations ~= arrayInitializer.startLocation;
127         arrayInitializer.accept(this);
128     }
129 
130     override void visit (const SharedStaticConstructor sharedStaticConstructor)
131     {
132         astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticConstructor.location;
133         sharedStaticConstructor.accept(this);
134     }
135 
136     override void visit (const SharedStaticDestructor sharedStaticDestructor)
137     {
138         astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticDestructor.location;
139         sharedStaticDestructor.accept(this);
140     }
141 
142     override void visit (const StaticConstructor staticConstructor)
143     {
144         astInformation.staticConstructorDestructorLocations ~= staticConstructor.location;
145         staticConstructor.accept(this);
146     }
147 
148     override void visit (const StaticDestructor staticDestructor)
149     {
150         astInformation.staticConstructorDestructorLocations ~= staticDestructor.location;
151         staticDestructor.accept(this);
152     }
153 
154     override void visit (const Constructor constructor)
155     {
156         astInformation.constructorDestructorLocations ~= constructor.location;
157         constructor.accept(this);
158     }
159 
160     override void visit (const Destructor destructor)
161     {
162         astInformation.constructorDestructorLocations ~= destructor.index;
163         destructor.accept(this);
164     }
165 
166     override void visit(const ConditionalDeclaration dec)
167     {
168         if (dec.hasElse)
169         {
170             auto condition = dec.compileCondition;
171             if (condition.versionCondition !is null)
172             {
173                 astInformation.conditionalWithElseLocations
174                     ~= condition.versionCondition.versionIndex;
175             }
176             else if (condition.debugCondition !is null)
177             {
178                 astInformation.conditionalWithElseLocations ~= condition.debugCondition.debugIndex;
179             }
180             // Skip "static if" because the formatting for normal "if" handles
181             // it properly
182         }
183         dec.accept(this);
184     }
185 
186     override void visit(const Constraint constraint)
187     {
188         astInformation.constraintLocations ~= constraint.location;
189         constraint.accept(this);
190     }
191 
192     override void visit(const ConditionalStatement statement)
193     {
194         auto condition = statement.compileCondition;
195         if (condition.versionCondition !is null)
196         {
197             astInformation.conditionalStatementLocations ~= condition.versionCondition.versionIndex;
198         }
199         else if (condition.debugCondition !is null)
200         {
201             astInformation.conditionalStatementLocations ~= condition.debugCondition.debugIndex;
202         }
203         statement.accept(this);
204     }
205 
206     override void visit(const FunctionLiteralExpression funcLit)
207     {
208         if (funcLit.functionBody !is null)
209         {
210             const bs = funcLit.functionBody.blockStatement;
211 
212             astInformation.funLitStartLocations ~= bs.startLocation;
213             astInformation.funLitEndLocations ~= bs.endLocation;
214             astInformation.indentInfoSortedByEndLocation ~=
215                 BraceIndentInfo(bs.startLocation, bs.endLocation);
216         }
217         funcLit.accept(this);
218     }
219 
220     override void visit(const DefaultStatement defaultStatement)
221     {
222         astInformation.caseEndLocations ~= defaultStatement.colonLocation;
223         defaultStatement.accept(this);
224     }
225 
226     override void visit(const CaseStatement caseStatement)
227     {
228         astInformation.caseEndLocations ~= caseStatement.colonLocation;
229         caseStatement.accept(this);
230     }
231 
232     override void visit(const CaseRangeStatement caseRangeStatement)
233     {
234         astInformation.caseEndLocations ~= caseRangeStatement.colonLocation;
235         caseRangeStatement.accept(this);
236     }
237 
238     override void visit(const FunctionBody functionBody)
239     {
240         if (functionBody.blockStatement !is null)
241             astInformation.doubleNewlineLocations ~= functionBody.blockStatement.endLocation;
242         if (functionBody.bodyStatement !is null && functionBody.bodyStatement
243                 .blockStatement !is null)
244             astInformation.doubleNewlineLocations
245                 ~= functionBody.bodyStatement.blockStatement.endLocation;
246         functionBody.accept(this);
247     }
248 
249     override void visit(const StructInitializer structInitializer)
250     {
251         astInformation.structInitStartLocations ~= structInitializer.startLocation;
252         astInformation.structInitEndLocations ~= structInitializer.endLocation;
253         astInformation.indentInfoSortedByEndLocation ~=
254             BraceIndentInfo(structInitializer.startLocation, structInitializer.endLocation);
255 
256         structInitializer.accept(this);
257     }
258 
259     override void visit(const EnumBody enumBody)
260     {
261         astInformation.doubleNewlineLocations ~= enumBody.endLocation;
262         enumBody.accept(this);
263     }
264 
265     override void visit(const Unittest unittest_)
266     {
267         astInformation.doubleNewlineLocations ~= unittest_.blockStatement.endLocation;
268         unittest_.accept(this);
269     }
270 
271     override void visit(const Invariant invariant_)
272     {
273         astInformation.doubleNewlineLocations ~= invariant_.blockStatement.endLocation;
274         invariant_.accept(this);
275     }
276 
277     override void visit(const StructBody structBody)
278     {
279         astInformation.doubleNewlineLocations ~= structBody.endLocation;
280         structBody.accept(this);
281     }
282 
283     override void visit(const TemplateDeclaration templateDeclaration)
284     {
285         astInformation.doubleNewlineLocations ~= templateDeclaration.endLocation;
286         templateDeclaration.accept(this);
287     }
288 
289     override void visit(const TypeSuffix typeSuffix)
290     {
291         if (typeSuffix.star.type != tok!"")
292             astInformation.spaceAfterLocations ~= typeSuffix.star.index;
293         typeSuffix.accept(this);
294     }
295 
296     override void visit(const UnaryExpression unary)
297     {
298         if (unary.prefix.type == tok!"~" || unary.prefix.type == tok!"&"
299                 || unary.prefix.type == tok!"*"
300                 || unary.prefix.type == tok!"+" || unary.prefix.type == tok!"-")
301         {
302             astInformation.unaryLocations ~= unary.prefix.index;
303         }
304         unary.accept(this);
305     }
306 
307     override void visit(const AttributeDeclaration attributeDeclaration)
308     {
309         astInformation.attributeDeclarationLines ~= attributeDeclaration.line;
310         attributeDeclaration.accept(this);
311     }
312 
313     override void visit(const InStatement inStatement)
314     {
315         astInformation.contractLocations ~= inStatement.inTokenLocation;
316         inStatement.accept(this);
317     }
318 
319     override void visit(const OutStatement outStatement)
320     {
321         astInformation.contractLocations ~= outStatement.outTokenLocation;
322         outStatement.accept(this);
323     }
324 
325 private:
326     ASTInformation* astInformation;
327 }