1 // This file is part of Visual D 2 // 3 // Visual D integrates the D programming language into Visual Studio 4 // Copyright (c) 2010-2011 by Rainer Schuetze, All Rights Reserved 5 // 6 // Distributed under the Boost Software License, Version 1.0. 7 // See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt 8 9 module vdc.ast.node; 10 11 import vdc.util; 12 import vdc.semantic; 13 import vdc.lexer; 14 import vdc.ast.expr; 15 import vdc.ast.type; 16 import vdc.ast.mod; 17 import vdc.ast.tmpl; 18 import vdc.ast.decl; 19 import vdc.ast.misc; 20 import vdc.ast.writer; 21 import vdc.logger; 22 import vdc.interpret; 23 24 import std.exception; 25 import std.stdio; 26 import std.string; 27 import std.conv; 28 import std.algorithm; 29 30 import stdext.util; 31 32 //version = COUNT; 33 //version = NODE_ALLOC; 34 35 version(COUNT) import visuald.windows; 36 37 version(NODE_ALLOC) 38 class NodeAllocData 39 { 40 enum kSize = 0x4000; 41 byte* pos; 42 43 private byte** data; 44 private int numdata; 45 46 byte* base() { return data[numdata-1]; } 47 48 void moreData() 49 { 50 byte* arr = cast(byte*) gc_calloc(kSize, 0); 51 // when appending to the array, ensure that no old reference is dangling 52 byte** ndata = cast(byte**) gc_malloc((numdata + 1) * data[0].sizeof, 0); 53 ndata[0..numdata] = data[0..numdata]; 54 data[0..numdata] = null; 55 ndata[numdata] = arr; 56 gc_free(data); 57 data = ndata; 58 numdata++; 59 pos = arr; 60 } 61 62 ~this() 63 { 64 destroy(false); // must not call back into GC 65 } 66 67 void destroy(bool free) 68 { 69 while(numdata > 0) 70 { 71 size_t sz; 72 byte* beg = data[--numdata]; 73 byte* end = beg + kSize; 74 for(byte* p = beg; p < end && *cast(size_t*)p != 0; p += sz) 75 { 76 Node n = cast(Node) p; 77 sz = typeid(n).initializer.length; 78 sz = (sz + 15) & ~15; 79 assert(sz > 0); 80 clear(n); // calls rt_finalize 81 } 82 if(free) 83 gc_free(beg); 84 data[numdata] = null; 85 } 86 if(data && free) 87 gc_free(data); 88 data = null; 89 pos = null; 90 } 91 92 static NodeAllocData current; 93 94 static NodeAllocData detachCurrent() 95 { 96 auto cur = current; 97 current = null; 98 return cur; 99 } 100 101 static void checkAlloc(size_t sz) 102 { 103 if(!current) 104 current = new NodeAllocData; 105 if(!current.pos) 106 current.moreData(); 107 if(current.pos + sz > current.base() + kSize) 108 current.moreData(); 109 } 110 111 static void* alloc(size_t sz) 112 { 113 sz = (sz + 15) & ~15; 114 checkAlloc(sz); 115 void* p = current.pos; 116 current.pos += sz; 117 //if(current.pos < current.base() + kSize) 118 // *cast(size_t*)current.pos = 0; 119 return p; 120 } 121 } 122 123 // moved out of Node due to regression http://d.puremagic.com/issues/show_bug.cgi?id=9101 124 mixin template ForwardCtor() 125 { 126 this() 127 { 128 // default constructor needed for clone() 129 } 130 this(ref const(TextSpan) _span) 131 { 132 super(_span); 133 } 134 this(Token tok) 135 { 136 super(tok); 137 } 138 this(TokenId _id, ref const(TextSpan) _span) 139 { 140 super(_id, _span); 141 } 142 } 143 144 mixin template ForwardCtorTok() 145 { 146 this() {} // default constructor needed for clone() 147 148 this(Token tok) 149 { 150 super(tok); 151 } 152 } 153 154 mixin template ForwardCtorNoId() 155 { 156 this() {} // default constructor needed for clone() 157 158 this(ref const(TextSpan) _span) 159 { 160 super(_span); 161 } 162 this(Token tok) 163 { 164 super(tok.span); 165 } 166 } 167 168 class Node 169 { 170 TokenId id; 171 Attribute attr; 172 Annotation annotation; 173 TextSpan span; // file extracted from parent module 174 TextSpan fulspan; 175 176 Node parent; 177 Node[] members; 178 179 // semantic data 180 int semanticSearches; 181 Scope scop; 182 183 version(COUNT) static __gshared int countNodes; 184 185 this() 186 { 187 version(COUNT) InterlockedIncrement(&countNodes); 188 // default constructor needed for clone() 189 } 190 191 this(ref const(TextSpan) _span) 192 { 193 version(COUNT) InterlockedIncrement(&countNodes); 194 fulspan = span = _span; 195 } 196 this(Token tok) 197 { 198 version(COUNT) InterlockedIncrement(&countNodes); 199 id = tok.id; 200 span = tok.span; 201 fulspan = tok.span; 202 } 203 this(TokenId _id, ref const(TextSpan) _span) 204 { 205 version(COUNT) InterlockedIncrement(&countNodes); 206 id = _id; 207 fulspan = span = _span; 208 } 209 210 version(COUNT) ~this() 211 { 212 version(COUNT) InterlockedDecrement(&countNodes); 213 } 214 215 void reinit() 216 { 217 id = 0; 218 attr = 0; 219 annotation = 0; 220 members.length = 0; 221 clearSpan(); 222 } 223 224 final Node _cloneShallow() 225 { 226 Node n = static_cast!Node(typeid(this).create()); 227 228 n.id = id; 229 n.attr = attr; 230 n.annotation = annotation; 231 n.span = span; 232 n.fulspan = fulspan; 233 234 return n; 235 } 236 237 Node clone() 238 { 239 Node n = _cloneShallow(); 240 foreach(m; members) 241 n.addMember(m.clone()); 242 return n; 243 } 244 245 bool compare(const(Node) n) const 246 { 247 if (typeid(this) !is typeid(n)) 248 return false; 249 250 if(n.id != id || n.attr != attr || n.annotation != annotation) 251 return false; 252 // ignore span 253 254 if(members.length != n.members.length) 255 return false; 256 257 for(int m = 0; m < members.length; m++) 258 if(!members[m].compare(n.members[m])) 259 return false; 260 261 return true; 262 } 263 264 //////////////////////////////////////////////////////////// 265 Node visit(DG)(DG dg) 266 { 267 if(!dg(this)) 268 return this; 269 for(int m = 0; m < members.length; m++) 270 if(auto n = members[m].visit(dg)) 271 return n; 272 return null; 273 } 274 275 bool detachFromModule(Module mod) 276 { 277 return true; 278 } 279 280 void disconnect() 281 { 282 for(int m = 0; m < members.length; m++) 283 members[m].disconnect(); 284 285 for(int m = 0; m < members.length; m++) 286 members[m].parent = null; 287 members = members.init; 288 } 289 290 void free() 291 { 292 for(int m = 0; m < members.length; m++) 293 members[m].free(); 294 295 for(int m = 0; m < members.length; m++) 296 members[m].parent = null; 297 298 import core.memory; 299 300 for(int m = 0; m < members.length; m++) 301 GC.free(cast(void*) (members[m])); 302 303 GC.free(cast(void*) (members.ptr)); 304 members = members.init; 305 } 306 307 //////////////////////////////////////////////////////////// 308 abstract void toD(CodeWriter writer) 309 { 310 writer(typeid(this).name); 311 writer.nl(); 312 313 auto indent = CodeIndenter(writer); 314 foreach(c; members) 315 writer(c); 316 } 317 318 void toC(CodeWriter writer) 319 { 320 toD(writer); 321 } 322 323 //////////////////////////////////////////////////////////// 324 static string genCheckState(string state) 325 { 326 return " 327 if(" ~ state ~ "!= 0) 328 return; 329 " ~ state ~ " = 1; 330 scope(exit) " ~ state ~ " = 2; 331 "; 332 } 333 334 enum SemanticState 335 { 336 None, 337 ExpandingNonScopeMembers, 338 ExpandedNonScopeMembers, 339 AddingSymbols, 340 AddedSymbols, 341 ResolvingSymbols, 342 ResolvedSymbols, 343 SemanticDone, 344 } 345 int semanticState; 346 347 void expandNonScopeSimple(Scope sc, size_t i, size_t j) 348 { 349 Node[1] narray; 350 for(size_t m = i; m < j; ) 351 { 352 Node n = members[m]; 353 narray[0] = n; 354 size_t mlen = members.length; 355 Node[] nm = n.expandNonScopeBlock(sc, narray); 356 assert(members.length == mlen); 357 if(nm.length == 1 && nm[0] == n) 358 { 359 n.addSymbols(sc); 360 assert(members.length == mlen); 361 m++; 362 } 363 else 364 { 365 replaceMember(m, nm); 366 assert(members.length == mlen + nm.length - 1); 367 j += nm.length - 1; 368 } 369 } 370 } 371 372 void expandNonScopeBlocks(Scope sc) 373 { 374 if(semanticState >= SemanticState.ExpandingNonScopeMembers) 375 return; 376 377 // simple expansions 378 semanticState = SemanticState.ExpandingNonScopeMembers; 379 expandNonScopeSimple(sc, 0, members.length); 380 381 // expansions with interpretation 382 Node[1] narray; 383 for(int m = 0; m < members.length; ) 384 { 385 Node n = members[m]; 386 narray[0] = n; 387 Node[] nm = n.expandNonScopeInterpret(sc, narray); 388 if(nm.length == 1 && nm[0] == n) 389 m++; 390 else 391 { 392 replaceMember(m, nm); 393 expandNonScopeSimple(sc, m, m + nm.length); 394 } 395 } 396 semanticState = SemanticState.ExpandedNonScopeMembers; 397 } 398 399 Node[] expandNonScopeBlock(Scope sc, Node[] athis) 400 { 401 return athis; 402 } 403 404 Node[] expandNonScopeInterpret(Scope sc, Node[] athis) 405 { 406 return athis; 407 } 408 409 void addMemberSymbols(Scope sc) 410 { 411 if(semanticState >= SemanticState.AddingSymbols) 412 return; 413 414 scop = sc; 415 expandNonScopeBlocks(scop); 416 417 semanticState = SemanticState.AddedSymbols; 418 } 419 420 void addSymbols(Scope sc) 421 { 422 } 423 424 bool createsScope() const { return false; } 425 426 Scope enterScope(ref Scope nscope, Scope sc) 427 { 428 if(!nscope) 429 { 430 nscope = sc.pushClone(); 431 nscope.node = this; 432 addMemberSymbols(nscope); 433 return nscope; 434 } 435 return sc.push(nscope); 436 } 437 Scope enterScope(Scope sc) 438 { 439 return enterScope(scop, sc); 440 } 441 442 final void semantic(Scope sc) 443 { 444 assert(sc); 445 446 if(semanticState < SemanticState.SemanticDone) 447 { 448 logInfo("Scope(%s):semantic(%s=%s)", cast(void*)sc, this, cast(void*)this); 449 LogIndent indent = LogIndent(1); 450 451 _semantic(sc); 452 semanticState = SemanticState.SemanticDone; 453 } 454 } 455 456 void _semantic(Scope sc) 457 { 458 // throw new SemanticException(text(this, ".semantic not implemented")); 459 foreach(m; members) 460 m.semantic(sc); 461 } 462 463 Scope getScope() 464 { 465 if(scop) 466 return scop; 467 if(parent) 468 { 469 Scope sc = parent.getScope(); 470 assert(sc); 471 if(sc && createsScope()) 472 sc = enterScope(sc); 473 return sc; 474 } 475 return null; 476 } 477 478 Node resolve() 479 { 480 return null; 481 } 482 483 Type calcType() 484 { 485 return semanticErrorType(this, ".calcType not implemented"); 486 } 487 488 Value interpret(Context sc) 489 { 490 return semanticErrorValue(this, ".interpret not implemented"); 491 } 492 493 Value interpretCatch(Context sc) 494 { 495 try 496 { 497 return interpret(sc); 498 } 499 catch(InterpretException) 500 { 501 } 502 return semanticErrorValue(this, ": interpretation stopped"); 503 } 504 505 ParameterList getParameterList() 506 { 507 return null; 508 } 509 ArgumentList getFunctionArguments() 510 { 511 return null; 512 } 513 514 bool isTemplate() 515 { 516 return false; 517 } 518 Node expandTemplate(Scope sc, TemplateArgumentList args) 519 { 520 return this; 521 } 522 523 //////////////////////////////////////////////////////////// 524 version(COUNT) {} else // invariant does not work with destructor 525 invariant() 526 { 527 if(!__ctfe) 528 foreach(m; members) 529 assert(m.parent is this); 530 } 531 532 void addMember(Node m) 533 { 534 assert(m.parent is null); 535 members ~= m; 536 m.parent = this; 537 extendSpan(m.fulspan); 538 } 539 540 Node removeMember(Node m) 541 { 542 auto n = std.algorithm.countUntil(members, m); 543 assert(n >= 0); 544 return removeMember(n); 545 } 546 547 Node removeMember(size_t m) 548 { 549 Node n = members[m]; 550 removeMember(m, 1); 551 return n; 552 } 553 554 void removeMember(size_t m, size_t cnt) 555 { 556 assert(m >= 0 && m + cnt <= members.length); 557 for (size_t i = 0; i < cnt; i++) 558 members[m + i].parent = null; 559 560 for (size_t n = m + cnt; n < members.length; n++) 561 members[n - cnt] = members[n]; 562 members.length = members.length - cnt; 563 } 564 565 Node[] removeAll() 566 { 567 for (size_t m = 0; m < members.length; m++) 568 members[m].parent = null; 569 Node[] nm = members; 570 members = members.init; 571 return nm; 572 } 573 574 void replaceMember(Node m, Node[] nm) 575 { 576 auto n = std.algorithm.countUntil(members, m); 577 assert(n >= 0); 578 replaceMember(n, nm); 579 } 580 581 void replaceMember(size_t m, Node[] nm) 582 { 583 if(m < members.length) 584 members[m].parent = null; 585 if(nm.length == 1 && m < members.length) 586 members[m] = nm[0]; 587 else 588 members = members[0..m] ~ nm ~ members[m+1..$]; 589 foreach(n; nm) 590 n.parent = this; 591 } 592 593 T getMember(T = Node)(size_t idx) 594 { 595 if (idx < 0 || idx >= members.length) 596 return null; 597 return static_cast!T(members[idx]); 598 } 599 600 Module getModule() 601 { 602 Node n = this; 603 while(n) 604 { 605 if(n.scop) 606 return n.scop.mod; 607 n = n.parent; 608 } 609 return null; 610 } 611 string getModuleFilename() 612 { 613 Module mod = getModule(); 614 if(!mod) 615 return null; 616 return mod.filename; 617 } 618 619 void semanticError(T...)(T args) 620 { 621 semanticErrorLoc(getModuleFilename(), span.start, args); 622 } 623 624 ErrorValue semanticErrorValue(T...)(T args) 625 { 626 semanticErrorLoc(getModuleFilename(), span.start, args); 627 return Singleton!(ErrorValue).get(); 628 } 629 630 ErrorType semanticErrorType(T...)(T args) 631 { 632 semanticErrorLoc(getModuleFilename(), span.start, args); 633 return Singleton!(ErrorType).get(); 634 } 635 636 //////////////////////////////////////////////////////////// 637 void extendSpan(ref const(TextSpan) _span) 638 { 639 if(_span.start < fulspan.start) 640 fulspan.start = _span.start; 641 if(_span.end > fulspan.end) 642 fulspan.end = _span.end; 643 } 644 void limitSpan(ref const(TextSpan) _span) 645 { 646 if(_span.start > fulspan.start) 647 fulspan.start = _span.start; 648 if(_span.end < fulspan.end) 649 fulspan.end = _span.end; 650 } 651 void clearSpan() 652 { 653 span.end.line = span.start.line; 654 span.end.index = span.start.index; 655 fulspan = span; 656 } 657 } 658 659 class ParseRecoverNode : Node 660 { 661 mixin ForwardCtor!(); 662 663 override void toD(CodeWriter writer) 664 { 665 string start = to!string(fulspan.start.line) ~ "," ~ to!string(fulspan.start.index); 666 string end = to!string(fulspan.end.line) ~ "," ~ to!string(fulspan.end.index); 667 writer("/+ syntax error: span = ", start, " - ", end, " +/"); 668 writer.nl(); 669 } 670 671 override void _semantic(Scope sc) 672 { 673 } 674 } 675 676 interface CallableNode 677 { 678 Value interpretCall(Context sc); 679 680 ParameterList getParameterList(); 681 FunctionBody getFunctionBody(); 682 } 683 684 TextPos minimumTextPos(Node node) 685 { 686 version(all) 687 return node.fulspan.start; 688 else 689 { 690 TextPos start = node.span.start; 691 while(node.members.length > 0) 692 { 693 if(compareTextSpanAddress(node.members[0].span.start.line, node.members[0].span.start.index, 694 start.line, start.index) < 0) 695 start = node.members[0].span.start; 696 node = node.members[0]; 697 } 698 return start; 699 } 700 } 701 702 TextPos maximumTextPos(Node node) 703 { 704 version(all) 705 return node.fulspan.end; 706 else 707 { 708 TextPos end = node.span.end; 709 while(node.members.length > 0) 710 { 711 if(compareTextSpanAddress(node.members[$-1].span.end.line, node.members[$-1].span.start.index, 712 end.line, end.index) > 0) 713 end = node.members[$-1].span.end; 714 node = node.members[$-1]; 715 } 716 return end; 717 } 718 } 719 720 // prefer start 721 bool nodeContains(Node node, in TextPos pos) 722 { 723 TextPos start = minimumTextPos(node); 724 if(start > pos) 725 return false; 726 TextPos end = maximumTextPos(node); 727 if(end <= pos) 728 return false; 729 return true; 730 } 731 732 bool nodeContains(Node node, in TextSpan* span) 733 { 734 TextPos start = minimumTextPos(node); 735 if(start > span.start) 736 return false; 737 TextPos end = maximumTextPos(node); 738 if(end < span.end) 739 return false; 740 return true; 741 } 742 743 // prefer end 744 bool nodeContainsEnd(Node node, in TextPos* pos) 745 { 746 TextPos start = minimumTextPos(node); 747 if(start >= *pos) 748 return false; 749 TextPos end = maximumTextPos(node); 750 if(end < *pos) 751 return false; 752 return true; 753 } 754 755 // figure out whether the given range is between the children of a binary expression 756 bool isBinaryOperator(Node root, int startLine, int startIndex, int endLine, int endIndex) 757 { 758 TextPos pos = TextPos(startIndex, startLine); 759 if(!nodeContains(root, pos)) 760 return false; 761 762 L_loop: 763 if(root.members.length == 2) 764 { 765 if(cast(BinaryExpression) root) 766 if(maximumTextPos(root.members[0]) <= pos && minimumTextPos(root.members[1]) > pos) 767 return true; 768 } 769 770 foreach(m; root.members) 771 if(nodeContains(m, pos)) 772 { 773 root = m; 774 goto L_loop; 775 } 776 777 return false; 778 } 779 780 Node getTextPosNode(Node root, in TextSpan* span, bool *inDotExpr) 781 { 782 if(!nodeContains(root, span)) 783 return null; 784 785 L_loop: 786 foreach(m; root.members) 787 if(nodeContains(m, span)) 788 { 789 root = m; 790 goto L_loop; 791 } 792 793 if(inDotExpr) 794 *inDotExpr = false; 795 796 if(auto dotexpr = cast(DotExpression)root) 797 { 798 if(inDotExpr) 799 { 800 root = dotexpr.getExpression(); 801 *inDotExpr = true; 802 } 803 } 804 else if(auto id = cast(Identifier)root) 805 { 806 if(auto dotexpr = cast(DotExpression)id.parent) 807 { 808 if(dotexpr.getIdentifier() == id) 809 { 810 if(inDotExpr) 811 { 812 root = dotexpr.getExpression(); 813 *inDotExpr = true; 814 } 815 else 816 root = dotexpr; 817 } 818 } 819 } 820 return root; 821 }