1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Oct 6, 2011
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.Translator;
8 
9 import std.file;
10 
11 import mambo.core._;
12 
13 import clang.c.index;
14 import clang.Cursor;
15 import clang.File;
16 import clang.TranslationUnit;
17 import clang.Type;
18 import clang.Util;
19 
20 import dstep.translator.Declaration;
21 import dstep.translator.Enum;
22 import dstep.translator.IncludeHandler;
23 import dstep.translator.objc.Category;
24 import dstep.translator.objc.ObjcInterface;
25 import dstep.translator.Output;
26 import dstep.translator.Record;
27 import dstep.translator.Type;
28 
29 private static string[Cursor] anonymousNames;
30 
31 class Translator
32 {	
33 	static struct Options
34 	{
35 		string outputFile;
36 		Language language = Language.c;
37 	}
38 	
39 	private
40 	{
41 		TranslationUnit translationUnit;
42 
43 		string outputFile;
44 		string inputFilename;
45 		File inputFile;
46 		Language language;
47 		string[string] deferredDeclarations;
48 	}
49 	
50 	this (string inputFilename, TranslationUnit translationUnit, const Options options = Options.init)
51 	{
52 		this.inputFilename = inputFilename;
53 		this.translationUnit = translationUnit;
54 		outputFile = options.outputFile;
55 		language = options.language;
56 
57 		inputFile = translationUnit.file(inputFilename);
58 	}
59 	
60 	void translate ()
61 	{
62 		foreach (cursor, parent ; translationUnit.declarations)
63 		{
64 			if (skipDeclaration(cursor))
65 				continue;
66 
67 			output.newContext();
68 			auto code = translate(cursor, parent);
69 
70 			with (CXCursorKind)
71 				switch (cursor.kind)
72 				{
73 					case CXCursor_ObjCInterfaceDecl:
74 					case CXCursor_ObjCProtocolDecl:
75 					case CXCursor_ObjCCategoryDecl:
76 						output.classes ~= code;
77 					break;
78 
79 					case CXCursor_StructDecl:
80 						if (cursor.isDefinition)
81 							output.structs ~= code;
82 						break;
83 					case CXCursor_EnumDecl: output.enums ~= code; break;
84 					case CXCursor_UnionDecl: output.unions ~= code; break;
85 					case CXCursor_VarDecl: output.variables ~= code; break;
86 					case CXCursor_FunctionDecl: output.functions ~= code; break;
87 					case CXCursor_TypedefDecl: output.typedefs ~= code; break;
88 
89 					default: continue;
90 				}
91 		}
92 
93 		output.structs ~= deferredDeclarations.values;
94 		output.externDeclaration = externDeclaration();
95 
96 		auto data = output.toString;
97 		write(outputFile, data);
98 	}
99 	
100 	string translate (Cursor cursor, Cursor parent = Cursor.empty)
101 	{
102 		with (CXCursorKind)
103 			switch (cursor.kind)
104 			{
105 				case CXCursor_ObjCInterfaceDecl:
106 					return (new ObjcInterface!(ClassData)(cursor, parent, this)).translate;
107 				break;
108 
109 				case CXCursor_ObjCProtocolDecl:
110 					return (new ObjcInterface!(InterfaceData)(cursor, parent, this)).translate;
111 				break;
112 
113 				case CXCursor_ObjCCategoryDecl:
114 					return (new Category(cursor, parent, this)).translate;
115 				break;
116 
117 				case CXCursor_VarDecl:
118 				{
119 					auto context = output.newContext();
120 					version (D1)
121 						context ~= "extern ";
122 					else
123 						context ~= "extern __gshared ";
124 					return variable(cursor, context);
125 				}
126 				break;
127 			
128 				case CXCursor_FunctionDecl:
129 				{
130 					auto name = translateIdentifier(cursor.spelling);
131 					return translateFunction(cursor.func, name, output) ~ ";";
132 				}
133 				break;
134 			
135 				case CXCursor_TypedefDecl:
136 					return typedef_(cursor, output.newContext);
137 				break;
138 			
139 				case CXCursor_StructDecl:
140 					auto code = (new Record!(StructData)(cursor, parent, this)).translate;
141 					if (cursor.isDefinition)
142 					{
143 						if (cursor.spelling in deferredDeclarations)
144 							deferredDeclarations.remove(cursor.spelling);
145 						return code;
146 					}
147 					else
148 					{
149 						deferredDeclarations[cursor.spelling] = code;
150 						return "";
151 					}
152 					break;
153 				case CXCursor_EnumDecl: return (new Enum(cursor, parent, this)).translate; break;
154 				case CXCursor_UnionDecl: return (new Record!(UnionData)(cursor, parent, this)).translate; break;
155 			
156 				default:
157 					return "";
158 					//assert(0, `Translator.translate: missing implementation for "` ~ cursor.kind.toString ~ `".`);
159 			}
160 	}
161 	
162 	string variable (Cursor cursor, String context = null)
163 	{
164 		if (!context)
165 			context = output;
166 
167 		context ~= translateType(cursor.type);
168 		context ~= " " ~ translateIdentifier(cursor.spelling);
169 		context ~= ";";
170 		
171 		return context.data;
172 	}
173 	
174 	string typedef_ (Cursor cursor, String context = output)
175 	{
176 		context ~= "alias ";
177 		context ~= translateType(cursor.type.canonicalType);
178 		context ~= " " ~ cursor.spelling;
179 		context ~= ";";
180 		
181 		return context.data;
182 	}
183 	
184 private:
185 
186 	bool skipDeclaration (Cursor cursor)
187 	{
188 		return inputFile != cursor.location.spelling.file;
189 	}
190 
191 	string externDeclaration ()
192 	{
193 		final switch (language)
194 		{
195 			case Language.c: return "extern (C):";
196 			case Language.objC: return "extern (Objective-C):";
197 			// case Language.cpp: return "extern (C++):";
198 		}
199 	}
200 }
201 
202 string translateFunction (FunctionCursor func, string name, String context, bool isStatic = false)
203 {
204 	if (isStatic)
205 		context ~= "static ";
206 		
207 	Parameter[] params;
208 
209 	if (func.type.isValid) // This will be invalid of Objective-C methods
210 		params.reserve(func.type.func.arguments.length);
211 	
212 	foreach (param ; func.parameters)
213 	{
214 		auto type = translateType(param.type);
215 		params ~= Parameter(type, param.spelling);
216 	}
217 
218 	auto resultType = translateType(func.resultType);
219 
220 	return translateFunction(resultType, name, params, func.isVariadic, context);
221 }
222 
223 string getAnonymousName (Cursor cursor)
224 {
225 	if (auto name = cursor in anonymousNames)
226 		return *name;
227 
228 	return "";
229 }
230 
231 string generateAnonymousName (Cursor cursor)
232 {
233 	auto name = getAnonymousName(cursor);
234 
235 	if (name.isBlank)
236 	{
237 		name = "_Anonymous_" ~ anonymousNames.length.toString;
238 		anonymousNames[cursor] = name;
239 	}
240 
241 	return name;
242 }
243 
244 package struct Parameter
245 {
246 	string type;
247 	string name;
248 	bool isConst;
249 }
250 
251 package string translateFunction (string result, string name, Parameter[] parameters, bool variadic, String context)
252 {
253 	context ~= result;
254 	context ~= ' ';
255 	context ~= name ~ " (";
256 
257 	string[] params;
258 	params.reserve(parameters.length);
259 
260 	foreach (param ; parameters)
261 	{
262 		string p;
263 
264 		version(D1)
265 		{
266 			p ~= param.type;
267 		}
268 		else
269 		{
270 			if (param.isConst)
271 				p ~= "const(";
272 		
273 			p ~= param.type;
274 
275 			if (param.isConst)
276 				p ~= ')';
277 		}
278 
279 		if (param.name.any)
280 			p ~= " " ~ translateIdentifier(param.name);
281 		
282 		params ~= p;
283 	}
284 
285 	if (variadic)
286 		params ~= "...";
287 
288 	context ~= params.join(", ");
289 	context ~= ')';
290 
291 	return context.data;
292 }
293 
294 string translateIdentifier (string str)
295 {
296 	return isDKeyword(str) ? str ~ '_' : str;
297 }
298 
299 string getInclude (Type type)
300 in
301 {
302 	assert(type.isValid);
303 }
304 body
305 {
306 	return type.declaration.location.spelling.file.name;
307 }
308 
309 void handleInclude (Type type)
310 {
311 	includeHandler.addInclude(getInclude(type));
312 }
313 
314 bool isDKeyword (string str)
315 {
316 	switch (str)
317 	{
318 		case "abstract":
319 		case "alias":
320 		case "align":
321 		case "asm":
322 		case "assert":
323 		case "auto":
324 
325 		case "body":
326 		case "bool":
327 		case "break":
328 		case "byte":
329 
330 		case "case":
331 		case "cast":
332 		case "catch":
333 		case "cdouble":
334 		case "cent":
335 		case "cfloat":
336 		case "char":
337 		case "class":
338 		case "const":
339 		case "continue":
340 		case "creal":
341 
342 		case "dchar":
343 		case "debug":
344 		case "default":
345 		case "delegate":
346 		case "delete":
347 		case "deprecated":
348 		case "do":
349 		case "double":
350 
351 		case "else":
352 		case "enum":
353 		case "export":
354 		case "extern":
355 
356 		case "false":
357 		case "final":
358 		case "finally":
359 		case "float":
360 		case "for":
361 		case "foreach":
362 		case "foreach_reverse":
363 		case "function":
364 
365 		case "goto":
366 
367 		case "idouble":
368 		case "if":
369 		case "ifloat":
370 		case "import":
371 		case "in":
372 		case "inout":
373 		case "int":
374 		case "interface":
375 		case "invariant":
376 		case "ireal":
377 		case "is":
378 
379 		case "lazy":
380 		case "long":
381 
382 		case "macro":
383 		case "mixin":
384 		case "module":
385 
386 		case "new":
387 		case "nothrow":
388 		case "null":
389 
390 		case "out":
391 		case "override":
392 
393 		case "package":
394 		case "pragma":
395 		case "private":
396 		case "protected":
397 		case "public":
398 		case "pure":
399 
400 		case "real":
401 		case "ref":
402 		case "return":
403 
404 		case "scope":
405 		case "shared":
406 		case "short":
407 		case "static":
408 		case "struct":
409 		case "super":
410 		case "switch":
411 		case "synchronized":
412 
413 		case "template":
414 		case "this":
415 		case "throw":
416 		case "true":
417 		case "try":
418 		case "typedef":
419 		case "typeid":
420 		case "typeof":
421 
422 		case "ubyte":
423 		case "ucent":
424 		case "uint":
425 		case "ulong":
426 		case "union":
427 		case "unittest":
428 		case "ushort":
429 
430 		case "version":
431 		case "void":
432 		case "volatile":
433 
434 		case "wchar":
435 		case "while":
436 		case "with":
437 
438 		case "__FILE__":
439 		case "__LINE__":
440 		case "__DATE__":
441 		case "__TIME__":
442 		case "__TIMESTAMP__":
443 		case "__VENDOR__":
444 		case "__VERSION__":
445 			return true;
446 
447 		default: break;
448 	}
449 
450 	if (true /*D2*/)
451 	{
452 		switch (str)
453 		{
454 			case "immutable":
455 			case "nothrow":
456 			case "pure":
457 			case "shared":
458 
459 			case "__gshared":
460 			case "__thread":
461 			case "__traits":
462 
463 			case "__EOF__":
464 				return true;
465 
466 			default: return str.any && str.first == '@';
467 		}
468 	}
469 	
470 	return false;
471 }
472 
473 enum Language
474 {
475 	c,
476 	objC
477 // Can't handle C++ yet
478 //	cpp
479 }