1 /**
2  * Copyright: Copyright (c) 2016 Wojciech Szęszoł. All rights reserved.
3  * Authors: Wojciech Szęszoł
4  * Version: Initial created: Jun 03, 2016
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.MacroDefinition;
8 
9 import std.array : Appender;
10 import std.traits;
11 import std.meta;
12 import std.variant;
13 
14 import clang.c.Index;
15 import clang.Cursor;
16 import clang.Util;
17 import clang.SourceLocation;
18 import clang.SourceRange;
19 import clang.Token;
20 import clang.Type;
21 
22 import dstep.translator.Context;
23 import dstep.translator.MacroParser;
24 import dstep.translator.Output;
25 import dstep.translator.Type;
26 import dstep.translator.TypeInference;
27 
28 void dumpAST(Expression expression, ref Appender!string result, size_t indent)
29 {
30     import std.format;
31     import std.array : replicate;
32 
33     result.put(" ".replicate(indent));
34     result.put(expression.toString());
35 }
36 
37 Expression debraced(Expression expression)
38 {
39     if (!expression.hasValue)
40         return expression;
41 
42     auto subExpr = expression.peek!SubExpr();
43 
44     return subExpr !is null ? subExpr.subexpr : expression;
45 }
46 
47 Expression braced(Expression expression)
48 {
49     if (!expression.hasValue)
50         return expression;
51 
52     if (auto subExpr = expression.peek!Identifier())
53         return expression;
54     else if (auto subExpr = expression.peek!Literal())
55         return expression;
56     else if (auto subExpr = expression.peek!SubExpr())
57         return expression;
58     else
59         return Expression(new SubExpr(expression));
60 }
61 
62 void resolveMacroDependency(Context context, string spelling)
63 {
64     auto cursor = spelling in context.macroIndex.globalCursors();
65 
66     if (cursor !is null)
67         return context.includeHandler.resolveDependency(*cursor);
68 }
69 
70 struct ExpressionContext
71 {
72     Context context;
73     Set!string params;
74     Set!string* imports;
75     Cursor scope_;
76     alias context this;
77 
78     @disable this();
79 
80     private this (int x) { }
81 
82     static ExpressionContext make()
83     {
84         struct AA { Set!string aa; }
85         auto result = ExpressionContext(0);
86         result.imports = &(new AA()).aa;
87         return result;
88     }
89 
90     static ExpressionContext make(Context context)
91     {
92         auto result = ExpressionContext.make();
93         result.context = context;
94         return result;
95     }
96 
97     static ExpressionContext make(Context context, Set!string params)
98     {
99         auto result = ExpressionContext.make(context);
100         result.params = params;
101         return result;
102     }
103 }
104 
105 string translate(Identifier identifier, ExpressionContext context)
106 {
107     context.resolveMacroDependency(identifier.spelling);
108 
109     auto constant = identifier.spelling in context.constNames();
110 
111     if (constant !is null)
112     {
113         if (constant.kind == CXCursorKind.enumConstantDecl)
114         {
115             auto typedefParent = context.typedefParent(constant.lexicalParent);
116             auto lexicalParent = constant.lexicalParent;
117 
118             auto scopeName = typedefParent
119                 ? typedefParent.spelling
120                 : lexicalParent.spelling;
121 
122             auto scopePrefix = lexicalParent != context.scope_
123                 ? scopeName ~ "."
124                 : "";
125 
126             return scopePrefix ~ context.translateSpelling(*constant);
127         }
128     }
129 
130     return identifier.spelling;
131 }
132 
133 string translate(CallExpr expression, ExpressionContext context)
134 {
135     import std.algorithm;
136     import std.format;
137     import std.range;
138     import std..string;
139 
140     auto spelling = expression.expr.spelling;
141 
142     alias fmap = a => a.debraced.translate(context);
143 
144     if (spelling !is null)
145     {
146         context.resolveMacroDependency(spelling);
147 
148         auto definition = spelling in context.translator.typedMacroDefinitions;
149 
150         if (definition !is null)
151         {
152             string[] typeArguments, valueArguments;
153 
154             auto arguments = zip(
155                 definition.signature.params,
156                 expression.args.map!fmap);
157 
158             foreach (type, argument; arguments) {
159                 if (auto defined = type.peek!Meta)
160                     typeArguments ~= argument;
161                 else
162                     valueArguments ~= argument;
163             }
164 
165             string typesList = typeArguments.length <= 1
166                 ? typeArguments.join(", ")
167                 : "(" ~ typeArguments.join(", ") ~ ")";
168 
169             string argumentList = "(" ~ valueArguments.join(", ") ~ ")";
170 
171             string exclamation = typesList.empty ? "" : "!";
172 
173             return spelling ~ exclamation ~ typesList ~ argumentList;
174         }
175     }
176 
177     return format(
178         "%s(%s)",
179         expression.expr.translate(context),
180         expression.args.map!fmap.join(", "));
181 }
182 
183 string translate(Expression expression, ExpressionContext context)
184 {
185     import std.format : format;
186 
187     if (!expression.hasValue)
188         return null;
189 
190     string translateBinaryOperator(T)(T operator)
191     {
192         return format(
193             "%s %s %s",
194             operator.left.translate(context),
195             operator.operator,
196             operator.right.translate(context));
197     }
198 
199     return expression.visit!(
200         delegate string(Identifier identifier)
201         {
202             return identifier.translate(context);
203         },
204         delegate string(TypeIdentifier identifier)
205         {
206             auto spelling = translateType(context, Cursor.init, identifier.type)
207                 .makeString();
208             context.resolveMacroDependency(spelling);
209             return spelling;
210         },
211         delegate string(Literal literal)
212         {
213             return literal.spelling;
214         },
215         delegate string(StringLiteral stringLiteral)
216         {
217             return stringLiteral.spelling;
218         },
219         delegate string(StringifyExpr stringifyExpr)
220         {
221             (*context.imports).add("std.conv : to");
222             return format("to!string(%s)", stringifyExpr.spelling);
223         },
224         delegate string(StringConcat stringConcat)
225         {
226             import std.algorithm.iteration : map;
227             import std.array : join;
228 
229             return stringConcat.substrings
230                 .map!(a => a.translate(context)).join(" ~ ");
231         },
232         delegate string(TokenConcat tokenConcat)
233         {
234             import std.algorithm.iteration : map;
235             import std.array : join;
236 
237             string stringify(Expression subexpr)
238             {
239                 auto identifier = subexpr.peek!Identifier;
240 
241                 if (identifier !is null &&
242                     context.params.contains(identifier.spelling))
243                 {
244                     import std.format : format;
245 
246                     (*context.imports).add("std.conv : to");
247 
248                     return format("to!string(%s)", identifier.spelling);
249                 }
250                 else
251                 {
252                     auto translated = subexpr.translate(context);
253 
254                     return format(`"%s"`, translated);
255                 }
256             }
257 
258             return tokenConcat.subexprs.map!stringify.join(" ~ ");
259         },
260         delegate string(IndexExpr indexExpr)
261         {
262             return format(
263                 "%s[%s]",
264                 indexExpr.subexpr.translate(context),
265                 indexExpr.index.translate(context));
266         },
267         delegate string(CallExpr callExpr)
268         {
269             return callExpr.translate(context);
270         },
271         delegate string(DotExpr dotExpr)
272         {
273             return format(
274                 "%s.%s",
275                 dotExpr.subexpr.translate(context),
276                 dotExpr.identifier);
277         },
278         delegate string(ArrowExpr arrowExpr)
279         {
280             return format(
281                 "%s.%s",
282                 arrowExpr.subexpr.translate(context),
283                 arrowExpr.identifier);
284         },
285         delegate string(SubExpr subExpr)
286         {
287             auto surplus = subExpr.subexpr.peek!Identifier !is null
288                 || subExpr.subexpr.peek!DotExpr !is null;
289 
290             string translated = subExpr.subexpr.translate(context);
291 
292             return surplus ? translated : "(" ~ translated ~ ")";
293         },
294         delegate string(UnaryExpr unaryExpr)
295         {
296             if (unaryExpr.operator == "sizeof")
297                 return format(
298                     "%s.sizeof",
299                     unaryExpr.subexpr.braced.translate(context));
300             else if (unaryExpr.postfix)
301                 return format(
302                     "%s%s",
303                     unaryExpr.subexpr.translate(context),
304                     unaryExpr.operator);
305             else
306                 return format(
307                     "%s%s",
308                     unaryExpr.operator,
309                     unaryExpr.subexpr.translate(context));
310         },
311         delegate string(DefinedExpr literal)
312         {
313             return "";
314         },
315         delegate string(SizeofType sizeofType)
316         {
317             return format(
318                 "%s.sizeof",
319                 translateType(context, Cursor.init, sizeofType.type).makeString());
320         },
321         delegate string(CastExpr castExpr)
322         {
323             return format(
324                 "cast(%s) %s",
325                 translateType(context, Cursor.init, castExpr.type).makeString(),
326                 castExpr.subexpr.debraced.translate(context));
327         },
328         translateBinaryOperator!MulExpr,
329         translateBinaryOperator!AddExpr,
330         translateBinaryOperator!SftExpr,
331         translateBinaryOperator!RelExpr,
332         translateBinaryOperator!EqlExpr,
333         translateBinaryOperator!AndExpr,
334         translateBinaryOperator!XorExpr,
335         translateBinaryOperator!OrExpr,
336         translateBinaryOperator!LogicalAndExpr,
337         translateBinaryOperator!LogicalOrExpr,
338         delegate string(CondExpr condExpr)
339         {
340             return format(
341                 "%s ? %s : %s",
342                 condExpr.expr.translate(context),
343                 condExpr.left.translate(context),
344                 condExpr.right.translate(context));
345         });
346 }
347 
348 void guessParamTypes(Expression expression, ref ExprType[string] params, ExprType type)
349 {
350     import std.format : format;
351 
352     if (!expression.hasValue)
353         return;
354 
355     void pass(T)(T operator) { }
356 
357     void guessBinaryOperator(T)(T operator)
358     {
359         operator.left.guessParamTypes(params, UnspecifiedExprType);
360         operator.right.guessParamTypes(params, UnspecifiedExprType);
361     }
362 
363     return expression.visit!(
364         delegate void(Identifier identifier)
365         {
366             auto param = identifier.spelling in params;
367 
368             if (param !is null && param.isUnspecified)
369                 *param = type;
370         },
371         delegate void(SubExpr subExpr)
372         {
373             subExpr.subexpr.guessParamTypes(params, type);
374         },
375         delegate void(UnaryExpr unaryExpr)
376         {
377             if (unaryExpr.operator == "sizeof")
378                 unaryExpr.subexpr.guessParamTypes(params, UnspecifiedExprType);
379             else if (unaryExpr.operator == "++" || unaryExpr.operator == "--")
380                 unaryExpr.subexpr.guessParamTypes(params, type.asLValue);
381             else
382                 unaryExpr.subexpr.guessParamTypes(params, type);
383         },
384         delegate void(CastExpr castExpr)
385         {
386             castExpr.subexpr.guessParamTypes(params, UnspecifiedExprType);
387         },
388         guessBinaryOperator!MulExpr,
389         guessBinaryOperator!AddExpr,
390         guessBinaryOperator!SftExpr,
391         guessBinaryOperator!RelExpr,
392         guessBinaryOperator!EqlExpr,
393         guessBinaryOperator!AndExpr,
394         guessBinaryOperator!XorExpr,
395         guessBinaryOperator!OrExpr,
396         guessBinaryOperator!LogicalAndExpr,
397         guessBinaryOperator!LogicalOrExpr,
398         delegate void(CondExpr condExpr)
399         {
400             condExpr.expr.guessParamTypes(params, type);
401             condExpr.left.guessParamTypes(params, type);
402             condExpr.right.guessParamTypes(params, type);
403         },
404         pass);
405 }
406 
407 struct ExprType
408 {
409     enum Kind
410     {
411         unspecified,
412         specified,
413         generic,
414     }
415 
416     bool lvalue = false;
417     Kind kind;
418     string spelling;
419 
420     this (Kind kind)
421     {
422         this.kind = kind;
423     }
424 
425     this (string spelling)
426     {
427         this.kind = Kind.specified;
428         this.spelling = spelling;
429     }
430 
431     bool isUnspecified()
432     {
433         return kind == Kind.unspecified;
434     }
435 
436     bool isSpecified()
437     {
438         return kind == Kind.specified;
439     }
440 
441     bool isGeneric()
442     {
443         return kind == Kind.generic;
444     }
445 
446     bool isLValue()
447     {
448         return lvalue;
449     }
450 
451     ExprType asRValue()
452     {
453         ExprType clone = this;
454         clone.lvalue = false;
455         return clone;
456     }
457 
458     ExprType asLValue()
459     {
460         ExprType clone = this;
461         clone.lvalue = true;
462         return clone;
463     }
464 
465     ExprType decayed()
466     {
467         ExprType clone = this;
468         clone.lvalue = false;
469         clone.kind = clone.isGeneric ? Kind.unspecified : clone.kind;
470         return clone;
471     }
472 }
473 
474 immutable UnspecifiedExprType = ExprType(ExprType.Kind.unspecified);
475 
476 string asParamType(ExprType type)
477 {
478     if (type.isSpecified)
479         return type.isLValue ? "auto ref " ~ type.spelling : type.spelling;
480     else
481         return "auto ref T" ~ type.spelling;
482 }
483 
484 string asPlainType(ExprType type)
485 {
486     if (type.isSpecified)
487         return type.spelling;
488     else
489         return "T" ~ type.spelling;
490 }
491 
492 string asReturnType(ExprType type)
493 {
494     final switch (type.kind)
495     {
496         case ExprType.Kind.unspecified: return "auto";
497         case ExprType.Kind.specified: return type.spelling;
498         case ExprType.Kind.generic: return "auto";
499     }
500 }
501 
502 ExprType strictCommonType(ExprType a, ExprType b)
503 {
504     if (a == b)
505         return a;
506     else
507         return ExprType(ExprType.kind.unspecified);
508 }
509 
510 bool translateFunctAlias(
511     Output output,
512     Context context,
513     MacroDefinition definition)
514 {
515     import std.algorithm.comparison : equal;
516     import std.algorithm.iteration : map;
517 
518     CallExpr* expr = definition.expr.peek!CallExpr;
519     auto expressionContext = ExpressionContext.make(context);
520 
521     if (expr !is null)
522     {
523         Identifier* ident = expr.expr.peek!Identifier;
524 
525         if (ident.spelling == "assert")
526         {
527             // aliasing `assert` from C "assert.h" is commong enough to warrant
528             // a special case to use function instead of an alias
529             output.singleLine("void %s(T...)(T args)", definition.spelling);
530             version (D1)
531             {
532                 // does not support template constraints
533             }
534             else
535             {
536                 output.singleLine("    if (T.length <= 2)");
537             }
538             output.singleLine("{");
539             output.singleLine("    assert(args);");
540             output.singleLine("}");
541             return true;
542         }
543 
544         if (ident !is null &&
545             equal(definition.params, expr.args
546                 .map!(a => a.translate(expressionContext))))
547         {
548             version (D1)
549                 enum fmt = "alias %2$s %1$s;";
550             else
551                 enum fmt = "alias %1$s = %2$s;";
552 
553             output.singleLine(fmt, definition.spelling, ident.spelling);
554             return true;
555         }
556     }
557 
558     return false;
559 }
560 
561 string spelling(CallExpr expression)
562 {
563     auto identifier = expression.expr.debraced.peek!Identifier();
564     return identifier is null ? null : identifier.spelling;
565 }
566 
567 string spelling(Expression expression)
568 {
569     auto debraced = expression.debraced();
570 
571     if (!debraced.hasValue)
572         return null;
573 
574     auto identifier = debraced.peek!Identifier();
575 
576     if (identifier is null)
577         return null;
578 
579     return identifier.spelling;
580 }
581 
582 void translateMacroDefinitionConstant(
583     Output output,
584     Context context,
585     TypedMacroDefinition definition)
586 {
587     auto expressionContext = ExpressionContext.make(context);
588 
589     version (D1)
590         enum formatString = "const %s = %s;";
591     else
592         enum formatString = "enum %s = %s;";
593 
594     output.singleLine(
595         formatString,
596         definition.definition.spelling,
597         definition.definition.expr.debraced.translate(expressionContext));
598 }
599 
600 void translateMacroDefinition(
601     Output output,
602     Context context,
603     TypedMacroDefinition definition)
604 {
605     import std.algorithm;
606     import std.conv;
607     import std.range;
608 
609     if (definition.constant)
610     {
611         translateMacroDefinitionConstant(output, context, definition);
612     }
613     else if (!translateFunctAlias(output, context, definition))
614     {
615         string[] typeParams, paramTypes, variables;
616 
617         auto params = zip(
618             definition.signature.params,
619             definition.params,
620             iota(definition.params.length));
621 
622         auto numTypes = definition.signature.params
623             .count!(x => x.peek!Generic !is null);
624 
625         foreach (type, name, index; params) {
626             if (auto defined = type.peek!Defined)
627             {
628                 variables ~= defined.spelling ~ " " ~ name;
629             }
630             else if (type.peek!Generic)
631             {
632                 paramTypes ~= numTypes == 1 ? "T" : "T" ~ index.to!string();
633                 variables ~= "auto ref " ~ paramTypes.back ~ " " ~ name;
634             }
635             else
636             {
637                 typeParams ~= name;
638             }
639         }
640 
641         auto types = typeParams ~ paramTypes;
642 
643         auto expressionContext = ExpressionContext.make(
644             context,
645             setFromList(definition.params));
646 
647         auto translated = definition.expr.debraced.translate(expressionContext);
648 
649         auto resultType = definition.signature.result;
650 
651         output.subscopeStrong(
652             "extern (D) %s %s%s(%s)",
653             resultType.peek!Defined ? resultType.get!Defined.spelling : "auto",
654             definition.spelling,
655             types.empty ? "" : "(" ~ types.join(", ") ~ ")",
656             variables.join(", "))
657         in {
658             if (expressionContext.imports.length != 0)
659             {
660                 foreach (item; expressionContext.imports.byKey)
661                     output.singleLine("import %s;", item);
662 
663                 output.separator;
664             }
665 
666             output.singleLine("return %s;", translated);
667         };
668     }
669 }
670 
671 string translateMacroDefinition(Context context, TypedMacroDefinition definition)
672 {
673     Output output = new Output();
674     translateMacroDefinition(output, context, definition);
675     return output.data;
676 }