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('\t');
445 	}
446 }
447 
448 struct NewLine {}
449 NewLine nl;