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 }