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 }