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.mod;
10 
11 import vdc.util;
12 import vdc.semantic;
13 import vdc.lexer;
14 
15 import vdc.ast.node;
16 import vdc.ast.decl;
17 import vdc.ast.expr;
18 import vdc.ast.misc;
19 import vdc.ast.aggr;
20 import vdc.ast.tmpl;
21 import vdc.ast.writer;
22 
23 import vdc.parser.engine;
24 import vdc.interpret;
25 
26 import stdext.util;
27 
28 import std.conv;
29 import std.path;
30 import std.array;
31 import std.algorithm;
32 
33 ////////////////////////////////////////////////////////////////
34 
35 //Module:
36 //    [ModuleDeclaration_opt DeclDef...]
37 class Module : Node
38 {
39     mixin ForwardCtor!();
40 
41     string filename;
42     bool imported;
43     Options options;
44 
45     override Module clone()
46     {
47         Module n = static_cast!Module(super.clone());
48         n.filename = filename;
49         n.imported = imported;
50         return n;
51     }
52 
53     override bool compare(const(Node) n) const
54     {
55         if(!super.compare(n))
56             return false;
57 
58         auto tn = static_cast!(typeof(this))(n);
59         return tn.filename == filename
60             && tn.imported == imported;
61     }
62 
63 
64     Project getProject() { return static_cast!Project(parent); }
65 
66     override void toD(CodeWriter writer)
67     {
68         foreach(m; members)
69             writer(m);
70     }
71 
72     override void toC(CodeWriter writer)
73     {
74         if(members.length > 0 && cast(ModuleDeclaration) getMember(0))
75         {
76             auto fqn = getMember(0).getMember!ModuleFullyQualifiedName(0);
77 
78             foreach(m; fqn.members)
79             {
80                 writer("namespace ", m, " {");
81                 writer.nl;
82             }
83             writer.nl;
84             foreach(m; members[1..$])
85                 writer(m);
86 
87             writer.nl(false);
88             foreach_reverse(m; fqn.members)
89             {
90                 writer("} // namespace ", m);
91                 writer.nl;
92             }
93         }
94         else
95         {
96             writer("namespace ", baseName(filename), " {");
97             writer.nl;
98             foreach(m; members)
99                 writer(m);
100             writer.nl(false);
101             writer("} // namespace ", baseName(filename));
102             writer.nl;
103         }
104     }
105 
106     void writeNamespace(CodeWriter writer)
107     {
108         if(members.length > 0 && cast(ModuleDeclaration) getMember(0))
109         {
110             auto fqn = getMember(0).getMember!ModuleFullyQualifiedName(0);
111 
112             foreach(m; fqn.members)
113                 writer("::", m);
114         }
115         else
116             writer("::", baseName(filename));
117         writer("::");
118     }
119 
120     string getModuleName()
121     {
122         if(auto md = cast(ModuleDeclaration) getMember(0))
123         {
124             auto mfqn = md.getMember!ModuleFullyQualifiedName(0);
125             string name = mfqn.getName();
126             return name;
127         }
128         return stripExtension(baseName(filename));
129     }
130 
131     override bool createsScope() const { return true; }
132 
133     override Scope enterScope(Scope sc)
134     {
135         initScope();
136         return super.enterScope(sc);
137     }
138 
139     override Scope getScope()
140     {
141         initScope();
142         return scop;
143     }
144 
145     void initScope()
146     {
147         if(!scop)
148         {
149             scop = new Scope;
150             scop.mod = this;
151             scop.node = this;
152 
153             // add implicite import of module object
154             if(auto prj = getProject())
155                 if(auto objmod = prj.getObjectModule(this))
156                     if(objmod !is this)
157                     {
158                         auto imp = Import.create(objmod); // only needs module name
159                         scop.addImport(imp);
160                     }
161 
162             super.addMemberSymbols(scop);
163         }
164     }
165 
166     override void addMemberSymbols(Scope sc)
167     {
168         initScope();
169     }
170 
171     Scope.SearchSet search(string ident)
172     {
173         initScope();
174         return scop.search(ident, false, false, true);
175     }
176 
177     override void _semantic(Scope sc)
178     {
179         if(imported) // no full semantic on imports
180             return;
181 
182         // the order in which lazy semantic analysis takes place:
183         // - evaluate/expand version/debug
184         // - evaluate mixins and static if conditionals in lexical order
185         // - analyze declarations:
186         //   - instantiate templates
187         //   - evaluate compile time expression
188         //   to resolve identifiers:
189         //     look in current scope, module
190         //     if not found, search all imports
191         initScope();
192 
193         sc = sc.push(scop);
194         scope(exit) sc.pop();
195 
196         foreach(m; members)
197         {
198             m.semantic(scop);
199         }
200     }
201 
202     public /* debug & version handling */ {
203     VersionDebug debugIds;
204     VersionDebug versionIds;
205 
206     Options getOptions()
207     {
208         if(options)
209             return options;
210         if(auto prj = getProject())
211             return prj.options;
212         return null;
213     }
214 
215     void specifyVersion(string ident, TextPos pos)
216     {
217         if(Options opt = getOptions())
218             if(opt.versionPredefined(ident) != 0)
219                 semanticError("cannot define predefined version identifier " ~ ident);
220         versionIds.define(ident, pos);
221     }
222 
223     void specifyVersion(int level)
224     {
225         versionIds.level = max(level, versionIds.level);
226     }
227 
228     bool versionEnabled(string ident, TextPos pos)
229     {
230         if(Options opt = getOptions())
231             if(opt.versionEnabled(ident))
232                 return true;
233         if(versionIds.defined(ident, pos))
234             return true;
235         return false;
236     }
237 
238     bool versionEnabled(int level)
239     {
240         if(Options opt = getOptions())
241             if(opt.versionEnabled(level))
242                 return true;
243         return level <= versionIds.level;
244     }
245 
246     void specifyDebug(string ident, TextPos pos)
247     {
248         debugIds.define(ident, pos);
249     }
250 
251     void specifyDebug(int level)
252     {
253         debugIds.level = max(level, debugIds.level);
254     }
255 
256     bool debugEnabled(string ident, TextPos pos)
257     {
258         if(Options opt = getOptions())
259         {
260             if(!opt.debugOn)
261                 return false;
262             if(opt.debugEnabled(ident))
263                 return true;
264         }
265         if(debugIds.defined(ident, pos))
266             return true;
267         return false;
268     }
269 
270     bool debugEnabled(int level)
271     {
272         if(Options opt = getOptions())
273         {
274             if(!opt.debugOn)
275                 return false;
276             if(opt.debugEnabled(level))
277                 return true;
278         }
279         return level <= debugIds.level;
280     }
281     bool debugEnabled()
282     {
283         if(Options opt = getOptions())
284             return opt.debugOn;
285         return false;
286     }
287 
288     }
289 }
290 
291 //ModuleDeclaration:
292 //    [ModuleFullyQualifiedName]
293 class ModuleDeclaration : Node
294 {
295     mixin ForwardCtor!();
296 
297     override void toD(CodeWriter writer)
298     {
299         writer("module ", getMember(0), ";");
300         writer.nl;
301     }
302 }
303 
304 class PackageIdentifier : Node
305 {
306     mixin ForwardCtor!();
307 
308     override void toD(CodeWriter writer)
309     {
310         assert(false);
311     }
312 }
313 
314 //ModuleFullyQualifiedName:
315 //    [Identifier...]
316 class ModuleFullyQualifiedName : Node
317 {
318     PackageIdentifier[] pkgs;
319 
320     mixin ForwardCtor!();
321 
322     override void toD(CodeWriter writer)
323     {
324         writer.writeArray(members, ".");
325     }
326 
327     override bool createsScope() const { return true; }
328 
329     override void addSymbols(Scope sc)
330     {
331         for(int m = 0; m < members.length; m++)
332         {
333             auto id = getMember!Identifier(m);
334             string name = id.ident;
335             Scope.SearchSet syms = sc.search(name, false, false, false);
336             if(syms.length > 1)
337             {
338                 semanticError("ambiguous use of package/module name ", name);
339                 break;
340             }
341             else if(syms.length == 1)
342             {
343                 Node n = syms.first();
344 
345                 if(auto pkg = cast(PackageIdentifier)n)
346                 {
347                     sc = pkg.enterScope(sc);
348                     pkgs ~= pkg;
349                 }
350                 else
351                 {
352                     semanticError("package/module name ", name, " also used as ", n);
353                     break;
354                 }
355             }
356             else
357             {
358                 auto pkg = new PackageIdentifier(id.span);
359                 sc.addSymbol(name, pkg);
360                 sc = pkg.enterScope(sc);
361                 pkgs ~= pkg;
362             }
363         }
364     }
365 
366     string getName()
367     {
368         string name = getMember!Identifier(0).ident;
369         foreach(m; 1..members.length)
370             name ~= "." ~ getMember!Identifier(m).ident;
371         return name;
372     }
373 }
374 
375 //EmptyDeclDef:
376 //    []
377 class EmptyDeclDef : Node
378 {
379     mixin ForwardCtor!();
380 
381     override void toD(CodeWriter writer)
382     {
383         writer(";");
384         writer.nl;
385     }
386 }
387 
388 //AttributeSpecifier:
389 //    attributes annotations [DeclarationBlock_opt]
390 class AttributeSpecifier : Node
391 {
392     mixin ForwardCtor!();
393 
394     override void toD(CodeWriter writer)
395     {
396         writer.writeAttributesAndAnnotations(attr, annotation);
397 
398         switch(id)
399         {
400             case TOK_colon:
401                 assert(members.length == 0);
402                 writer(":");
403                 writer.nl;
404                 break;
405             case TOK_lcurly:
406                 writer.nl;
407                 //writer("{");
408                 //writer.nl;
409                 //{
410                 //    CodeIndenter indent = CodeIndenter(writer);
411                     writer(getMember(0));
412                 //}
413                 //writer("}");
414                 //writer.nl;
415                 break;
416             default:
417                 writer(getMember(0));
418                 break;
419         }
420     }
421 
422     void applyAttributes(Node m)
423     {
424         m.attr = combineAttributes(attr, m.attr);
425         m.annotation = combineAnnotations(annotation, m.annotation);
426     }
427 
428     override Node[] expandNonScopeBlock(Scope sc, Node[] athis)
429     {
430         switch(id)
431         {
432             case TOK_colon:
433                 combineAttributes(sc.attributes, attr);
434                 combineAnnotations(sc.attributes, annotation);
435                 return [];
436             case TOK_lcurly:
437                 auto db = getMember!DeclarationBlock(0);
438                 foreach(m; db.members)
439                     applyAttributes(m);
440                 return db.removeAll();
441             default:
442                 applyAttributes(getMember(0));
443                 return removeAll();
444         }
445     }
446 }
447 
448 //UserAttributeSpecifier:
449 //    ident [ArgumentList_opt]
450 class UserAttributeSpecifier : Node
451 {
452     mixin ForwardCtor!();
453 
454     string ident;
455 
456     ArgumentList getArgumentList() { return getMember!ArgumentList(0); }
457 
458     override void toD(CodeWriter writer)
459     {
460         writer(ident);
461         if(members.length)
462         {
463             writer("(");
464             writer.writeArray(members);
465             writer(")");
466         }
467     }
468 
469     override void addSymbols(Scope sc)
470     {
471         addMemberSymbols(sc);
472     }
473     override void _semantic(Scope sc)
474     {
475     }
476 }
477 
478 //DeclarationBlock:
479 //    [DeclDef...]
480 class DeclarationBlock : Node
481 {
482     mixin ForwardCtor!();
483 
484     override void toD(CodeWriter writer)
485     {
486         if(id == TOK_lcurly)
487         {
488             writer("{");
489             writer.nl;
490             {
491                 CodeIndenter indent = CodeIndenter(writer);
492                 foreach(m; members)
493                     writer(m);
494             }
495             writer("}");
496             writer.nl;
497         }
498         else
499             foreach(m; members)
500                 writer(m);
501     }
502 
503     override Node[] expandNonScopeBlock(Scope sc, Node[] athis)
504     {
505         return removeAll();
506     }
507 }
508 
509 //LinkageAttribute:
510 //    attribute
511 class LinkageAttribute : AttributeSpecifier
512 {
513     mixin ForwardCtor!();
514 
515     override Node[] expandNonScopeBlock(Scope sc, Node[] athis)
516     {
517         return super.expandNonScopeBlock(sc, athis);
518     }
519 }
520 
521 //AlignAttribute:
522 //    attribute
523 class AlignAttribute : AttributeSpecifier
524 {
525     mixin ForwardCtor!();
526 }
527 
528 //Pragma:
529 //    ident [TemplateArgumentList]
530 class Pragma : Node
531 {
532     mixin ForwardCtor!();
533 
534     string ident;
535 
536     TemplateArgumentList getTemplateArgumentList() { return getMember!TemplateArgumentList(0); }
537 
538     override Pragma clone()
539     {
540         Pragma n = static_cast!Pragma(super.clone());
541         n.ident = ident;
542         return n;
543     }
544     override bool compare(const(Node) n) const
545     {
546         if(!super.compare(n))
547             return false;
548 
549         auto tn = static_cast!(typeof(this))(n);
550         return tn.ident == ident;
551     }
552 
553     override void toD(CodeWriter writer)
554     {
555         writer("pragma(", ident, ", ", getMember(0), ")");
556     }
557 
558     override void _semantic(Scope sc)
559     {
560         if(ident == "msg")
561         {
562             string msg;
563             auto alst = getTemplateArgumentList();
564             alst.semantic(sc);
565 
566             foreach(m; alst.members)
567             {
568                 Value val = m.interpretCatch(nullContext);
569                 msg ~= val.toMixin();
570             }
571             semanticMessage(msg);
572         }
573     }
574 }
575 
576 
577 //ImportDeclaration:
578 //    [ImportList]
579 class ImportDeclaration : Node
580 {
581     mixin ForwardCtor!();
582 
583     override void toD(CodeWriter writer)
584     {
585         writer("import ", getMember(0), ";");
586         writer.nl();
587     }
588     override void toC(CodeWriter writer)
589     {
590     }
591 
592     override void _semantic(Scope sc)
593     {
594         getMember(0).annotation = annotation; // copy protection attributes
595         getMember(0).semantic(sc);
596     }
597 
598     override void addSymbols(Scope sc)
599     {
600         getMember(0).addSymbols(sc);
601     }
602 
603 }
604 
605 //ImportList:
606 //    [Import...]
607 class ImportList : Node
608 {
609     mixin ForwardCtor!();
610 
611     override void toD(CodeWriter writer)
612     {
613         writer.writeArray(members);
614     }
615 
616     override void _semantic(Scope sc)
617     {
618         foreach(m; members)
619         {
620             m.annotation = annotation; // copy protection attributes
621             m.semantic(sc);
622         }
623     }
624 
625     override void addSymbols(Scope sc)
626     {
627         foreach(m; members)
628             m.addSymbols(sc);
629     }
630 }
631 
632 //Import:
633 //    aliasIdent_opt [ModuleFullyQualifiedName ImportBindList_opt]
634 class Import : Node
635 {
636     mixin ForwardCtor!();
637 
638     string aliasIdent;
639     ImportBindList getImportBindList() { return members.length > 1 ? getMember!ImportBindList(1) : null; }
640 
641     Annotation getProtection()
642     {
643         if(parent && parent.parent)
644             return parent.parent.annotation & Annotation_ProtectionMask;
645         return Annotation_Private;
646     }
647 
648     // semantic data
649     Module mod;
650     int countLookups;
651     int countFound;
652 
653     override Import clone()
654     {
655         Import n = static_cast!Import(super.clone());
656         n.aliasIdent = aliasIdent;
657         return n;
658     }
659 
660     override bool compare(const(Node) n) const
661     {
662         if(!super.compare(n))
663             return false;
664 
665         auto tn = static_cast!(typeof(this))(n);
666         return tn.aliasIdent == aliasIdent;
667     }
668 
669     override void toD(CodeWriter writer)
670     {
671         if(aliasIdent.length)
672             writer(aliasIdent, " = ");
673         writer(getMember(0));
674         if(auto bindList = getImportBindList())
675             writer(" : ", bindList);
676     }
677 
678     override void addSymbols(Scope sc)
679     {
680         sc.addImport(this);
681         if(aliasIdent.length > 0)
682             sc.addSymbol(aliasIdent, this);
683 
684         auto mfqn = getMember!ModuleFullyQualifiedName(0);
685         mfqn.addSymbols(sc);
686     }
687 
688     Scope.SearchSet search(Scope sc, string ident)
689     {
690         if(!mod)
691             if(auto prj = sc.mod.getProject())
692                 mod = prj.importModule(getModuleName(), this);
693 
694         if(!mod)
695             return Scope.SearchSet();
696 
697         return mod.search(ident);
698     }
699 
700     string getModuleName()
701     {
702         auto mfqn = getMember!ModuleFullyQualifiedName(0);
703         return mfqn.getName();
704     }
705 
706     static Import create(Module mod)
707     {
708         // TODO: no location info
709         auto id = new Identifier;
710         id.ident = mod.getModuleName(); // TODO: incorrect for modules in packages
711         auto mfqm = new ModuleFullyQualifiedName;
712         mfqm.addMember(id);
713         auto imp = new Import;
714         imp.addMember(mfqm);
715         return imp;
716     }
717 }
718 
719 unittest
720 {
721     verifyParseWrite(q{ import test; });
722     verifyParseWrite(q{ import ntest = pkg.test; });
723     verifyParseWrite(q{ import io = std.stdio : writeln, write; });
724 }
725 
726 
727 //ImportBindList:
728 //    ImportBind
729 //    ImportBind , ImportBindList
730 class ImportBindList : Node
731 {
732     mixin ForwardCtor!();
733 
734     override void toD(CodeWriter writer)
735     {
736         writer.writeArray(members);
737     }
738 }
739 
740 
741 //ImportBind:
742 //    Identifier
743 //    Identifier = Identifier
744 class ImportBind : Node
745 {
746     mixin ForwardCtor!();
747 
748     override void toD(CodeWriter writer)
749     {
750         writer(getMember(0));
751         if(members.length > 1)
752             writer(" = ", getMember(1));
753     }
754 }
755 
756 //MixinDeclaration:
757 //    mixin ( AssignExpression ) ;
758 class MixinDeclaration : Node
759 {
760     mixin ForwardCtor!();
761 
762     override void toD(CodeWriter writer)
763     {
764         writer("mixin(", getMember(0), ");");
765         writer.nl;
766     }
767 
768     override Node[] expandNonScopeInterpret(Scope sc, Node[] athis)
769     {
770         Context ctx = new Context(nullContext);
771         ctx.scop = sc;
772         Value v = getMember(0).interpret(ctx);
773         string s = v.toMixin();
774         Parser parser = new Parser;
775         if(auto prj = sc.getProject())
776             parser.saveErrors = prj.saveErrors;
777         return parser.parseDeclarations(s, span);
778     }
779 
780 }
781 
782 //Unittest:
783 //    unittest BlockStatement
784 class Unittest : Node
785 {
786     mixin ForwardCtor!();
787 
788     override void toD(CodeWriter writer)
789     {
790         writer("unittest");
791         writer.nl;
792         writer(getMember(0));
793     }
794 
795     override void toC(CodeWriter writer)
796     {
797     }
798 }