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 }