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