1 /**
2  * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 29, 2012
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.Output;
8 
9 static import std.array;
10 
11 import tango.util.container.HashSet;
12 
13 import mambo.core._;
14 
15 import clang.Cursor;
16 import dstep.translator.IncludeHandler;
17 import dstep.translator.Type;
18 
19 Output output;
20 
21 static this ()
22 {
23     output = new Output;
24 }
25 
26 class Output
27 {
28     String currentContext;
29 
30     alias currentContext this;
31 
32     String before;
33     String after;
34     String imports;
35     string externDeclaration;
36 
37     string[] typedefs;
38     string[] variables;
39 
40     string[] classes;
41     string[] interfaces;
42     string[] structs;
43     string[] enums;
44     string[] unions;
45     string[] functions;
46 
47     ClassData currentClass;
48     ClassData currentInterface;
49 
50     this ()
51     {
52         before = new String;
53         after = new String;
54         imports = new String;
55         currentContext = new String;
56 
57         currentClass = new ClassData;
58         currentInterface = new ClassData;
59     }
60 
61     @property string data ()
62     {
63         newContext();
64 
65         this ~= before.data;
66         addDeclarations(includeHandler.toImports(), false);
67         this ~= imports.data;
68 
69         if (imports.any)
70             this ~= nl;
71 
72         if (externDeclaration.isPresent)
73         {
74             this ~= externDeclaration;
75             currentContext.put(nl, nl);
76         }
77 
78         addDeclarations(typedefs, false);
79         addDeclarations(variables, false);
80         addDeclarations(enums);
81         addDeclarations(structs);
82         addDeclarations(unions);
83         addDeclarations(classes);
84         addDeclarations(interfaces);
85         addDeclarations(functions, false);
86 
87         this ~= after.data;
88 
89         return currentContext.data;
90     }
91 
92     /**
93      * Creates a new context and sets it as the current context. Returns the newly created
94      * context.
95      */
96     String newContext ()
97     {
98         auto context = new String;
99         context.indentationLevel = currentContext.indentationLevel;
100         return currentContext = context;
101     }
102 
103     override string toString ()
104     {
105         return data.strip('\n');
106     }
107 
108 private:
109 
110     void addDeclarations (string[] declarations, bool extraNewline = true)
111     {
112         auto newline = "\n";
113 
114         if (extraNewline)
115             newline ~= "\n";
116 
117         this ~= declarations.join(newline);
118 
119         if (declarations.any)
120             this ~= "\n\n";
121     }
122 }
123 
124 class StructData
125 {
126     string name;
127 
128     string[] instanceVariables;
129 
130     bool isFwdDeclaration;
131 
132     @property string data ()
133     {
134         auto context = output.newContext();
135 
136         if (name.isPresent)
137             name = ' ' ~ name;
138 
139         if (isFwdDeclaration)
140         {
141             context.put(type, name, ";", nl);
142             isFwdDeclaration = true;
143         }
144         else
145         {
146             context.put(type, name, nl, '{', nl);
147 
148             context.indent in {
149                 addDeclarations(context, instanceVariables);
150             };
151 
152             auto str = context.data.strip('\n');
153             context = output.newContext();
154             context ~= str;
155             context.put(nl, '}');
156         }
157 
158         return context.data;
159     }
160 
161 protected:
162 
163     @property string type ()
164     {
165         return "struct";
166     }
167 
168     void addDeclarations (String context, string[] declarations)
169     {
170         foreach (i, e ; declarations)
171         {
172             if (i != 0)
173                 context ~= nl;
174 
175             context ~= e;
176         }
177 
178         if (declarations.any)
179         {
180             context ~= nl;
181             context ~= nl;
182         }
183     }
184 }
185 
186 class EnumData : StructData
187 {
188     @property override string type ()
189     {
190         return "enum";
191     }
192 
193 protected:
194 
195     override void addDeclarations (String context, string[] declarations)
196     {
197         foreach (i, e ; declarations)
198         {
199             if (i != 0)
200             {
201                 context ~= ",";
202                 context ~= nl;
203             }
204 
205             context ~= e;
206         }
207 
208         if (declarations.any)
209         {
210             context ~= nl;
211             context ~= nl;
212         }
213     }
214 }
215 
216 class UnionData : StructData
217 {
218     @property override string type ()
219     {
220         return "union";
221     }
222 }
223 
224 class ClassData : StructData
225 {
226     string[] instanceMethods;
227     string[] staticMethods;
228     string[] properties;
229     string[] staticProperties;
230     string[] staticVariables;
231 
232     string name;
233     string superclass;
234     string[] interfaces;
235 
236     HashSet!(string) propertyList;
237 
238     private bool[string] mangledMethods;
239 
240     this ()
241     {
242         propertyList = new HashSet!(string);
243     }
244 
245     string getMethodName (FunctionCursor func, string name = "", bool translateIdentifier = true)
246     {
247         auto mangledName = mangle(func, name);
248         auto selector = func.spelling;
249 
250         if (!(mangledName in mangledMethods))
251         {
252             mangledMethods[mangledName] = true;
253             name = name.isBlank ? selector : name;
254             return translateSelector(name, false, translateIdentifier);
255         }
256 
257         return translateSelector(name, true, translateIdentifier);
258     }
259 
260     private string mangle (FunctionCursor func, string name)
261     {
262         auto selector = func.spelling;
263         name = name.isBlank ? translateSelector(selector) : name;
264         auto mangledName = name;
265 
266         foreach (param ; func.parameters)
267             mangledName ~= "_" ~ translateType(param.type);
268 
269         return mangledName;
270     }
271 
272     @property override string data ()
273     {
274         auto cls = output.newContext();
275 
276         cls.put(type, ' ', name);
277 
278         if (superclass.any)
279             cls.put(" : ", superclass);
280 
281         writeInterfaces(cls);
282         cls.put(nl, '{', nl);
283         writeMembers(cls);
284 
285         auto context = output.newContext();
286         context ~= cls.data.strip('\n');
287         context.put(nl, '}');
288 
289         return context.data;
290     }
291 
292     override protected @property string type ()
293     {
294         return "class";
295     }
296 
297 private:
298 
299     void writeInterfaces (String cls)
300     {
301         if (interfaces.any)
302         {
303             if (superclass.isEmpty)
304                 cls ~= " : ";
305 
306             foreach (i, s ; interfaces)
307             {
308                 if (i != 0)
309                     cls ~= ", ";
310 
311                 cls ~= s;
312             }
313         }
314     }
315 
316     void writeMembers (String cls)
317     {
318         cls.indent in {
319             addDeclarations(cls, staticVariables);
320             addDeclarations(cls, instanceVariables);
321             addDeclarations(cls, staticProperties);
322             addDeclarations(cls, properties);
323             addDeclarations(cls, staticMethods);
324             addDeclarations(cls, instanceMethods);
325         };
326     }
327 }
328 
329 class InterfaceData : ClassData
330 {
331     protected @property override string type ()
332     {
333         return "interface";
334     }
335 }
336 
337 class ClassExtensionData : ClassData
338 {
339     protected @property override string type ()
340     {
341         return "__classext";
342     }
343 }
344 
345 class String
346 {
347     int indentationLevel;
348 
349     private
350     {
351         std.array.Appender!(string) appender;
352         int prevIndendationLevel;
353         bool shouldIndent;
354     }
355 
356     String opOpAssign (string op, T) (T t) if (op == "~" && !is(T == NewLine))
357     {
358         return put(t);
359     }
360 
361     String opOpAssign (string op) (NewLine) if (op == "~")
362     {
363         return put(nl);
364     }
365 
366     String put (Args...) (Args args)// if (!is(T == NewLine))
367     {
368         foreach (arg ; args)
369         {
370             static if (is(typeof(arg) == NewLine))
371                 put(nl);
372 
373             else
374             {
375                 if (shouldIndent)
376                 {
377                     _indent();
378                     shouldIndent = false;
379                 }
380 
381                 appender.put(arg);
382             }
383         }
384 
385         return this;
386     }
387 
388     String put () (NewLine)
389     {
390         appender.put('\n');
391         shouldIndent = indentationLevel > 0;
392 
393         return this;
394     }
395 
396     alias put append;
397 
398     String appendnl (T) (T t)
399     {
400         put(t);
401         return put(nl);
402     }
403 
404     @property string data ()
405     {
406         return appender.data;
407     }
408 
409     @property bool isEmpty ()
410     {
411         return appender.data.isEmpty;
412     }
413 
414     Indendation indent ()
415     {
416         return indent(indentationLevel + 1);
417     }
418 
419     Indendation indent (int indentationLevel)
420     {
421         prevIndendationLevel = this.indentationLevel;
422         this.indentationLevel = indentationLevel;
423         return Indendation(this);
424     }
425 
426     static struct Indendation
427     {
428         private String str;
429 
430         void opIn (void delegate () dg)
431         {
432             str.shouldIndent = str.indentationLevel > 0;
433             dg();
434             output.currentContext = str;
435             str.indentationLevel = str.prevIndendationLevel;
436         }
437     }
438 
439 private:
440 
441     void _indent ()
442     {
443         foreach (i ; 0 .. indentationLevel)
444             appender.put("    ");
445     }
446 }
447 
448 struct NewLine {}
449 NewLine nl;